From 3e8bba67ee94a6842f60b34c1ac3873e909b7654 Mon Sep 17 00:00:00 2001 From: Recoup Agent Date: Fri, 13 Mar 2026 15:18:56 +0000 Subject: [PATCH 1/2] agent: @U0AJM7X8FBR read the mono repo, then ULTRATHINK about our non-technical --- app/api/accounts/[id]/route.ts | 7 +- .../admins/sandboxes/__tests__/route.test.ts | 13 +- app/api/artists/route.ts | 1 - app/api/coding-agent/[platform]/route.ts | 33 +- .../callback/__tests__/route.test.ts | 4 +- app/api/organizations/artists/route.ts | 1 - app/api/songs/analyze/presets/route.ts | 1 + app/api/spotify/artist/albums/route.ts | 1 - app/api/transcribe/route.ts | 5 +- evals/catalog-songs-count.eval.ts | 5 +- evals/first-week-album-sales.eval.ts | 12 +- evals/memory-tools.eval.ts | 2 +- evals/monthly-listeners-tracking.eval.ts | 2 +- evals/search-web-tool.eval.ts | 9 +- evals/social-scraping.eval.ts | 8 +- evals/spotify-tools.eval.ts | 5 +- evals/tiktok-analytics-questions.eval.ts | 6 +- lib/accounts/getAccountHandler.ts | 1 - lib/accounts/validateAccountParams.ts | 1 - .../sandboxes/aggregateAccountSandboxStats.ts | 3 +- .../sandboxes/getAdminSandboxesHandler.ts | 4 +- lib/ai/__tests__/getAvailableModels.test.ts | 8 +- lib/ai/__tests__/getModel.test.ts | 10 +- lib/ai/getAvailableModels.ts | 6 +- lib/ai/getModel.ts | 7 +- lib/ai/isEmbedModel.ts | 2 + lib/artist/validateArtistSocialsScrapeBody.ts | 1 - .../__tests__/buildGetArtistsParams.test.ts | 4 +- .../checkAccountArtistAccess.test.ts | 20 +- .../__tests__/createArtistInDb.test.ts | 4 +- .../__tests__/createArtistPostHandler.test.ts | 5 + .../validateCreateArtistBody.test.ts | 5 + .../validateGetArtistsRequest.test.ts | 30 +- lib/artists/checkAccountArtistAccess.ts | 4 +- lib/artists/createArtistPostHandler.ts | 9 +- lib/artists/getFormattedArtist.ts | 13 +- lib/artists/getSocialPlatformByLink.ts | 1 - lib/artists/validateArtistsQuery.ts | 1 - lib/arweave/isIPFSUrl.ts | 1 - lib/arweave/isNormalizedIPFSURL.ts | 1 - .../__tests__/validateAuthContext.test.ts | 4 + lib/catalog/formatCatalogSongsAsCSV.ts | 6 +- lib/catalog/getCatalogDataAsCSV.ts | 2 + lib/catalog/getCatalogSongs.ts | 24 +- lib/catalog/getCatalogs.ts | 10 +- .../buildSystemPromptWithImages.test.ts | 2 +- .../__tests__/handleChatCompletion.test.ts | 34 +- .../integration/chatEndToEnd.test.ts | 95 +- lib/chat/__tests__/saveChatCompletion.test.ts | 10 +- lib/chat/__tests__/setupConversation.test.ts | 14 +- .../__tests__/setupToolsForRequest.test.ts | 16 +- .../__tests__/validateChatRequest.test.ts | 77 +- lib/chat/handleChatCompletion.ts | 3 +- lib/chat/handleChatStream.ts | 10 +- .../__tests__/getPrepareStepResult.test.ts | 76 +- .../toolChains/__tests__/toolChains.test.ts | 27 +- .../toolChains/getExecutedToolTimeline.ts | 6 +- lib/chat/toolChains/getPrepareStepResult.ts | 11 +- lib/chat/toolChains/index.ts | 7 +- lib/chat/validateMessages.ts | 4 +- lib/chats/__tests__/updateChatHandler.test.ts | 16 +- .../__tests__/validateUpdateChatBody.test.ts | 13 +- lib/chats/buildGetChatsParams.ts | 2 +- lib/chats/processCompactChatRequest.ts | 4 + .../__tests__/encodeGitHubThreadId.test.ts | 6 +- .../handleCodingAgentCallback.test.ts | 4 +- .../__tests__/handleGitHubWebhook.test.ts | 6 + .../__tests__/onMergeAction.test.ts | 9 +- .../__tests__/onMergeTestToMainAction.test.ts | 14 +- .../parseMergeTestToMainActionId.test.ts | 4 +- lib/coding-agent/bot.ts | 23 +- lib/coding-agent/buildPRCard.ts | 4 +- lib/coding-agent/encodeGitHubThreadId.ts | 2 + lib/coding-agent/handleMergeSuccess.ts | 2 + lib/coding-agent/handlers/onMergeAction.ts | 8 +- lib/coding-agent/handlers/onNewMention.ts | 2 +- lib/coding-agent/mergeGithubBranch.ts | 27 +- lib/coding-agent/parseMergeActionId.ts | 2 + .../parseMergeTestToMainActionId.ts | 2 + lib/coding-agent/postGitHubComment.ts | 17 +- lib/coding-agent/types.ts | 3 +- lib/coding-agent/validateEnv.ts | 11 +- .../__tests__/getConnectorsHandler.test.ts | 4 +- .../validateAuthorizeConnectorRequest.test.ts | 5 +- .../validateGetConnectorsRequest.test.ts | 8 +- .../connectors/disconnectConnectorHandler.ts | 5 +- lib/composio/connectors/getConnectors.ts | 7 +- .../validateAuthorizeConnectorRequest.ts | 5 +- .../validateDisconnectConnectorRequest.ts | 5 +- .../validateGetConnectorsRequest.ts | 5 +- lib/composio/getCallbackUrl.ts | 1 + .../__tests__/createToolRouterSession.test.ts | 7 +- .../__tests__/getComposioTools.test.ts | 6 +- .../toolRouter/createToolRouterSession.ts | 4 +- lib/const.ts | 1 - lib/contact/contactTeam.ts | 1 - lib/credits/__tests__/getCreditUsage.test.ts | 6 +- .../__tests__/handleChatCredits.test.ts | 13 +- lib/credits/getCreditUsage.ts | 7 +- lib/credits/handleChatCredits.ts | 4 + .../validateInboundEmailEvent.test.ts | 3 +- .../__tests__/extractRoomIdFromHtml.test.ts | 3 +- .../inbound/__tests__/getFromWithName.test.ts | 9 +- lib/emails/inbound/extractRoomIdFromHtml.ts | 5 +- lib/emails/processAndSendEmail.ts | 6 +- lib/evals/callChatFunctions.ts | 1 + lib/evals/callChatFunctionsWithResult.ts | 5 +- lib/evals/createToolsCalledScorer.ts | 18 +- lib/evals/extractTextFromResult.ts | 6 +- lib/evals/extractTextResultFromSteps.ts | 15 +- lib/evals/getCatalogSongsCountExpected.ts | 3 + lib/evals/getSpotifyFollowersData.ts | 4 +- lib/evals/getSpotifyFollowersExpected.ts | 4 + lib/evals/scorers/CatalogAvailability.ts | 29 +- lib/evals/scorers/QuestionAnswered.ts | 31 +- lib/evals/scorers/ToolsCalled.ts | 26 +- .../__tests__/getKnowledgeBaseText.test.ts | 4 +- lib/files/generateAndStoreTxtFile.ts | 1 - lib/files/parseFilesFromQuery.ts | 5 +- .../__tests__/callFlamingoGenerate.test.ts | 18 +- .../getFlamingoPresetsHandler.test.ts | 3 + lib/flamingo/__tests__/postProcessors.test.ts | 8 +- lib/flamingo/__tests__/presets.test.ts | 9 +- .../processAnalyzeMusicRequest.test.ts | 13 +- lib/flamingo/callFlamingoGenerate.ts | 4 +- lib/flamingo/executeFullReport.ts | 69 +- lib/flamingo/getFlamingoPresetsHandler.ts | 5 +- lib/flamingo/isFlamingoGenerateResult.ts | 10 +- lib/flamingo/postFlamingoGenerateHandler.ts | 7 +- lib/flamingo/presets/audienceProfile.ts | 2 +- lib/flamingo/presets/catalogMetadata.ts | 2 +- lib/flamingo/presets/condenseRepetitions.ts | 6 +- lib/flamingo/presets/contentAdvisory.ts | 2 +- lib/flamingo/presets/deduplicateArray.ts | 1 - lib/flamingo/presets/extractOneCycle.ts | 1 - lib/flamingo/presets/getAllPresets.ts | 1 - lib/flamingo/presets/getPreset.ts | 1 - lib/flamingo/presets/getPresetSummaries.ts | 3 +- lib/flamingo/presets/lyricTranscription.ts | 5 +- lib/flamingo/presets/moodTags.ts | 5 +- lib/flamingo/presets/musicTheory.ts | 6 +- lib/flamingo/presets/parseJsonLike.ts | 1 - lib/flamingo/presets/presetRegistry.ts | 6 +- lib/flamingo/presets/similarArtists.ts | 2 +- lib/flamingo/presets/syncBriefMatch.ts | 2 +- lib/flamingo/processAnalyzeMusicRequest.ts | 9 +- lib/flamingo/validateFlamingoGenerateBody.ts | 8 +- .../deleteAccountGithubRepos.test.ts | 8 +- .../__tests__/findOrgReposByAccountId.test.ts | 6 +- .../__tests__/getRepoGitModules.test.ts | 19 +- lib/github/expandSubmoduleEntries.ts | 6 + lib/github/findOrgReposByAccountId.ts | 19 +- lib/github/getRepoFileTree.ts | 4 +- lib/github/getRepoGitModules.ts | 14 +- lib/github/resolveSubmodulePath.ts | 2 + lib/mcp/__tests__/getMcpTools.test.ts | 6 +- lib/mcp/__tests__/verifyApiKey.test.ts | 6 +- lib/mcp/resolveAccountId.ts | 2 + .../__tests__/registerSendEmailTool.test.ts | 3 +- .../registerWebDeepResearchTool.test.ts | 5 +- .../registerGetArtistSocialsTool.ts | 1 - .../registerUpdateArtistSocialsTool.ts | 5 +- .../registerCreateNewArtistTool.test.ts | 8 +- .../artists/registerCreateNewArtistTool.ts | 10 +- .../flamingo/registerAnalyzeMusicTool.ts | 3 +- lib/mcp/tools/registerWebDeepResearchTool.ts | 4 +- .../registerPromptSandboxTool.test.ts | 13 +- .../search/registerSearchGoogleImagesTool.ts | 22 +- .../registerGetTaskRunStatusTool.test.ts | 13 +- lib/mcp/tools/transcribe/index.ts | 1 - .../transcribe/registerTranscribeAudioTool.ts | 5 +- .../__tests__/convertToUiMessages.test.ts | 4 +- .../extractImageUrlsFromMessages.test.ts | 21 +- .../getLatestUserMessageText.test.ts | 4 +- lib/messages/__tests__/getTextContent.test.ts | 4 +- lib/messages/convertToUiMessages.ts | 2 +- lib/messages/getLatestUserMessageText.ts | 4 +- lib/messages/getTextContent.ts | 2 +- .../createNotificationHandler.test.ts | 7 +- .../validateCreateNotificationBody.test.ts | 10 +- .../__tests__/canAccessAccount.test.ts | 4 +- .../validateOrganizationAccess.test.ts | 4 +- lib/organizations/addArtistToOrgHandler.ts | 1 - lib/organizations/canAccessAccount.ts | 4 +- .../createOrganizationHandler.ts | 1 - .../formatAccountOrganizations.ts | 5 +- .../validateAddArtistToOrgBody.ts | 5 +- .../validateCreateOrganizationBody.ts | 1 - lib/perplexity/chatWithPerplexity.ts | 4 +- lib/prompts/getSystemPrompt.ts | 1 + .../__tests__/buildGetPulsesParams.test.ts | 4 +- lib/pulse/__tests__/getPulsesHandler.test.ts | 6 +- .../__tests__/updatePulsesHandler.test.ts | 6 +- .../validateGetPulsesRequest.test.ts | 42 +- lib/rooms/__tests__/copyRoom.test.ts | 4 +- lib/rooms/copyRoom.ts | 5 +- .../createSandboxFromSnapshot.test.ts | 6 +- .../__tests__/getActiveSandbox.test.ts | 15 +- .../__tests__/getOrCreateSandbox.test.ts | 3 +- .../__tests__/getSandboxesFileHandler.test.ts | 2 +- lib/sandbox/getActiveSandbox.ts | 4 +- lib/sandbox/runClaudeCode.ts | 2 +- lib/segments/createSegmentResponses.ts | 1 - lib/spotify/getSpotifyFollowers.ts | 10 +- .../__tests__/insertAccountArtistId.test.ts | 4 +- .../__tests__/selectAccountArtistId.test.ts | 4 +- .../account_artist_ids/getAccountArtistIds.ts | 5 +- .../selectAccountArtistId.ts | 5 +- .../account_info/insertAccountInfo.ts | 1 - .../selectAccountOrganizationIds.test.ts | 4 +- .../addAccountToOrganization.ts | 1 - .../selectAccountOrganizationIds.ts | 5 +- .../__tests__/insertAccountSandbox.test.ts | 4 +- .../__tests__/upsertAccountSnapshot.test.ts | 3 +- .../selectAccountWorkspaceId.test.ts | 4 +- .../getAccountWorkspaceIds.ts | 3 +- .../selectAccountWorkspaceId.ts | 5 +- .../selectAccountWithSocials.test.ts | 12 +- lib/supabase/accounts/selectAccounts.ts | 9 +- .../selectArtistOrganizationIds.test.ts | 4 +- .../addArtistToOrganization.ts | 1 - .../getArtistsByOrganization.ts | 1 - lib/supabase/files/createFileRecord.ts | 7 +- .../__tests__/selectPulseAccounts.test.ts | 45 +- .../__tests__/upsertPulseAccount.test.ts | 4 +- .../song_artists/insertSongArtists.ts | 21 +- lib/supabase/storage/uploadFileByKey.ts | 19 +- lib/tasks/__tests__/deleteTask.test.ts | 16 +- lib/tasks/__tests__/getTaskRunHandler.test.ts | 27 +- .../__tests__/validateGetTaskRunQuery.test.ts | 2 + lib/tasks/validateGetTaskRunQuery.ts | 7 +- lib/telegram/sendErrorNotification.ts | 7 +- lib/transcribe/formatTranscriptMd.ts | 1 - lib/transcribe/index.ts | 1 - lib/transcribe/processAudioTranscription.ts | 7 +- lib/transcribe/saveAudioToFiles.ts | 14 +- lib/transcribe/saveTranscriptToFiles.ts | 4 + lib/transcribe/transcribeAudio.ts | 3 +- lib/transcribe/types.ts | 8 +- lib/workspaces/createWorkspacePostHandler.ts | 9 +- lib/youtube/fetchYouTubeChannelInfo.ts | 1 - lib/youtube/getDefaultDateRange.ts | 1 - lib/youtube/isTokenExpired.ts | 1 - next.config.ts | 12 +- package.json | 1 - pnpm-lock.yaml | 190 +- types/database.types.ts | 6833 ++++++++--------- 247 files changed, 4423 insertions(+), 4632 deletions(-) diff --git a/app/api/accounts/[id]/route.ts b/app/api/accounts/[id]/route.ts index bf1b0349..42b388bf 100644 --- a/app/api/accounts/[id]/route.ts +++ b/app/api/accounts/[id]/route.ts @@ -23,13 +23,10 @@ export async function OPTIONS() { * - id (required): The unique identifier of the account (UUID) * * @param request - The request object + * @param params.params * @param params - Route params containing the account ID * @returns A NextResponse with account data */ -export async function GET( - request: NextRequest, - { params }: { params: Promise<{ id: string }> }, -) { +export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { return getAccountHandler(request, params); } - diff --git a/app/api/admins/sandboxes/__tests__/route.test.ts b/app/api/admins/sandboxes/__tests__/route.test.ts index 98e2650c..444485e8 100644 --- a/app/api/admins/sandboxes/__tests__/route.test.ts +++ b/app/api/admins/sandboxes/__tests__/route.test.ts @@ -3,8 +3,7 @@ import { NextRequest, NextResponse } from "next/server"; const mockGetAdminSandboxesHandler = vi.fn(); vi.mock("@/lib/admins/sandboxes/getAdminSandboxesHandler", () => ({ - getAdminSandboxesHandler: (...args: unknown[]) => - mockGetAdminSandboxesHandler(...args), + getAdminSandboxesHandler: (...args: unknown[]) => mockGetAdminSandboxesHandler(...args), })); const { GET, OPTIONS } = await import("../route"); @@ -15,10 +14,7 @@ beforeEach(() => { describe("GET /api/admins/sandboxes", () => { it("delegates to getAdminSandboxesHandler and returns its response", async () => { - const expected = NextResponse.json( - { status: "success", accounts: [] }, - { status: 200 }, - ); + const expected = NextResponse.json({ status: "success", accounts: [] }, { status: 200 }); mockGetAdminSandboxesHandler.mockResolvedValue(expected); const request = new NextRequest("https://example.com/api/admins/sandboxes"); @@ -86,10 +82,7 @@ describe("GET /api/admins/sandboxes", () => { it("returns 500 when handler returns internal error", async () => { mockGetAdminSandboxesHandler.mockResolvedValue( - NextResponse.json( - { status: "error", message: "Internal server error" }, - { status: 500 }, - ), + NextResponse.json({ status: "error", message: "Internal server error" }, { status: 500 }), ); const request = new NextRequest("https://example.com/api/admins/sandboxes"); diff --git a/app/api/artists/route.ts b/app/api/artists/route.ts index 64b65a42..7fe9a21b 100644 --- a/app/api/artists/route.ts +++ b/app/api/artists/route.ts @@ -54,4 +54,3 @@ export async function GET(request: NextRequest) { export async function POST(request: NextRequest) { return createArtistPostHandler(request); } - diff --git a/app/api/coding-agent/[platform]/route.ts b/app/api/coding-agent/[platform]/route.ts index 996a0a4d..f41af8f5 100644 --- a/app/api/coding-agent/[platform]/route.ts +++ b/app/api/coding-agent/[platform]/route.ts @@ -3,36 +3,14 @@ import { after } from "next/server"; import { codingAgentBot } from "@/lib/coding-agent/bot"; import "@/lib/coding-agent/handlers/registerHandlers"; -/** - * GET /api/coding-agent/[platform] - * - * Handles webhook verification handshakes (e.g. WhatsApp hub.challenge). - * - * @param request - The incoming verification request - * @param params - Route params containing the platform name - */ -export async function GET( - request: NextRequest, - { params }: { params: Promise<{ platform: string }> }, -) { - const { platform } = await params; - - const handler = codingAgentBot.webhooks[platform as keyof typeof codingAgentBot.webhooks]; - - if (!handler) { - return new Response("Unknown platform", { status: 404 }); - } - - return handler(request, { waitUntil: p => after(() => p) }); -} - /** * POST /api/coding-agent/[platform] * * Webhook endpoint for the coding agent bot. - * Handles Slack and WhatsApp webhooks via dynamic [platform] segment. + * Currently handles Slack webhooks via dynamic [platform] segment. * * @param request - The incoming webhook request + * @param params.params * @param params - Route params containing the platform name */ export async function POST( @@ -44,8 +22,11 @@ export async function POST( // Handle Slack url_verification challenge before loading the bot. // This avoids blocking on Redis/adapter initialization during setup. if (platform === "slack") { - const body = await request.clone().json().catch(() => null); - if (body?.type === "url_verification" && typeof body?.challenge === "string") { + const body = await request + .clone() + .json() + .catch(() => null); + if (body?.type === "url_verification" && body?.challenge) { return Response.json({ challenge: body.challenge }); } } diff --git a/app/api/coding-agent/callback/__tests__/route.test.ts b/app/api/coding-agent/callback/__tests__/route.test.ts index 892cfa84..d817f0fc 100644 --- a/app/api/coding-agent/callback/__tests__/route.test.ts +++ b/app/api/coding-agent/callback/__tests__/route.test.ts @@ -2,9 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; const mockInitialize = vi.fn(); -const mockHandleCallback = vi.fn().mockResolvedValue( - new Response("ok", { status: 200 }), -); +const mockHandleCallback = vi.fn().mockResolvedValue(new Response("ok", { status: 200 })); vi.mock("@/lib/coding-agent/bot", () => ({ codingAgentBot: { diff --git a/app/api/organizations/artists/route.ts b/app/api/organizations/artists/route.ts index c722e06f..028c9f5c 100644 --- a/app/api/organizations/artists/route.ts +++ b/app/api/organizations/artists/route.ts @@ -29,4 +29,3 @@ export async function OPTIONS() { export async function POST(request: NextRequest) { return addArtistToOrgHandler(request); } - diff --git a/app/api/songs/analyze/presets/route.ts b/app/api/songs/analyze/presets/route.ts index 8baccd38..b809394c 100644 --- a/app/api/songs/analyze/presets/route.ts +++ b/app/api/songs/analyze/presets/route.ts @@ -28,6 +28,7 @@ export async function OPTIONS() { * - status: "success" * - presets: Array of { name, label, description, requiresAudio, responseFormat } * + * @param request * @returns A NextResponse with the list of available presets */ export async function GET(request: NextRequest): Promise { diff --git a/app/api/spotify/artist/albums/route.ts b/app/api/spotify/artist/albums/route.ts index b7d939f8..b7ac94e0 100644 --- a/app/api/spotify/artist/albums/route.ts +++ b/app/api/spotify/artist/albums/route.ts @@ -32,4 +32,3 @@ export async function OPTIONS() { export async function GET(request: NextRequest) { return getSpotifyArtistAlbumsHandler(request); } - diff --git a/app/api/transcribe/route.ts b/app/api/transcribe/route.ts index 5cf0b9a5..0896806b 100644 --- a/app/api/transcribe/route.ts +++ b/app/api/transcribe/route.ts @@ -2,6 +2,10 @@ import { NextRequest, NextResponse } from "next/server"; import { processAudioTranscription } from "@/lib/transcribe/processAudioTranscription"; import { formatTranscriptionError } from "@/lib/transcribe/types"; +/** + * + * @param req + */ export async function POST(req: NextRequest) { try { const body = await req.json(); @@ -40,4 +44,3 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: message }, { status }); } } - diff --git a/evals/catalog-songs-count.eval.ts b/evals/catalog-songs-count.eval.ts index 960068b5..1792f22a 100644 --- a/evals/catalog-songs-count.eval.ts +++ b/evals/catalog-songs-count.eval.ts @@ -1,9 +1,6 @@ import { Eval } from "braintrust"; import { Factuality, AnswerCorrectness } from "autoevals"; -import { - callChatFunctionsWithResult, - extractTextFromResult, -} from "@/lib/evals"; +import { callChatFunctionsWithResult, extractTextFromResult } from "@/lib/evals"; import getCatalogSongsCountData from "@/lib/evals/getCatalogSongsCountData"; /** diff --git a/evals/first-week-album-sales.eval.ts b/evals/first-week-album-sales.eval.ts index 31f03c49..6ba63486 100644 --- a/evals/first-week-album-sales.eval.ts +++ b/evals/first-week-album-sales.eval.ts @@ -14,8 +14,7 @@ import { callChatFunctions } from "@/lib/evals"; Eval("First Week Album Sales Evaluation", { data: () => [ { - input: - "how many albums did Halsey The Great Impersonator sell the first week of release", + input: "how many albums did Halsey The Great Impersonator sell the first week of release", expected: "Halsey's album 'The Great Impersonator' sold between 93,000 and 100,000 copies in its first week of release. It debuted at No. 2 on the Billboard 200 chart.", metadata: { @@ -26,8 +25,7 @@ Eval("First Week Album Sales Evaluation", { }, }, { - input: - "what were the first week sales for Taylor Swift's Midnights album?", + input: "what were the first week sales for Taylor Swift's Midnights album?", expected: "Taylor Swift's Midnights sold 1.578 million equivalent album units in its first week in the United States, which included between 1 million and 1.5 million pure album sales. This marked the biggest sales week for any album since Adele's 25 in 2015 and the best sales week for a vinyl album in the modern tracking era.", metadata: { @@ -38,8 +36,7 @@ Eval("First Week Album Sales Evaluation", { }, }, { - input: - "how many copies did Drake's Certified Lover Boy sell in the first week", + input: "how many copies did Drake's Certified Lover Boy sell in the first week", expected: "Drake's 'Certified Lover Boy' sold approximately 613,000 album-equivalent units in its first week, securing the #1 spot on the Billboard 200 chart.", metadata: { @@ -50,8 +47,7 @@ Eval("First Week Album Sales Evaluation", { }, }, { - input: - "what are the first week streaming numbers for Bad Bunny's Un Verano Sin Ti", + input: "what are the first week streaming numbers for Bad Bunny's Un Verano Sin Ti", expected: "In its first week of release in the United States, Bad Bunny's album Un Verano Sin Ti garnered over 355 million on-demand streams and 274,000 album-equivalent units, the most for any album that year and the largest streaming week for a Latin album ever at that time. The album also set a record for the biggest debut for a Latin album in Spotify's history.", metadata: { diff --git a/evals/memory-tools.eval.ts b/evals/memory-tools.eval.ts index 9de8fda6..41c8daa6 100644 --- a/evals/memory-tools.eval.ts +++ b/evals/memory-tools.eval.ts @@ -50,7 +50,7 @@ Eval("Memory & Storage Tools Evaluation", { const result = await callChatFunctionsWithResult(input); const output = extractTextFromResult(result); const toolCalls = - result.toolCalls?.map((tc) => ({ + result.toolCalls?.map(tc => ({ toolName: tc.toolName, args: {}, })) || []; diff --git a/evals/monthly-listeners-tracking.eval.ts b/evals/monthly-listeners-tracking.eval.ts index 7fa4965b..b46fbb35 100644 --- a/evals/monthly-listeners-tracking.eval.ts +++ b/evals/monthly-listeners-tracking.eval.ts @@ -16,7 +16,7 @@ Eval("Monthly Listeners Tracking Evaluation", { data: () => { const { startDateFormatted, endDateFormatted } = getDynamicDates(); - const testCases = EVAL_ARTISTS.map((artist) => ({ + const testCases = EVAL_ARTISTS.map(artist => ({ input: `what is the increase for ${artist} monthlies from ${startDateFormatted} to ${endDateFormatted}`, expected: `I've set up a monthlies tracking for ${artist}. would you like to receive the updated list in your email?`, metadata: { diff --git a/evals/search-web-tool.eval.ts b/evals/search-web-tool.eval.ts index dda17492..f02584d9 100644 --- a/evals/search-web-tool.eval.ts +++ b/evals/search-web-tool.eval.ts @@ -80,8 +80,7 @@ This year's Grammys were notable for historic wins, genre-crossing breakthroughs }, }, { - input: - "What are the current Spotify streaming numbers for Bad Bunny's latest album?", + input: "What are the current Spotify streaming numbers for Bad Bunny's latest album?", expected: `Bad Bunny's latest album titled "DeBÍ TiRAR MáS FOToS," released in 2025, currently has around 1.07 billion streams on Spotify. His previous album "Un Verano Sin Ti" has over 20.6 billion streams on Spotify, making it one of the most streamed albums on the platform as of late 2025.`, metadata: { category: "streaming_data", @@ -181,8 +180,7 @@ These collaborations highlight a range of featured artists who dropped new track }, }, { - input: - "What are the current music industry revenue statistics for Q2 2025?", + input: "What are the current music industry revenue statistics for Q2 2025?", expected: `The current music industry revenue statistics for Q2 2025 show continued growth driven primarily by streaming and subscription services. Key highlights from major companies: @@ -227,8 +225,7 @@ Overall, 2025 sees music copyright law adapting to address the growing influence }, }, { - input: - "What are the latest features on TikTok for music creators in Q2 2025?", + input: "What are the latest features on TikTok for music creators in Q2 2025?", expected: `The latest features on TikTok for music creators in Q2 2025 center around the beta launch of TikTok Songwriter Features. These include: - Songwriter Account Label: Music creators can create official Songwriter accounts that add a clear "Songwriter" label to their profile for identification. diff --git a/evals/social-scraping.eval.ts b/evals/social-scraping.eval.ts index 277616d9..e9748abc 100644 --- a/evals/social-scraping.eval.ts +++ b/evals/social-scraping.eval.ts @@ -56,8 +56,7 @@ Eval("Social Scraping Evaluation", { }, { input: "How many Instagram followers do I have?", - expected: - "Specific follower count from fresh scraping (e.g., '112,545 followers')", + expected: "Specific follower count from fresh scraping (e.g., '112,545 followers')", metadata: { artist: "Fat Beats", platform: "instagram", @@ -94,8 +93,7 @@ Eval("Social Scraping Evaluation", { { input: "Fetch streaming trend and playlist-add history for the last 90 days (shows what's actually growing).", - expected: - "Streaming trend data and playlist-add history from Spotify tools and web search", + expected: "Streaming trend data and playlist-add history from Spotify tools and web search", metadata: { artist: "Unknown", platform: "spotify", @@ -124,7 +122,7 @@ Eval("Social Scraping Evaluation", { const result = await callChatFunctionsWithResult(input); const output = extractTextFromResult(result); const toolCalls = - result.toolCalls?.map((tc) => ({ + result.toolCalls?.map(tc => ({ toolName: tc.toolName, args: {}, })) || []; diff --git a/evals/spotify-tools.eval.ts b/evals/spotify-tools.eval.ts index adb8856d..8a7010c6 100644 --- a/evals/spotify-tools.eval.ts +++ b/evals/spotify-tools.eval.ts @@ -37,8 +37,7 @@ Eval("Spotify Tools Evaluation", { { input: 'craft a spotify for artists pitch, in line with their requirements, about the 2 late to be toxic album and focus track "what happened 2 us". ask any questions you need to get necessary info you don\'t have', - expected: - "A Spotify for Artists pitch using data from Spotify tools and web research", + expected: "A Spotify for Artists pitch using data from Spotify tools and web research", metadata: { artist: "Unknown", platform: "Spotify", @@ -67,7 +66,7 @@ Eval("Spotify Tools Evaluation", { const result = await callChatFunctionsWithResult(input); const output = extractTextFromResult(result); const toolCalls = - result.toolCalls?.map((tc) => ({ + result.toolCalls?.map(tc => ({ toolName: tc.toolName, args: {}, })) || []; diff --git a/evals/tiktok-analytics-questions.eval.ts b/evals/tiktok-analytics-questions.eval.ts index b74579a3..019ee8f7 100644 --- a/evals/tiktok-analytics-questions.eval.ts +++ b/evals/tiktok-analytics-questions.eval.ts @@ -23,8 +23,7 @@ Eval("TikTok Analytics Questions Evaluation", { data: () => [ { input: "how many total views does @iamjuliusblack have on TikTok", - expected: - "A specific number of total views (e.g., '150,000 total views')", + expected: "A specific number of total views (e.g., '150,000 total views')", metadata: { artist: "Julius Black", platform: "tiktok", @@ -34,8 +33,7 @@ Eval("TikTok Analytics Questions Evaluation", { }, { input: "show me @iamjuliusblack's 10 highest performing TikTok posts", - expected: - "A ranked list of 10 TikTok posts with URLs, view counts, likes, and captions", + expected: "A ranked list of 10 TikTok posts with URLs, view counts, likes, and captions", metadata: { artist: "Julius Black", platform: "tiktok", diff --git a/lib/accounts/getAccountHandler.ts b/lib/accounts/getAccountHandler.ts index 327242f0..0c58a58f 100644 --- a/lib/accounts/getAccountHandler.ts +++ b/lib/accounts/getAccountHandler.ts @@ -61,4 +61,3 @@ export async function getAccountHandler( ); } } - diff --git a/lib/accounts/validateAccountParams.ts b/lib/accounts/validateAccountParams.ts index 8b2c4534..47864d24 100644 --- a/lib/accounts/validateAccountParams.ts +++ b/lib/accounts/validateAccountParams.ts @@ -34,4 +34,3 @@ export function validateAccountParams(id: string): NextResponse | AccountParams return result.data; } - diff --git a/lib/admins/sandboxes/aggregateAccountSandboxStats.ts b/lib/admins/sandboxes/aggregateAccountSandboxStats.ts index 99bd17ec..69cdbcf0 100644 --- a/lib/admins/sandboxes/aggregateAccountSandboxStats.ts +++ b/lib/admins/sandboxes/aggregateAccountSandboxStats.ts @@ -37,7 +37,6 @@ export async function aggregateAccountSandboxStats(): Promise - new Date(b.last_created_at).getTime() - new Date(a.last_created_at).getTime(), + (a, b) => new Date(b.last_created_at).getTime() - new Date(a.last_created_at).getTime(), ); } diff --git a/lib/admins/sandboxes/getAdminSandboxesHandler.ts b/lib/admins/sandboxes/getAdminSandboxesHandler.ts index 02741a79..0290bdfd 100644 --- a/lib/admins/sandboxes/getAdminSandboxesHandler.ts +++ b/lib/admins/sandboxes/getAdminSandboxesHandler.ts @@ -48,9 +48,7 @@ export async function getAdminSandboxesHandler(request: NextRequest): Promise( - emailRows - .filter(r => r.account_id !== null) - .map(r => [r.account_id as string, r.email]), + emailRows.filter(r => r.account_id !== null).map(r => [r.account_id as string, r.email]), ); const accounts = stats.map(s => ({ diff --git a/lib/ai/__tests__/getAvailableModels.test.ts b/lib/ai/__tests__/getAvailableModels.test.ts index 05a9014b..271752bc 100644 --- a/lib/ai/__tests__/getAvailableModels.test.ts +++ b/lib/ai/__tests__/getAvailableModels.test.ts @@ -1,14 +1,14 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { gateway } from "@ai-sdk/gateway"; +import { getAvailableModels } from "../getAvailableModels"; + vi.mock("@ai-sdk/gateway", () => ({ gateway: { getAvailableModels: vi.fn(), }, })); -import { gateway } from "@ai-sdk/gateway"; -import { getAvailableModels } from "../getAvailableModels"; - const mockGatewayGetAvailableModels = vi.mocked(gateway.getAvailableModels); describe("getAvailableModels", () => { @@ -35,7 +35,7 @@ describe("getAvailableModels", () => { // Should filter out embed models (output price = 0) expect(models).toHaveLength(2); - expect(models.map((m) => m.id)).toEqual(["gpt-4", "claude-3-opus"]); + expect(models.map(m => m.id)).toEqual(["gpt-4", "claude-3-opus"]); }); it("returns empty array when gateway returns no models", async () => { diff --git a/lib/ai/__tests__/getModel.test.ts b/lib/ai/__tests__/getModel.test.ts index 52ec40c2..367fa40c 100644 --- a/lib/ai/__tests__/getModel.test.ts +++ b/lib/ai/__tests__/getModel.test.ts @@ -1,12 +1,12 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { getAvailableModels } from "@/lib/ai/getAvailableModels"; +import { getModel } from "../getModel"; + vi.mock("@/lib/ai/getAvailableModels", () => ({ getAvailableModels: vi.fn(), })); -import { getAvailableModels } from "@/lib/ai/getAvailableModels"; -import { getModel } from "../getModel"; - const mockGetAvailableModels = vi.mocked(getAvailableModels); describe("getModel", () => { @@ -35,9 +35,7 @@ describe("getModel", () => { }); it("returns undefined when model is not found", async () => { - const models = [ - { id: "gpt-4", pricing: { input: "0.00003", output: "0.00006" } }, - ]; + const models = [{ id: "gpt-4", pricing: { input: "0.00003", output: "0.00006" } }]; mockGetAvailableModels.mockResolvedValue(models as any); const model = await getModel("unknown-model"); diff --git a/lib/ai/getAvailableModels.ts b/lib/ai/getAvailableModels.ts index 8ecfb8f3..a46fd79e 100644 --- a/lib/ai/getAvailableModels.ts +++ b/lib/ai/getAvailableModels.ts @@ -5,12 +5,10 @@ import isEmbedModel from "./isEmbedModel"; * Returns the list of available LLMs from the Vercel AI Gateway. * Filters out embed models that are not suitable for chat. */ -export const getAvailableModels = async (): Promise< - GatewayLanguageModelEntry[] -> => { +export const getAvailableModels = async (): Promise => { try { const apiResponse = await gateway.getAvailableModels(); - const gatewayModels = apiResponse.models.filter((m) => !isEmbedModel(m)); + const gatewayModels = apiResponse.models.filter(m => !isEmbedModel(m)); return gatewayModels; } catch { return []; diff --git a/lib/ai/getModel.ts b/lib/ai/getModel.ts index b802acaf..99ca9c2f 100644 --- a/lib/ai/getModel.ts +++ b/lib/ai/getModel.ts @@ -3,15 +3,14 @@ import { GatewayLanguageModelEntry } from "@ai-sdk/gateway"; /** * Returns a specific model by its ID from the list of available models. + * * @param modelId - The ID of the model to find * @returns The matching model or undefined if not found */ -export const getModel = async ( - modelId: string, -): Promise => { +export const getModel = async (modelId: string): Promise => { try { const availableModels = await getAvailableModels(); - return availableModels.find((model) => model.id === modelId); + return availableModels.find(model => model.id === modelId); } catch (error) { console.error(`Failed to get model with ID ${modelId}:`, error); return undefined; diff --git a/lib/ai/isEmbedModel.ts b/lib/ai/isEmbedModel.ts index 7c5fbbfb..4901f1e8 100644 --- a/lib/ai/isEmbedModel.ts +++ b/lib/ai/isEmbedModel.ts @@ -3,6 +3,8 @@ import { GatewayLanguageModelEntry } from "@ai-sdk/gateway"; /** * Determines if a model is an embedding model (not suitable for chat). * Embed models typically have 0 output pricing since they only produce embeddings. + * + * @param m */ export const isEmbedModel = (m: GatewayLanguageModelEntry): boolean => { const pricing = m.pricing; diff --git a/lib/artist/validateArtistSocialsScrapeBody.ts b/lib/artist/validateArtistSocialsScrapeBody.ts index 7ef98c2a..a6d5cf8e 100644 --- a/lib/artist/validateArtistSocialsScrapeBody.ts +++ b/lib/artist/validateArtistSocialsScrapeBody.ts @@ -36,4 +36,3 @@ export function validateArtistSocialsScrapeBody( return validationResult.data; } - diff --git a/lib/artists/__tests__/buildGetArtistsParams.test.ts b/lib/artists/__tests__/buildGetArtistsParams.test.ts index 398d670b..6e5ec1f1 100644 --- a/lib/artists/__tests__/buildGetArtistsParams.test.ts +++ b/lib/artists/__tests__/buildGetArtistsParams.test.ts @@ -1,12 +1,12 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { buildGetArtistsParams } from "../buildGetArtistsParams"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; + vi.mock("@/lib/organizations/canAccessAccount", () => ({ canAccessAccount: vi.fn(), })); -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; - describe("buildGetArtistsParams", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/artists/__tests__/checkAccountArtistAccess.test.ts b/lib/artists/__tests__/checkAccountArtistAccess.test.ts index 1bb0b253..9966402e 100644 --- a/lib/artists/__tests__/checkAccountArtistAccess.test.ts +++ b/lib/artists/__tests__/checkAccountArtistAccess.test.ts @@ -1,6 +1,10 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { checkAccountArtistAccess } from "../checkAccountArtistAccess"; +import { selectAccountArtistId } from "@/lib/supabase/account_artist_ids/selectAccountArtistId"; +import { selectArtistOrganizationIds } from "@/lib/supabase/artist_organization_ids/selectArtistOrganizationIds"; +import { selectAccountOrganizationIds } from "@/lib/supabase/account_organization_ids/selectAccountOrganizationIds"; + vi.mock("@/lib/supabase/account_artist_ids/selectAccountArtistId", () => ({ selectAccountArtistId: vi.fn(), })); @@ -13,10 +17,6 @@ vi.mock("@/lib/supabase/account_organization_ids/selectAccountOrganizationIds", selectAccountOrganizationIds: vi.fn(), })); -import { selectAccountArtistId } from "@/lib/supabase/account_artist_ids/selectAccountArtistId"; -import { selectArtistOrganizationIds } from "@/lib/supabase/artist_organization_ids/selectArtistOrganizationIds"; -import { selectAccountOrganizationIds } from "@/lib/supabase/account_organization_ids/selectAccountOrganizationIds"; - describe("checkAccountArtistAccess", () => { beforeEach(() => { vi.clearAllMocks(); @@ -34,12 +34,8 @@ describe("checkAccountArtistAccess", () => { it("should return true when account and artist share an organization", async () => { vi.mocked(selectAccountArtistId).mockResolvedValue(null); - vi.mocked(selectArtistOrganizationIds).mockResolvedValue([ - { organization_id: "org-1" }, - ]); - vi.mocked(selectAccountOrganizationIds).mockResolvedValue([ - { organization_id: "org-1" }, - ]); + vi.mocked(selectArtistOrganizationIds).mockResolvedValue([{ organization_id: "org-1" }]); + vi.mocked(selectAccountOrganizationIds).mockResolvedValue([{ organization_id: "org-1" }]); const result = await checkAccountArtistAccess("account-123", "artist-456"); @@ -69,9 +65,7 @@ describe("checkAccountArtistAccess", () => { it("should return false when account org lookup errors (fail closed)", async () => { vi.mocked(selectAccountArtistId).mockResolvedValue(null); - vi.mocked(selectArtistOrganizationIds).mockResolvedValue([ - { organization_id: "org-1" }, - ]); + vi.mocked(selectArtistOrganizationIds).mockResolvedValue([{ organization_id: "org-1" }]); vi.mocked(selectAccountOrganizationIds).mockResolvedValue(null); const result = await checkAccountArtistAccess("account-123", "artist-456"); diff --git a/lib/artists/__tests__/createArtistInDb.test.ts b/lib/artists/__tests__/createArtistInDb.test.ts index e979fbb5..eaed9442 100644 --- a/lib/artists/__tests__/createArtistInDb.test.ts +++ b/lib/artists/__tests__/createArtistInDb.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { createArtistInDb } from "../createArtistInDb"; + const mockInsertAccount = vi.fn(); const mockInsertAccountInfo = vi.fn(); const mockSelectAccountWithSocials = vi.fn(); @@ -26,8 +28,6 @@ vi.mock("@/lib/supabase/artist_organization_ids/addArtistToOrganization", () => addArtistToOrganization: (...args: unknown[]) => mockAddArtistToOrganization(...args), })); -import { createArtistInDb } from "../createArtistInDb"; - describe("createArtistInDb", () => { const mockAccount = { id: "artist-123", diff --git a/lib/artists/__tests__/createArtistPostHandler.test.ts b/lib/artists/__tests__/createArtistPostHandler.test.ts index e63d244d..dd72b2e1 100644 --- a/lib/artists/__tests__/createArtistPostHandler.test.ts +++ b/lib/artists/__tests__/createArtistPostHandler.test.ts @@ -14,6 +14,11 @@ vi.mock("@/lib/auth/validateAuthContext", () => ({ validateAuthContext: (...args: unknown[]) => mockValidateAuthContext(...args), })); +/** + * + * @param body + * @param headers + */ function createRequest(body: unknown, headers: Record = {}): NextRequest { const defaultHeaders: Record = { "Content-Type": "application/json", diff --git a/lib/artists/__tests__/validateCreateArtistBody.test.ts b/lib/artists/__tests__/validateCreateArtistBody.test.ts index 4de5562b..d12fe1ba 100644 --- a/lib/artists/__tests__/validateCreateArtistBody.test.ts +++ b/lib/artists/__tests__/validateCreateArtistBody.test.ts @@ -9,6 +9,11 @@ vi.mock("@/lib/auth/validateAuthContext", () => ({ validateAuthContext: (...args: unknown[]) => mockValidateAuthContext(...args), })); +/** + * + * @param body + * @param headers + */ function createRequest(body: unknown, headers: Record = {}): NextRequest { const defaultHeaders: Record = { "Content-Type": "application/json" }; return new NextRequest("http://localhost/api/artists", { diff --git a/lib/artists/__tests__/validateGetArtistsRequest.test.ts b/lib/artists/__tests__/validateGetArtistsRequest.test.ts index 699f80c6..d7449d81 100644 --- a/lib/artists/__tests__/validateGetArtistsRequest.test.ts +++ b/lib/artists/__tests__/validateGetArtistsRequest.test.ts @@ -2,6 +2,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest, NextResponse } from "next/server"; import { validateGetArtistsRequest } from "../validateGetArtistsRequest"; +import { validateAuthContext } from "@/lib/auth/validateAuthContext"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; + vi.mock("@/lib/auth/validateAuthContext", () => ({ validateAuthContext: vi.fn(), })); @@ -14,9 +17,6 @@ vi.mock("@/lib/networking/getCorsHeaders", () => ({ getCorsHeaders: vi.fn(() => new Headers()), })); -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; - describe("validateGetArtistsRequest", () => { beforeEach(() => { vi.clearAllMocks(); @@ -76,9 +76,7 @@ describe("validateGetArtistsRequest", () => { authToken: "test-token", }); - const request = new NextRequest( - `http://localhost/api/artists?org_id=${orgFilterId}`, - ); + const request = new NextRequest(`http://localhost/api/artists?org_id=${orgFilterId}`); const result = await validateGetArtistsRequest(request); expect(result).not.toBeInstanceOf(NextResponse); @@ -95,9 +93,7 @@ describe("validateGetArtistsRequest", () => { authToken: "test-token", }); - const request = new NextRequest( - "http://localhost/api/artists?account_id=not-a-uuid", - ); + const request = new NextRequest("http://localhost/api/artists?account_id=not-a-uuid"); const result = await validateGetArtistsRequest(request); expect(result).toBeInstanceOf(NextResponse); @@ -111,9 +107,7 @@ describe("validateGetArtistsRequest", () => { authToken: "test-token", }); - const request = new NextRequest( - "http://localhost/api/artists?org_id=not-a-uuid", - ); + const request = new NextRequest("http://localhost/api/artists?org_id=not-a-uuid"); const result = await validateGetArtistsRequest(request); expect(result).toBeInstanceOf(NextResponse); @@ -130,9 +124,7 @@ describe("validateGetArtistsRequest", () => { }); vi.mocked(canAccessAccount).mockResolvedValue(true); - const request = new NextRequest( - `http://localhost/api/artists?account_id=${targetAccountId}`, - ); + const request = new NextRequest(`http://localhost/api/artists?account_id=${targetAccountId}`); const result = await validateGetArtistsRequest(request); expect(canAccessAccount).toHaveBeenCalledWith({ @@ -155,9 +147,7 @@ describe("validateGetArtistsRequest", () => { }); vi.mocked(canAccessAccount).mockResolvedValue(false); - const request = new NextRequest( - `http://localhost/api/artists?account_id=${otherAccountId}`, - ); + const request = new NextRequest(`http://localhost/api/artists?account_id=${otherAccountId}`); const result = await validateGetArtistsRequest(request); expect(result).toBeInstanceOf(NextResponse); @@ -174,9 +164,7 @@ describe("validateGetArtistsRequest", () => { }); vi.mocked(canAccessAccount).mockResolvedValue(false); - const request = new NextRequest( - `http://localhost/api/artists?account_id=${notInOrgId}`, - ); + const request = new NextRequest(`http://localhost/api/artists?account_id=${notInOrgId}`); const result = await validateGetArtistsRequest(request); expect(canAccessAccount).toHaveBeenCalledWith({ diff --git a/lib/artists/checkAccountArtistAccess.ts b/lib/artists/checkAccountArtistAccess.ts index a0b8defe..9b851f76 100644 --- a/lib/artists/checkAccountArtistAccess.ts +++ b/lib/artists/checkAccountArtistAccess.ts @@ -31,9 +31,7 @@ export async function checkAccountArtistAccess( if (!artistOrgs.length) return false; - const orgIds = artistOrgs - .map((o) => o.organization_id) - .filter((id): id is string => Boolean(id)); + const orgIds = artistOrgs.map(o => o.organization_id).filter((id): id is string => Boolean(id)); if (!orgIds.length) return false; const userOrgAccess = await selectAccountOrganizationIds(accountId, orgIds); diff --git a/lib/artists/createArtistPostHandler.ts b/lib/artists/createArtistPostHandler.ts index 58bed3f7..80cb3adf 100644 --- a/lib/artists/createArtistPostHandler.ts +++ b/lib/artists/createArtistPostHandler.ts @@ -19,9 +19,7 @@ import { createArtistInDb } from "@/lib/artists/createArtistInDb"; * @param request - The request object containing JSON body * @returns A NextResponse with artist data or error */ -export async function createArtistPostHandler( - request: NextRequest, -): Promise { +export async function createArtistPostHandler(request: NextRequest): Promise { const validated = await validateCreateArtistBody(request); if (validated instanceof NextResponse) { return validated; @@ -41,10 +39,7 @@ export async function createArtistPostHandler( ); } - return NextResponse.json( - { artist }, - { status: 201, headers: getCorsHeaders() }, - ); + return NextResponse.json({ artist }, { status: 201, headers: getCorsHeaders() }); } catch (error) { const message = error instanceof Error ? error.message : "Failed to create artist"; return NextResponse.json( diff --git a/lib/artists/getFormattedArtist.ts b/lib/artists/getFormattedArtist.ts index 8aa2211f..a4f75e72 100644 --- a/lib/artists/getFormattedArtist.ts +++ b/lib/artists/getFormattedArtist.ts @@ -60,13 +60,11 @@ export function getFormattedArtist(row: ArtistQueryRow): FormattedArtist { instruction: "", }; - const account_socials = (artist.account_socials || []).map( - (social: AccountSocialWithSocial) => ({ - ...social.social, - link: social.social?.profile_url || "", - type: getSocialPlatformByLink(social.social?.profile_url || ""), - }), - ); + const account_socials = (artist.account_socials || []).map((social: AccountSocialWithSocial) => ({ + ...social.social, + link: social.social?.profile_url || "", + type: getSocialPlatformByLink(social.social?.profile_url || ""), + })); return { name: artist.name, @@ -76,4 +74,3 @@ export function getFormattedArtist(row: ArtistQueryRow): FormattedArtist { pinned: row.pinned || false, }; } - diff --git a/lib/artists/getSocialPlatformByLink.ts b/lib/artists/getSocialPlatformByLink.ts index aaf6ab68..744ad626 100644 --- a/lib/artists/getSocialPlatformByLink.ts +++ b/lib/artists/getSocialPlatformByLink.ts @@ -17,4 +17,3 @@ export function getSocialPlatformByLink(link: string): string { return "NONE"; } - diff --git a/lib/artists/validateArtistsQuery.ts b/lib/artists/validateArtistsQuery.ts index 9c02188d..91b5a908 100644 --- a/lib/artists/validateArtistsQuery.ts +++ b/lib/artists/validateArtistsQuery.ts @@ -37,4 +37,3 @@ export function validateArtistsQuery(searchParams: URLSearchParams): NextRespons return result.data; } - diff --git a/lib/arweave/isIPFSUrl.ts b/lib/arweave/isIPFSUrl.ts index ba9e5484..271ce35f 100644 --- a/lib/arweave/isIPFSUrl.ts +++ b/lib/arweave/isIPFSUrl.ts @@ -10,4 +10,3 @@ import { isGatewayIPFSUrl } from "./isGatewayIPFSUrl"; export function isIPFSUrl(url: string | null | undefined): boolean { return url ? isNormalizedIPFSURL(url) || isGatewayIPFSUrl(url) : false; } - diff --git a/lib/arweave/isNormalizedIPFSURL.ts b/lib/arweave/isNormalizedIPFSURL.ts index cdfbe372..7275e645 100644 --- a/lib/arweave/isNormalizedIPFSURL.ts +++ b/lib/arweave/isNormalizedIPFSURL.ts @@ -7,4 +7,3 @@ export function isNormalizedIPFSURL(url: string | null | undefined): boolean { return url && typeof url === "string" ? url.startsWith("ipfs://") : false; } - diff --git a/lib/auth/__tests__/validateAuthContext.test.ts b/lib/auth/__tests__/validateAuthContext.test.ts index 911ecbc4..ac44727d 100644 --- a/lib/auth/__tests__/validateAuthContext.test.ts +++ b/lib/auth/__tests__/validateAuthContext.test.ts @@ -39,6 +39,10 @@ const mockGetApiKeyDetails = vi.mocked(getApiKeyDetails); const mockValidateOrganizationAccess = vi.mocked(validateOrganizationAccess); const mockCanAccessAccount = vi.mocked(canAccessAccount); +/** + * + * @param headers + */ function createMockRequest(headers: Record = {}): Request { return { headers: { diff --git a/lib/catalog/formatCatalogSongsAsCSV.ts b/lib/catalog/formatCatalogSongsAsCSV.ts index f9d0bf22..29cc443c 100644 --- a/lib/catalog/formatCatalogSongsAsCSV.ts +++ b/lib/catalog/formatCatalogSongsAsCSV.ts @@ -2,10 +2,12 @@ import { CatalogSong } from "./getCatalogSongs"; /** * Formats catalog songs into the CSV-like format expected by the scorer + * + * @param songs */ export function formatCatalogSongsAsCSV(songs: CatalogSong[]): string { - const csvLines = songs.map((song) => { - const artistNames = song.artists.map((artist) => artist.name).join(", "); + const csvLines = songs.map(song => { + const artistNames = song.artists.map(artist => artist.name).join(", "); return `${song.isrc},"${song.name}","${song.album}","${artistNames}"`; }); diff --git a/lib/catalog/getCatalogDataAsCSV.ts b/lib/catalog/getCatalogDataAsCSV.ts index ea529c37..4a86fc0e 100644 --- a/lib/catalog/getCatalogDataAsCSV.ts +++ b/lib/catalog/getCatalogDataAsCSV.ts @@ -3,6 +3,8 @@ import { formatCatalogSongsAsCSV } from "./formatCatalogSongsAsCSV"; /** * Gets all catalog songs and formats them as CSV for the scorer + * + * @param catalogId */ export async function getCatalogDataAsCSV(catalogId: string): Promise { const allSongs: CatalogSong[] = []; diff --git a/lib/catalog/getCatalogSongs.ts b/lib/catalog/getCatalogSongs.ts index b82747a6..d7b5ca62 100644 --- a/lib/catalog/getCatalogSongs.ts +++ b/lib/catalog/getCatalogSongs.ts @@ -25,11 +25,18 @@ export interface CatalogSongsResponse { error?: string; } +/** + * + * @param catalogId + * @param pageSize + * @param page + * @param artistName + */ export async function getCatalogSongs( catalogId: string, pageSize: number = 100, page: number = 1, - artistName?: string + artistName?: string, ): Promise { try { const params = new URLSearchParams({ @@ -42,15 +49,12 @@ export async function getCatalogSongs( params.append("artistName", artistName); } - const response = await fetch( - `${NEW_API_BASE_URL}/api/catalogs/songs?${params}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - } - ); + const response = await fetch(`${NEW_API_BASE_URL}/api/catalogs/songs?${params}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); if (!response.ok) { const errorText = await response.text(); diff --git a/lib/catalog/getCatalogs.ts b/lib/catalog/getCatalogs.ts index 179d1f3a..4ac8a842 100644 --- a/lib/catalog/getCatalogs.ts +++ b/lib/catalog/getCatalogs.ts @@ -8,9 +8,11 @@ export interface CatalogsResponse { error?: string; } -export async function getCatalogs( - accountId: string -): Promise { +/** + * + * @param accountId + */ +export async function getCatalogs(accountId: string): Promise { try { const response = await fetch( `https://api.recoupable.com/api/catalogs?account_id=${accountId}`, @@ -19,7 +21,7 @@ export async function getCatalogs( headers: { "Content-Type": "application/json", }, - } + }, ); if (!response.ok) { diff --git a/lib/chat/__tests__/buildSystemPromptWithImages.test.ts b/lib/chat/__tests__/buildSystemPromptWithImages.test.ts index 37717cee..7f5e6d48 100644 --- a/lib/chat/__tests__/buildSystemPromptWithImages.test.ts +++ b/lib/chat/__tests__/buildSystemPromptWithImages.test.ts @@ -57,7 +57,7 @@ describe("buildSystemPromptWithImages", () => { const result = buildSystemPromptWithImages(basePrompt, imageUrls); const lines = result.split("\n"); - const imageLines = lines.filter((line) => line.startsWith("- Image")); + const imageLines = lines.filter(line => line.startsWith("- Image")); expect(imageLines).toHaveLength(2); }); }); diff --git a/lib/chat/__tests__/handleChatCompletion.test.ts b/lib/chat/__tests__/handleChatCompletion.test.ts index 098a8a8c..aab50328 100644 --- a/lib/chat/__tests__/handleChatCompletion.test.ts +++ b/lib/chat/__tests__/handleChatCompletion.test.ts @@ -1,6 +1,17 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import type { UIMessage } from "ai"; +import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom"; +import upsertMemory from "@/lib/supabase/memories/upsertMemory"; +import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification"; +import { generateChatTitle } from "@/lib/chat/generateChatTitle"; +import { handleSendEmailToolOutputs } from "@/lib/emails/handleSendEmailToolOutputs"; +import { sendErrorNotification } from "@/lib/telegram/sendErrorNotification"; +import { handleChatCompletion } from "../handleChatCompletion"; +import type { ChatRequestBody } from "../validateChatRequest"; + // Mock all dependencies before importing the module under test vi.mock("@/lib/supabase/account_emails/selectAccountEmails", () => ({ default: vi.fn(), @@ -34,17 +45,6 @@ vi.mock("@/lib/telegram/sendErrorNotification", () => ({ sendErrorNotification: vi.fn(), })); -import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails"; -import selectRoom from "@/lib/supabase/rooms/selectRoom"; -import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom"; -import upsertMemory from "@/lib/supabase/memories/upsertMemory"; -import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification"; -import { generateChatTitle } from "@/lib/chat/generateChatTitle"; -import { handleSendEmailToolOutputs } from "@/lib/emails/handleSendEmailToolOutputs"; -import { sendErrorNotification } from "@/lib/telegram/sendErrorNotification"; -import { handleChatCompletion } from "../handleChatCompletion"; -import type { ChatRequestBody } from "../validateChatRequest"; - const mockSelectAccountEmails = vi.mocked(selectAccountEmails); const mockSelectRoom = vi.mocked(selectRoom); const mockUpsertRoom = vi.mocked(upsertRoom); @@ -55,6 +55,12 @@ const mockHandleSendEmailToolOutputs = vi.mocked(handleSendEmailToolOutputs); const mockSendErrorNotification = vi.mocked(sendErrorNotification); // Helper to create mock UIMessage +/** + * + * @param id + * @param role + * @param text + */ function createMockUIMessage(id: string, role: "user" | "assistant", text: string): UIMessage { return { id, @@ -65,6 +71,10 @@ function createMockUIMessage(id: string, role: "user" | "assistant", text: strin } // Helper to create mock ChatRequestBody +/** + * + * @param overrides + */ function createMockBody(overrides: Partial = {}): ChatRequestBody { return { accountId: "account-123", @@ -122,7 +132,7 @@ describe("handleChatCompletion", () => { const responseMessages = [createMockUIMessage("resp-1", "assistant", "Hi there!")]; const callOrder: string[] = []; - mockUpsertMemory.mockImplementation(async (params) => { + mockUpsertMemory.mockImplementation(async params => { callOrder.push(params.id); return null; }); diff --git a/lib/chat/__tests__/integration/chatEndToEnd.test.ts b/lib/chat/__tests__/integration/chatEndToEnd.test.ts index 6e075cc9..f08d5bcb 100644 --- a/lib/chat/__tests__/integration/chatEndToEnd.test.ts +++ b/lib/chat/__tests__/integration/chatEndToEnd.test.ts @@ -1,6 +1,24 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { NextResponse } from "next/server"; +import { getApiKeyAccountId } from "@/lib/auth/getApiKeyAccountId"; +import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails"; +import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo"; +import { getAccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetails"; +import { getKnowledgeBaseText } from "@/lib/files/getKnowledgeBaseText"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom"; +import upsertMemory from "@/lib/supabase/memories/upsertMemory"; +import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification"; +import { handleSendEmailToolOutputs } from "@/lib/emails/handleSendEmailToolOutputs"; +import { getCreditUsage } from "@/lib/credits/getCreditUsage"; +import { deductCredits } from "@/lib/credits/deductCredits"; +import { generateChatTitle } from "../../generateChatTitle"; +import { handleChatCompletion } from "../../handleChatCompletion"; +import { handleChatCredits } from "@/lib/credits/handleChatCredits"; +import { validateChatRequest } from "../../validateChatRequest"; +import { setupChatRequest } from "../../setupChatRequest"; + /** * Integration tests for chat endpoints. * @@ -137,24 +155,6 @@ vi.mock("ai", () => ({ })), })); -import { getApiKeyAccountId } from "@/lib/auth/getApiKeyAccountId"; -import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails"; -import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo"; -import { getAccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetails"; -import { getKnowledgeBaseText } from "@/lib/files/getKnowledgeBaseText"; -import selectRoom from "@/lib/supabase/rooms/selectRoom"; -import { upsertRoom } from "@/lib/supabase/rooms/upsertRoom"; -import upsertMemory from "@/lib/supabase/memories/upsertMemory"; -import { sendNewConversationNotification } from "@/lib/telegram/sendNewConversationNotification"; -import { handleSendEmailToolOutputs } from "@/lib/emails/handleSendEmailToolOutputs"; -import { getCreditUsage } from "@/lib/credits/getCreditUsage"; -import { deductCredits } from "@/lib/credits/deductCredits"; -import { generateChatTitle } from "../../generateChatTitle"; -import { handleChatCompletion } from "../../handleChatCompletion"; -import { handleChatCredits } from "@/lib/credits/handleChatCredits"; -import { validateChatRequest } from "../../validateChatRequest"; -import { setupChatRequest } from "../../setupChatRequest"; - const mockGetApiKeyAccountId = vi.mocked(getApiKeyAccountId); const mockSelectAccountEmails = vi.mocked(selectAccountEmails); const mockSelectAccountInfo = vi.mocked(selectAccountInfo); @@ -170,10 +170,12 @@ const mockDeductCredits = vi.mocked(deductCredits); const mockGenerateChatTitle = vi.mocked(generateChatTitle); // Helper to create mock NextRequest -function createMockRequest( - body: unknown, - headers: Record = {}, -): Request { +/** + * + * @param body + * @param headers + */ +function createMockRequest(body: unknown, headers: Record = {}): Request { return { json: () => Promise.resolve(body), headers: { @@ -210,10 +212,7 @@ describe("Chat Integration Tests", () => { it("validates and returns body for valid request with prompt", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-123"); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "valid-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "valid-key" }); const result = await validateChatRequest(request as any); @@ -251,16 +250,10 @@ describe("Chat Integration Tests", () => { it("returns 401 when API key lookup fails", async () => { // getApiKeyAccountId returns a NextResponse when authentication fails mockGetApiKeyAccountId.mockResolvedValue( - NextResponse.json( - { status: "error", message: "Unauthorized" }, - { status: 401 }, - ), + NextResponse.json({ status: "error", message: "Unauthorized" }, { status: 401 }), ); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "invalid-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "invalid-key" }); const result = await validateChatRequest(request as any); @@ -271,10 +264,7 @@ describe("Chat Integration Tests", () => { it("returns 400 when neither messages nor prompt is provided", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-123"); - const request = createMockRequest( - { roomId: "room-123" }, - { "x-api-key": "valid-key" }, - ); + const request = createMockRequest({ roomId: "room-123" }, { "x-api-key": "valid-key" }); const result = await validateChatRequest(request as any); @@ -406,9 +396,7 @@ describe("Chat Integration Tests", () => { mockGenerateChatTitle.mockResolvedValue("New Chat Title"); const body = { - messages: [ - { id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }, - ], + messages: [{ id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }], roomId: "new-room-123", accountId: "account-123", }; @@ -433,9 +421,7 @@ describe("Chat Integration Tests", () => { mockSelectRoom.mockResolvedValue({ id: "existing-room" } as any); const body = { - messages: [ - { id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }, - ], + messages: [{ id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }], roomId: "existing-room", accountId: "account-123", }; @@ -454,9 +440,7 @@ describe("Chat Integration Tests", () => { mockSelectRoom.mockResolvedValue({ id: "room-123" } as any); const body = { - messages: [ - { id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }, - ], + messages: [{ id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }], roomId: "room-123", accountId: "account-123", }; @@ -486,9 +470,7 @@ describe("Chat Integration Tests", () => { mockSelectRoom.mockResolvedValue({ id: "room-123" } as any); const body = { - messages: [ - { id: "msg-1", role: "user", parts: [{ type: "text", text: "Send an email" }] }, - ], + messages: [{ id: "msg-1", role: "user", parts: [{ type: "text", text: "Send an email" }] }], roomId: "room-123", accountId: "account-123", }; @@ -516,9 +498,7 @@ describe("Chat Integration Tests", () => { mockSelectRoom.mockRejectedValue(new Error("Database error")); const body = { - messages: [ - { id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }, - ], + messages: [{ id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }], roomId: "room-123", accountId: "account-123", }; @@ -535,9 +515,7 @@ describe("Chat Integration Tests", () => { it("handles empty roomId by defaulting to empty string", async () => { const body = { - messages: [ - { id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }, - ], + messages: [{ id: "msg-1", role: "user", parts: [{ type: "text", text: "Hello" }] }], accountId: "account-123", // roomId not provided }; @@ -629,10 +607,7 @@ describe("Chat Integration Tests", () => { it("validates prompt-based requests through full pipeline", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-123"); - const request = createMockRequest( - { prompt: "What is 2+2?" }, - { "x-api-key": "valid-key" }, - ); + const request = createMockRequest({ prompt: "What is 2+2?" }, { "x-api-key": "valid-key" }); const validationResult = await validateChatRequest(request as any); expect(validationResult).not.toBeInstanceOf(NextResponse); diff --git a/lib/chat/__tests__/saveChatCompletion.test.ts b/lib/chat/__tests__/saveChatCompletion.test.ts index f25d7177..ddd995f6 100644 --- a/lib/chat/__tests__/saveChatCompletion.test.ts +++ b/lib/chat/__tests__/saveChatCompletion.test.ts @@ -1,5 +1,10 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { getMessages } from "@/lib/messages/getMessages"; +import filterMessageContentForMemories from "@/lib/messages/filterMessageContentForMemories"; +import insertMemories from "@/lib/supabase/memories/insertMemories"; +import { saveChatCompletion } from "../saveChatCompletion"; + // Mock dependencies before importing the module under test vi.mock("@/lib/messages/getMessages", () => ({ getMessages: vi.fn(), @@ -13,11 +18,6 @@ vi.mock("@/lib/supabase/memories/insertMemories", () => ({ default: vi.fn(), })); -import { getMessages } from "@/lib/messages/getMessages"; -import filterMessageContentForMemories from "@/lib/messages/filterMessageContentForMemories"; -import insertMemories from "@/lib/supabase/memories/insertMemories"; -import { saveChatCompletion } from "../saveChatCompletion"; - const mockGetMessages = vi.mocked(getMessages); const mockFilterMessageContentForMemories = vi.mocked(filterMessageContentForMemories); const mockInsertMemories = vi.mocked(insertMemories); diff --git a/lib/chat/__tests__/setupConversation.test.ts b/lib/chat/__tests__/setupConversation.test.ts index 9f395d3d..f0b8f317 100644 --- a/lib/chat/__tests__/setupConversation.test.ts +++ b/lib/chat/__tests__/setupConversation.test.ts @@ -1,5 +1,12 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { generateUUID } from "@/lib/uuid/generateUUID"; +import { createNewRoom } from "@/lib/chat/createNewRoom"; +import insertMemories from "@/lib/supabase/memories/insertMemories"; +import filterMessageContentForMemories from "@/lib/messages/filterMessageContentForMemories"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import { setupConversation } from "../setupConversation"; + // Mock dependencies vi.mock("@/lib/uuid/generateUUID", () => { const mockFn = vi.fn(() => "mock-uuid"); @@ -25,13 +32,6 @@ vi.mock("@/lib/supabase/rooms/selectRoom", () => ({ default: vi.fn(), })); -import { generateUUID } from "@/lib/uuid/generateUUID"; -import { createNewRoom } from "@/lib/chat/createNewRoom"; -import insertMemories from "@/lib/supabase/memories/insertMemories"; -import filterMessageContentForMemories from "@/lib/messages/filterMessageContentForMemories"; -import selectRoom from "@/lib/supabase/rooms/selectRoom"; -import { setupConversation } from "../setupConversation"; - const mockGenerateUUID = vi.mocked(generateUUID); const mockCreateNewRoom = vi.mocked(createNewRoom); const mockInsertMemories = vi.mocked(insertMemories); diff --git a/lib/chat/__tests__/setupToolsForRequest.test.ts b/lib/chat/__tests__/setupToolsForRequest.test.ts index c5b4bbf2..68b0b48c 100644 --- a/lib/chat/__tests__/setupToolsForRequest.test.ts +++ b/lib/chat/__tests__/setupToolsForRequest.test.ts @@ -1,6 +1,11 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { ChatRequestBody } from "../validateChatRequest"; +// Import after mocks +import { setupToolsForRequest } from "../setupToolsForRequest"; +import { getMcpTools } from "@/lib/mcp/getMcpTools"; +import { getComposioTools } from "@/lib/composio/toolRouter"; + // Mock external dependencies vi.mock("@/lib/mcp/getMcpTools", () => ({ getMcpTools: vi.fn(), @@ -10,11 +15,6 @@ vi.mock("@/lib/composio/toolRouter", () => ({ getComposioTools: vi.fn(), })); -// Import after mocks -import { setupToolsForRequest } from "../setupToolsForRequest"; -import { getMcpTools } from "@/lib/mcp/getMcpTools"; -import { getComposioTools } from "@/lib/composio/toolRouter"; - const mockGetMcpTools = vi.mocked(getMcpTools); const mockGetComposioTools = vi.mocked(getComposioTools); @@ -238,14 +238,14 @@ describe("setupToolsForRequest", () => { // Track when each operation starts and completes mockGetMcpTools.mockImplementation(async () => { executionOrder.push("getMcpTools:start"); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise(resolve => setTimeout(resolve, 10)); executionOrder.push("getMcpTools:end"); return { mcpTool: { description: "MCP Tool", parameters: {} } }; }); mockGetComposioTools.mockImplementation(async () => { executionOrder.push("getComposioTools:start"); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise(resolve => setTimeout(resolve, 10)); executionOrder.push("getComposioTools:end"); return { composioTool: { description: "Composio Tool", parameters: {} } }; }); @@ -293,6 +293,4 @@ describe("setupToolsForRequest", () => { expect(mockGetComposioTools).toHaveBeenCalledTimes(1); }); }); - - }); diff --git a/lib/chat/__tests__/validateChatRequest.test.ts b/lib/chat/__tests__/validateChatRequest.test.ts index a5876eca..2f7651d0 100644 --- a/lib/chat/__tests__/validateChatRequest.test.ts +++ b/lib/chat/__tests__/validateChatRequest.test.ts @@ -2,6 +2,17 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextResponse } from "next/server"; import { validateChatRequest, chatRequestSchema } from "../validateChatRequest"; +import { getApiKeyAccountId } from "@/lib/auth/getApiKeyAccountId"; +import { getAuthenticatedAccountId } from "@/lib/auth/getAuthenticatedAccountId"; +import { validateOverrideAccountId } from "@/lib/accounts/validateOverrideAccountId"; +import { getApiKeyDetails } from "@/lib/keys/getApiKeyDetails"; +import { validateOrganizationAccess } from "@/lib/organizations/validateOrganizationAccess"; +import { generateUUID } from "@/lib/uuid/generateUUID"; +import { createNewRoom } from "@/lib/chat/createNewRoom"; +import insertMemories from "@/lib/supabase/memories/insertMemories"; +import filterMessageContentForMemories from "@/lib/messages/filterMessageContentForMemories"; +import { setupConversation } from "@/lib/chat/setupConversation"; + // Mock dependencies vi.mock("@/lib/auth/getApiKeyAccountId", () => ({ getApiKeyAccountId: vi.fn(), @@ -47,17 +58,6 @@ vi.mock("@/lib/chat/setupConversation", () => ({ setupConversation: vi.fn(), })); -import { getApiKeyAccountId } from "@/lib/auth/getApiKeyAccountId"; -import { getAuthenticatedAccountId } from "@/lib/auth/getAuthenticatedAccountId"; -import { validateOverrideAccountId } from "@/lib/accounts/validateOverrideAccountId"; -import { getApiKeyDetails } from "@/lib/keys/getApiKeyDetails"; -import { validateOrganizationAccess } from "@/lib/organizations/validateOrganizationAccess"; -import { generateUUID } from "@/lib/uuid/generateUUID"; -import { createNewRoom } from "@/lib/chat/createNewRoom"; -import insertMemories from "@/lib/supabase/memories/insertMemories"; -import filterMessageContentForMemories from "@/lib/messages/filterMessageContentForMemories"; -import { setupConversation } from "@/lib/chat/setupConversation"; - const mockGetApiKeyAccountId = vi.mocked(getApiKeyAccountId); const mockGetAuthenticatedAccountId = vi.mocked(getAuthenticatedAccountId); const mockValidateOverrideAccountId = vi.mocked(validateOverrideAccountId); @@ -70,6 +70,11 @@ const mockFilterMessageContentForMemories = vi.mocked(filterMessageContentForMem const mockSetupConversation = vi.mocked(setupConversation); // Helper to create mock NextRequest +/** + * + * @param body + * @param headers + */ function createMockRequest(body: unknown, headers: Record = {}): Request { return { json: () => Promise.resolve(body), @@ -94,10 +99,7 @@ describe("validateChatRequest", () => { it("rejects when neither messages nor prompt is provided", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-123"); - const request = createMockRequest( - { roomId: "room-123" }, - { "x-api-key": "test-key" }, - ); + const request = createMockRequest({ roomId: "room-123" }, { "x-api-key": "test-key" }); const result = await validateChatRequest(request as any); @@ -143,10 +145,7 @@ describe("validateChatRequest", () => { it("accepts valid prompt string", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-123"); - const request = createMockRequest( - { prompt: "Hello, world!" }, - { "x-api-key": "test-key" }, - ); + const request = createMockRequest({ prompt: "Hello, world!" }, { "x-api-key": "test-key" }); const result = await validateChatRequest(request as any); @@ -183,16 +182,10 @@ describe("validateChatRequest", () => { it("rejects request with invalid API key", async () => { mockGetApiKeyAccountId.mockResolvedValue( - NextResponse.json( - { status: "error", message: "Invalid API key" }, - { status: 401 }, - ), + NextResponse.json({ status: "error", message: "Invalid API key" }, { status: 401 }), ); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "invalid-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "invalid-key" }); const result = await validateChatRequest(request as any); @@ -202,10 +195,7 @@ describe("validateChatRequest", () => { it("uses accountId from valid API key", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-abc-123"); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "valid-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "valid-key" }); const result = await validateChatRequest(request as any); @@ -254,10 +244,7 @@ describe("validateChatRequest", () => { orgId: "org-account-123", }); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "org-api-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "org-api-key" }); const result = await validateChatRequest(request as any); @@ -273,10 +260,7 @@ describe("validateChatRequest", () => { orgId: null, }); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "personal-api-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "personal-api-key" }); const result = await validateChatRequest(request as any); @@ -350,10 +334,7 @@ describe("validateChatRequest", () => { it("converts prompt to messages array", async () => { mockGetApiKeyAccountId.mockResolvedValue("account-123"); - const request = createMockRequest( - { prompt: "Hello, world!" }, - { "x-api-key": "test-key" }, - ); + const request = createMockRequest({ prompt: "Hello, world!" }, { "x-api-key": "test-key" }); const result = await validateChatRequest(request as any); @@ -583,10 +564,7 @@ describe("validateChatRequest", () => { orgId: "api-key-org-123", }); - const request = createMockRequest( - { prompt: "Hello" }, - { "x-api-key": "org-api-key" }, - ); + const request = createMockRequest({ prompt: "Hello" }, { "x-api-key": "org-api-key" }); const result = await validateChatRequest(request as any); @@ -635,7 +613,10 @@ describe("validateChatRequest", () => { memoryId: "memory-id", }); - const request = createMockRequest({ prompt: "Create a new room" }, { "x-api-key": "test-key" }); + const request = createMockRequest( + { prompt: "Create a new room" }, + { "x-api-key": "test-key" }, + ); await validateChatRequest(request as any); diff --git a/lib/chat/handleChatCompletion.ts b/lib/chat/handleChatCompletion.ts index 0a162ec6..81dedabb 100644 --- a/lib/chat/handleChatCompletion.ts +++ b/lib/chat/handleChatCompletion.ts @@ -47,8 +47,7 @@ export async function handleChatCompletion( // Create room and send notification if this is a new conversation if (!room) { - const latestMessageText = - lastMessage.parts.find((part) => part.type === "text")?.text || ""; + const latestMessageText = lastMessage.parts.find(part => part.type === "text")?.text || ""; const conversationName = await generateChatTitle(latestMessageText); await Promise.all([ diff --git a/lib/chat/handleChatStream.ts b/lib/chat/handleChatStream.ts index fe971374..b181bcde 100644 --- a/lib/chat/handleChatStream.ts +++ b/lib/chat/handleChatStream.ts @@ -31,25 +31,23 @@ export async function handleChatStream(request: NextRequest): Promise const stream = createUIMessageStream({ originalMessages: body.messages, generateId: generateUUID, - execute: async (options) => { + execute: async options => { const { writer } = options; const result = await agent.stream(chatConfig); writer.merge(result.toUIMessageStream()); // Note: Credit handling and chat completion handling will be added // as part of the handleChatCredits and handleChatCompletion migrations }, - onFinish: async (event) => { + onFinish: async event => { if (event.isAborted) { return; } - const assistantMessages = event.messages.filter( - (message) => message.role === "assistant", - ); + const assistantMessages = event.messages.filter(message => message.role === "assistant"); const responseMessages = assistantMessages.length > 0 ? assistantMessages : [event.responseMessage]; await handleChatCompletion(body, responseMessages); }, - onError: (e) => { + onError: e => { console.error("/api/chat onError:", e); return JSON.stringify({ status: "error", diff --git a/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts b/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts index 3f1ace6b..df582c01 100644 --- a/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts +++ b/lib/chat/toolChains/__tests__/getPrepareStepResult.test.ts @@ -1,13 +1,11 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import getPrepareStepResult from "../getPrepareStepResult"; + // Mock the toolChains module vi.mock("../toolChains", () => ({ TOOL_CHAINS: { - test_trigger: [ - { toolName: "step_one" }, - { toolName: "step_two" }, - { toolName: "step_three" }, - ], + test_trigger: [{ toolName: "step_one" }, { toolName: "step_two" }, { toolName: "step_three" }], custom_chain: [ { toolName: "custom_step_one", @@ -25,8 +23,6 @@ vi.mock("../toolChains", () => ({ }, })); -import getPrepareStepResult from "../getPrepareStepResult"; - describe("getPrepareStepResult", () => { beforeEach(() => { vi.clearAllMocks(); @@ -50,7 +46,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "unrelated_tool", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "unrelated_tool", + output: { type: "json", value: {} }, + }, ], }, ], @@ -70,7 +70,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "test_trigger", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "test_trigger", + output: { type: "json", value: {} }, + }, ], }, ], @@ -90,7 +94,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "test_trigger", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "test_trigger", + output: { type: "json", value: {} }, + }, ], }, { @@ -116,7 +124,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "test_trigger", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "test_trigger", + output: { type: "json", value: {} }, + }, ], }, { @@ -149,12 +161,20 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "test_trigger", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "test_trigger", + output: { type: "json", value: {} }, + }, ], }, { toolResults: [ - { toolCallId: "call-2", toolName: "some_other_tool", output: { type: "json", value: {} } }, + { + toolCallId: "call-2", + toolName: "some_other_tool", + output: { type: "json", value: {} }, + }, ], }, { @@ -183,7 +203,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "custom_chain", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "custom_chain", + output: { type: "json", value: {} }, + }, ], }, ], @@ -206,12 +230,20 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "custom_chain", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "custom_chain", + output: { type: "json", value: {} }, + }, ], }, { toolResults: [ - { toolCallId: "call-2", toolName: "custom_step_one", output: { type: "json", value: {} } }, + { + toolCallId: "call-2", + toolName: "custom_step_one", + output: { type: "json", value: {} }, + }, ], }, ], @@ -237,7 +269,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "test_trigger", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "test_trigger", + output: { type: "json", value: {} }, + }, ], }, { @@ -260,7 +296,11 @@ describe("getPrepareStepResult", () => { steps: [ { toolResults: [ - { toolCallId: "call-1", toolName: "test_trigger", output: { type: "json", value: {} } }, + { + toolCallId: "call-1", + toolName: "test_trigger", + output: { type: "json", value: {} }, + }, ], }, ], diff --git a/lib/chat/toolChains/__tests__/toolChains.test.ts b/lib/chat/toolChains/__tests__/toolChains.test.ts index 59a23aab..fdd5782f 100644 --- a/lib/chat/toolChains/__tests__/toolChains.test.ts +++ b/lib/chat/toolChains/__tests__/toolChains.test.ts @@ -1,10 +1,5 @@ import { describe, it, expect } from "vitest"; -import { - TOOL_CHAINS, - TOOL_MODEL_MAP, - ToolChainItem, - PrepareStepResult, -} from "../toolChains"; +import { TOOL_CHAINS, TOOL_MODEL_MAP, ToolChainItem, PrepareStepResult } from "../toolChains"; describe("toolChains", () => { describe("ToolChainItem type", () => { @@ -86,7 +81,7 @@ describe("toolChains", () => { it("includes update_account_info with custom system prompt", () => { const chain = TOOL_CHAINS.create_new_artist; const updateAccountStep = chain.find( - (item) => item.toolName === "update_account_info" && item.system + item => item.toolName === "update_account_info" && item.system, ); expect(updateAccountStep).toBeDefined(); expect(updateAccountStep?.system).toContain("get_spotify_search"); @@ -106,16 +101,14 @@ describe("toolChains", () => { it("does not include create_knowledge_base tool", () => { const chain = TOOL_CHAINS.create_new_artist; const hasCreateKnowledgeBase = chain.some( - (item) => item.toolName === "create_knowledge_base" + item => item.toolName === "create_knowledge_base", ); expect(hasCreateKnowledgeBase).toBe(false); }); it("includes generate_txt_file with system prompt for knowledge base report", () => { const chain = TOOL_CHAINS.create_new_artist; - const generateStep = chain.find( - (item) => item.toolName === "generate_txt_file" - ); + const generateStep = chain.find(item => item.toolName === "generate_txt_file"); expect(generateStep).toBeDefined(); expect(generateStep?.system).toBeDefined(); expect(generateStep?.system).toContain("knowledge base report"); @@ -123,12 +116,8 @@ describe("toolChains", () => { it("includes update_account_info step with system prompt to link knowledge base", () => { const chain = TOOL_CHAINS.create_new_artist; - const updateSteps = chain.filter( - (item) => item.toolName === "update_account_info" - ); - const knowledgeLinkStep = updateSteps.find( - (item) => item.system?.includes("knowledges") - ); + const updateSteps = chain.filter(item => item.toolName === "update_account_info"); + const knowledgeLinkStep = updateSteps.find(item => item.system?.includes("knowledges")); expect(knowledgeLinkStep).toBeDefined(); expect(knowledgeLinkStep?.system).toContain("arweaveUrl"); }); @@ -142,9 +131,7 @@ describe("toolChains", () => { it("includes generate_txt_file with custom system prompt", () => { const chain = TOOL_CHAINS.create_release_report; - const generateStep = chain.find( - (item) => item.toolName === "generate_txt_file" - ); + const generateStep = chain.find(item => item.toolName === "generate_txt_file"); expect(generateStep).toBeDefined(); expect(generateStep?.system).toBeDefined(); }); diff --git a/lib/chat/toolChains/getExecutedToolTimeline.ts b/lib/chat/toolChains/getExecutedToolTimeline.ts index 1e522d70..32bf281a 100644 --- a/lib/chat/toolChains/getExecutedToolTimeline.ts +++ b/lib/chat/toolChains/getExecutedToolTimeline.ts @@ -10,16 +10,16 @@ type ToolCallContent = { const getExecutedToolTimeline = (steps: StepResult[]): string[] => { const toolCallsContent = steps.flatMap( (step): ToolCallContent[] => - step.toolResults?.map((result) => ({ + step.toolResults?.map(result => ({ type: "tool-result" as const, toolCallId: result.toolCallId || "", toolName: result.toolName, output: { type: "json" as const, value: result.output }, - })) || [] + })) || [], ); // Build timeline of executed tools from toolCallsContent - return toolCallsContent.map((call) => call.toolName); + return toolCallsContent.map(call => call.toolName); }; export default getExecutedToolTimeline; diff --git a/lib/chat/toolChains/getPrepareStepResult.ts b/lib/chat/toolChains/getPrepareStepResult.ts index 5a908afa..c011c078 100644 --- a/lib/chat/toolChains/getPrepareStepResult.ts +++ b/lib/chat/toolChains/getPrepareStepResult.ts @@ -12,10 +12,10 @@ type PrepareStepOptions = { /** * Returns the next tool to run based on timeline progression through tool chains. * Uses toolCallsContent to track exact execution order and position in sequence. + * + * @param options */ -const getPrepareStepResult = ( - options: PrepareStepOptions -): PrepareStepResult | undefined => { +const getPrepareStepResult = (options: PrepareStepOptions): PrepareStepResult | undefined => { const { steps } = options; // Extract tool calls timeline (history) from steps const executedTimeline = getExecutedToolTimeline(steps); @@ -32,10 +32,7 @@ const getPrepareStepResult = ( let timelinePosition = triggerIndex; // Walk through the timeline starting from trigger - while ( - timelinePosition < executedTimeline.length && - sequencePosition < fullSequence.length - ) { + while (timelinePosition < executedTimeline.length && sequencePosition < fullSequence.length) { const currentTool = executedTimeline[timelinePosition]; const expectedTool = fullSequence[sequencePosition].toolName; diff --git a/lib/chat/toolChains/index.ts b/lib/chat/toolChains/index.ts index 7d52b25e..24bc4a87 100644 --- a/lib/chat/toolChains/index.ts +++ b/lib/chat/toolChains/index.ts @@ -1,4 +1,9 @@ -export { TOOL_CHAINS, TOOL_MODEL_MAP, type ToolChainItem, type PrepareStepResult } from "./toolChains"; +export { + TOOL_CHAINS, + TOOL_MODEL_MAP, + type ToolChainItem, + type PrepareStepResult, +} from "./toolChains"; export { default as getPrepareStepResult } from "./getPrepareStepResult"; export { default as getExecutedToolTimeline } from "./getExecutedToolTimeline"; export { createNewArtistToolChain } from "./createNewArtistToolChain"; diff --git a/lib/chat/validateMessages.ts b/lib/chat/validateMessages.ts index 7ae4c8e0..e67aec8f 100644 --- a/lib/chat/validateMessages.ts +++ b/lib/chat/validateMessages.ts @@ -14,8 +14,6 @@ export function validateMessages(messages: UIMessage[]) { return { lastMessage: messages[messages.length - 1], - validMessages: messages.filter( - (m) => m.parts.find((part) => part.type === "text")?.text?.length, - ), + validMessages: messages.filter(m => m.parts.find(part => part.type === "text")?.text?.length), }; } diff --git a/lib/chats/__tests__/updateChatHandler.test.ts b/lib/chats/__tests__/updateChatHandler.test.ts index cbb33603..b5969544 100644 --- a/lib/chats/__tests__/updateChatHandler.test.ts +++ b/lib/chats/__tests__/updateChatHandler.test.ts @@ -2,6 +2,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest, NextResponse } from "next/server"; import { updateChatHandler } from "../updateChatHandler"; +import { validateUpdateChatBody } from "@/lib/chats/validateUpdateChatBody"; +import { updateRoom } from "@/lib/supabase/rooms/updateRoom"; + vi.mock("@/lib/networking/getCorsHeaders", () => ({ getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), })); @@ -14,9 +17,6 @@ vi.mock("@/lib/supabase/rooms/updateRoom", () => ({ updateRoom: vi.fn(), })); -import { validateUpdateChatBody } from "@/lib/chats/validateUpdateChatBody"; -import { updateRoom } from "@/lib/supabase/rooms/updateRoom"; - describe("updateChatHandler", () => { const mockRequest = () => { return new NextRequest("http://localhost/api/chats", { @@ -87,10 +87,7 @@ describe("updateChatHandler", () => { it("returns 401 from validation when auth fails", async () => { vi.mocked(validateUpdateChatBody).mockResolvedValue( - NextResponse.json( - { status: "error", error: "Unauthorized" }, - { status: 401 }, - ), + NextResponse.json({ status: "error", error: "Unauthorized" }, { status: 401 }), ); const request = mockRequest(); @@ -102,10 +99,7 @@ describe("updateChatHandler", () => { it("returns 404 from validation when chat not found", async () => { vi.mocked(validateUpdateChatBody).mockResolvedValue( - NextResponse.json( - { status: "error", error: "Chat room not found" }, - { status: 404 }, - ), + NextResponse.json({ status: "error", error: "Chat room not found" }, { status: 404 }), ); const request = mockRequest(); diff --git a/lib/chats/__tests__/validateUpdateChatBody.test.ts b/lib/chats/__tests__/validateUpdateChatBody.test.ts index d3328d98..e59e8c64 100644 --- a/lib/chats/__tests__/validateUpdateChatBody.test.ts +++ b/lib/chats/__tests__/validateUpdateChatBody.test.ts @@ -2,6 +2,10 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest, NextResponse } from "next/server"; import { validateUpdateChatBody } from "../validateUpdateChatBody"; +import { validateAuthContext } from "@/lib/auth/validateAuthContext"; +import selectRoom from "@/lib/supabase/rooms/selectRoom"; +import { buildGetChatsParams } from "@/lib/chats/buildGetChatsParams"; + vi.mock("@/lib/networking/getCorsHeaders", () => ({ getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), })); @@ -18,10 +22,6 @@ vi.mock("@/lib/chats/buildGetChatsParams", () => ({ buildGetChatsParams: vi.fn(), })); -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import selectRoom from "@/lib/supabase/rooms/selectRoom"; -import { buildGetChatsParams } from "@/lib/chats/buildGetChatsParams"; - describe("validateUpdateChatBody", () => { beforeEach(() => { vi.clearAllMocks(); @@ -296,10 +296,7 @@ describe("validateUpdateChatBody", () => { describe("authentication errors", () => { it("returns 401 when auth fails", async () => { vi.mocked(validateAuthContext).mockResolvedValue( - NextResponse.json( - { status: "error", error: "Unauthorized" }, - { status: 401 }, - ), + NextResponse.json({ status: "error", error: "Unauthorized" }, { status: 401 }), ); const request = createRequest({ diff --git a/lib/chats/buildGetChatsParams.ts b/lib/chats/buildGetChatsParams.ts index 71f3c097..f2723651 100644 --- a/lib/chats/buildGetChatsParams.ts +++ b/lib/chats/buildGetChatsParams.ts @@ -58,7 +58,7 @@ export async function buildGetChatsParams( if (org_id) { // Org key: fetch all member account IDs for this organization const orgMembers = await getAccountOrganizations({ organizationId: org_id }); - const memberAccountIds = orgMembers.map((member) => member.account_id); + const memberAccountIds = orgMembers.map(member => member.account_id); return { params: { account_ids: memberAccountIds, artist_id }, error: null }; } diff --git a/lib/chats/processCompactChatRequest.ts b/lib/chats/processCompactChatRequest.ts index 73ba607c..ba085123 100644 --- a/lib/chats/processCompactChatRequest.ts +++ b/lib/chats/processCompactChatRequest.ts @@ -18,6 +18,10 @@ interface ProcessCompactChatRequestParams { * Verifies the chat exists and the user has access before compacting. * * @param params - The parameters for processing the chat compaction. + * @param params.chatId + * @param params.prompt + * @param params.accountId + * @param params.orgId * @returns The result of the compaction attempt. */ export async function processCompactChatRequest({ diff --git a/lib/coding-agent/__tests__/encodeGitHubThreadId.test.ts b/lib/coding-agent/__tests__/encodeGitHubThreadId.test.ts index b69c7224..10afc197 100644 --- a/lib/coding-agent/__tests__/encodeGitHubThreadId.test.ts +++ b/lib/coding-agent/__tests__/encodeGitHubThreadId.test.ts @@ -3,9 +3,9 @@ import { encodeGitHubThreadId } from "../encodeGitHubThreadId"; describe("encodeGitHubThreadId", () => { it("encodes PR-level thread ID", () => { - expect( - encodeGitHubThreadId({ owner: "recoupable", repo: "tasks", prNumber: 68 }), - ).toBe("github:recoupable/tasks:68"); + expect(encodeGitHubThreadId({ owner: "recoupable", repo: "tasks", prNumber: 68 })).toBe( + "github:recoupable/tasks:68", + ); }); it("encodes review comment thread ID", () => { diff --git a/lib/coding-agent/__tests__/handleCodingAgentCallback.test.ts b/lib/coding-agent/__tests__/handleCodingAgentCallback.test.ts index 06819dca..ebc0e519 100644 --- a/lib/coding-agent/__tests__/handleCodingAgentCallback.test.ts +++ b/lib/coding-agent/__tests__/handleCodingAgentCallback.test.ts @@ -131,7 +131,9 @@ describe("handleCodingAgentCallback", () => { expect(response.status).toBe(200); // stdout should be posted first, then the card expect(mockPost).toHaveBeenCalledTimes(2); - expect(mockPost.mock.calls[0][0]).toBe("Created branch agent/fix-bug-1234\nModified 3 files in api/"); + expect(mockPost.mock.calls[0][0]).toBe( + "Created branch agent/fix-bug-1234\nModified 3 files in api/", + ); expect(mockPost.mock.calls[1][0]).toEqual(expect.objectContaining({ card: expect.anything() })); }); diff --git a/lib/coding-agent/__tests__/handleGitHubWebhook.test.ts b/lib/coding-agent/__tests__/handleGitHubWebhook.test.ts index 5e059f4e..194a7170 100644 --- a/lib/coding-agent/__tests__/handleGitHubWebhook.test.ts +++ b/lib/coding-agent/__tests__/handleGitHubWebhook.test.ts @@ -45,6 +45,12 @@ const BASE_PAYLOAD = { }, }; +/** + * + * @param body + * @param event + * @param signature + */ function makeRequest(body: unknown, event = "issue_comment", signature = "valid") { return { text: () => Promise.resolve(JSON.stringify(body)), diff --git a/lib/coding-agent/__tests__/onMergeAction.test.ts b/lib/coding-agent/__tests__/onMergeAction.test.ts index fde84379..56f25c32 100644 --- a/lib/coding-agent/__tests__/onMergeAction.test.ts +++ b/lib/coding-agent/__tests__/onMergeAction.test.ts @@ -160,14 +160,15 @@ describe("registerOnMergeAction", () => { await handler({ thread: mockThread, actionId: "merge_pr:recoupable/api#42" }); expect(mockHandleMergeSuccess).not.toHaveBeenCalled(); - expect(mockThread.post).toHaveBeenCalledWith("❌ recoupable/api#42 failed to merge: Not allowed"); + expect(mockThread.post).toHaveBeenCalledWith( + "❌ recoupable/api#42 failed to merge: Not allowed", + ); }); it("merge button click triggers snapshot upsert in Supabase via handleMergeSuccess", async () => { // Use the real handleMergeSuccess instead of the mock to verify the full chain - const { handleMergeSuccess: realHandleMergeSuccess } = await vi.importActual< - typeof import("../handleMergeSuccess") - >("../handleMergeSuccess"); + const { handleMergeSuccess: realHandleMergeSuccess } = + await vi.importActual("../handleMergeSuccess"); mockHandleMergeSuccess.mockImplementation(realHandleMergeSuccess); mockMergeGithubPR.mockResolvedValue({ ok: true }); diff --git a/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts b/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts index 8405b85f..f173d6ce 100644 --- a/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts +++ b/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts @@ -5,15 +5,16 @@ vi.mock("../mergeGithubBranch", () => ({ mergeGithubBranch: (...args: unknown[]) => mockMergeGithubBranch(...args), })); -const { registerOnMergeTestToMainAction } = await import( - "../handlers/onMergeTestToMainAction" -); +const { registerOnMergeTestToMainAction } = await import("../handlers/onMergeTestToMainAction"); beforeEach(() => { vi.clearAllMocks(); process.env.GITHUB_TOKEN = "ghp_test"; }); +/** + * + */ function createMockBot() { return { onAction: vi.fn() } as any; } @@ -36,7 +37,12 @@ describe("registerOnMergeTestToMainAction", () => { await handler({ thread: mockThread, actionId: "merge_test_to_main:recoupable/chat" }); - expect(mockMergeGithubBranch).toHaveBeenCalledWith("recoupable/chat", "test", "main", "ghp_test"); + expect(mockMergeGithubBranch).toHaveBeenCalledWith( + "recoupable/chat", + "test", + "main", + "ghp_test", + ); expect(mockThread.post).toHaveBeenCalledWith("✅ Merged test → main for recoupable/chat."); }); diff --git a/lib/coding-agent/__tests__/parseMergeTestToMainActionId.test.ts b/lib/coding-agent/__tests__/parseMergeTestToMainActionId.test.ts index 40a4683b..4bfff65b 100644 --- a/lib/coding-agent/__tests__/parseMergeTestToMainActionId.test.ts +++ b/lib/coding-agent/__tests__/parseMergeTestToMainActionId.test.ts @@ -3,7 +3,9 @@ import { parseMergeTestToMainActionId } from "../parseMergeTestToMainActionId"; describe("parseMergeTestToMainActionId", () => { it("parses a valid action ID", () => { - expect(parseMergeTestToMainActionId("merge_test_to_main:recoupable/api")).toBe("recoupable/api"); + expect(parseMergeTestToMainActionId("merge_test_to_main:recoupable/api")).toBe( + "recoupable/api", + ); }); it("parses action ID with hyphenated repo name", () => { diff --git a/lib/coding-agent/bot.ts b/lib/coding-agent/bot.ts index 88160c5c..1b6fefdc 100644 --- a/lib/coding-agent/bot.ts +++ b/lib/coding-agent/bot.ts @@ -1,23 +1,15 @@ import { Chat, ConsoleLogger } from "chat"; import { SlackAdapter } from "@chat-adapter/slack"; -import { createWhatsAppAdapter, WhatsAppAdapter } from "@chat-adapter/whatsapp"; import { createGitHubAdapter } from "@chat-adapter/github"; import { createIoRedisState } from "@chat-adapter/state-ioredis"; import redis from "@/lib/redis/connection"; import type { CodingAgentThreadState } from "./types"; import { validateCodingAgentEnv } from "./validateEnv"; -import { isWhatsAppConfigured } from "./whatsApp/isWhatsAppConfigured"; const logger = new ConsoleLogger(); -type CodingAgentAdapters = { - slack: SlackAdapter; - github: ReturnType; - whatsapp?: WhatsAppAdapter; -}; - /** - * Creates a new Chat bot instance configured with Slack, GitHub, and optionally WhatsApp adapters. + * Creates a new Chat bot instance configured with the Slack adapter. */ export function createCodingAgentBot() { validateCodingAgentEnv(); @@ -48,15 +40,12 @@ export function createCodingAgentBot() { logger, }); - const adapters: CodingAgentAdapters = { slack, github }; - - if (isWhatsAppConfigured()) { - adapters.whatsapp = createWhatsAppAdapter({ logger }); - } - - return new Chat({ + return new Chat< + { slack: SlackAdapter; github: ReturnType }, + CodingAgentThreadState + >({ userName: "Recoup Agent", - adapters, + adapters: { slack, github }, state, }); } diff --git a/lib/coding-agent/buildPRCard.ts b/lib/coding-agent/buildPRCard.ts index b46c6c1f..61ca0a87 100644 --- a/lib/coding-agent/buildPRCard.ts +++ b/lib/coding-agent/buildPRCard.ts @@ -11,7 +11,9 @@ export function buildPRCard(title: string, prs: CodingAgentPR[]) { return Card({ title, children: [ - CardText(`${prs.map(pr => `- ${pr.repo}#${pr.number} → \`${pr.baseBranch}\``).join("\n")}\n\nReply in this thread to give feedback.`), + CardText( + `${prs.map(pr => `- ${pr.repo}#${pr.number} → \`${pr.baseBranch}\``).join("\n")}\n\nReply in this thread to give feedback.`, + ), Actions([ ...prs.flatMap(pr => [ LinkButton({ url: pr.url, label: `Review ${pr.repo}#${pr.number}` }), diff --git a/lib/coding-agent/encodeGitHubThreadId.ts b/lib/coding-agent/encodeGitHubThreadId.ts index 1cfff2fe..f4797e43 100644 --- a/lib/coding-agent/encodeGitHubThreadId.ts +++ b/lib/coding-agent/encodeGitHubThreadId.ts @@ -6,6 +6,8 @@ import type { GitHubThreadId } from "@chat-adapter/github"; * * - PR-level: `github:{owner}/{repo}:{prNumber}` * - Review comment: `github:{owner}/{repo}:{prNumber}:rc:{reviewCommentId}` + * + * @param thread */ export function encodeGitHubThreadId(thread: GitHubThreadId): string { const { owner, repo, prNumber, reviewCommentId } = thread; diff --git a/lib/coding-agent/handleMergeSuccess.ts b/lib/coding-agent/handleMergeSuccess.ts index f026f48d..c241923b 100644 --- a/lib/coding-agent/handleMergeSuccess.ts +++ b/lib/coding-agent/handleMergeSuccess.ts @@ -7,6 +7,8 @@ import type { CodingAgentThreadState } from "./types"; * Handles post-merge cleanup after all PRs merged successfully. * Deletes the shared PR state keys for all repos and persists the latest * snapshot via upsertAccountSnapshot. + * + * @param state */ export async function handleMergeSuccess(state: CodingAgentThreadState): Promise { try { diff --git a/lib/coding-agent/handlers/onMergeAction.ts b/lib/coding-agent/handlers/onMergeAction.ts index ccb5a82c..887e3525 100644 --- a/lib/coding-agent/handlers/onMergeAction.ts +++ b/lib/coding-agent/handlers/onMergeAction.ts @@ -36,9 +36,7 @@ export function registerOnMergeAction(bot: CodingAgentBot) { return; } - const pr = state?.prs?.find( - p => p.repo === parsed.repo && p.number === parsed.number, - ); + const pr = state?.prs?.find(p => p.repo === parsed.repo && p.number === parsed.number); if (!pr) { await thread.post(`PR ${parsed.repo}#${parsed.number} not found in this thread.`); @@ -54,9 +52,7 @@ export function registerOnMergeAction(bot: CodingAgentBot) { } // Remove merged PR from state - const remainingPrs = state!.prs!.filter( - p => !(p.repo === pr.repo && p.number === pr.number), - ); + const remainingPrs = state!.prs!.filter(p => !(p.repo === pr.repo && p.number === pr.number)); const allMerged = remainingPrs.length === 0; await thread.setState({ diff --git a/lib/coding-agent/handlers/onNewMention.ts b/lib/coding-agent/handlers/onNewMention.ts index ad647a52..63514474 100644 --- a/lib/coding-agent/handlers/onNewMention.ts +++ b/lib/coding-agent/handlers/onNewMention.ts @@ -44,7 +44,7 @@ export function registerOnNewMention(bot: CodingAgentBot) { status: "running", prompt, runId: handle.id, - threadId: thread.id, + slackThreadId: thread.id, }); } catch (error) { console.error("[coding-agent] onNewMention error:", error); diff --git a/lib/coding-agent/mergeGithubBranch.ts b/lib/coding-agent/mergeGithubBranch.ts index 5efeff31..74024a0f 100644 --- a/lib/coding-agent/mergeGithubBranch.ts +++ b/lib/coding-agent/mergeGithubBranch.ts @@ -24,22 +24,19 @@ export async function mergeGithubBranch( token: string, ): Promise { const [owner, repoName] = repo.split("/"); - const response = await fetch( - `https://api.github.com/repos/${owner}/${repoName}/merges`, - { - method: "POST", - headers: { - Authorization: `Bearer ${token}`, - Accept: "application/vnd.github+json", - "X-GitHub-Api-Version": "2022-11-28", - }, - body: JSON.stringify({ - base, - head, - commit_message: `Merge ${head} into ${base}`, - }), + const response = await fetch(`https://api.github.com/repos/${owner}/${repoName}/merges`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + Accept: "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", }, - ); + body: JSON.stringify({ + base, + head, + commit_message: `Merge ${head} into ${base}`, + }), + }); // 201 = merged, 204 = already up to date (both are success) if (response.ok) { diff --git a/lib/coding-agent/parseMergeActionId.ts b/lib/coding-agent/parseMergeActionId.ts index 5118249e..25fd3eeb 100644 --- a/lib/coding-agent/parseMergeActionId.ts +++ b/lib/coding-agent/parseMergeActionId.ts @@ -1,6 +1,8 @@ /** * Parses a merge action ID like "merge_pr:recoupable/api#42" * into { repo, number } or null if the format doesn't match. + * + * @param actionId */ export function parseMergeActionId(actionId: string) { const match = actionId.match(/^merge_pr:(.+)#(\d+)$/); diff --git a/lib/coding-agent/parseMergeTestToMainActionId.ts b/lib/coding-agent/parseMergeTestToMainActionId.ts index 1228615f..14133eac 100644 --- a/lib/coding-agent/parseMergeTestToMainActionId.ts +++ b/lib/coding-agent/parseMergeTestToMainActionId.ts @@ -1,6 +1,8 @@ /** * Parses a merge_test_to_main action ID like "merge_test_to_main:recoupable/api" * into the repo string, or null if the format doesn't match. + * + * @param actionId */ export function parseMergeTestToMainActionId(actionId: string): string | null { const prefix = "merge_test_to_main:"; diff --git a/lib/coding-agent/postGitHubComment.ts b/lib/coding-agent/postGitHubComment.ts index 007c36a1..be399352 100644 --- a/lib/coding-agent/postGitHubComment.ts +++ b/lib/coding-agent/postGitHubComment.ts @@ -12,17 +12,14 @@ export async function postGitHubComment( ): Promise { const token = process.env.GITHUB_TOKEN; - const response = await fetch( - `https://api.github.com/repos/${repo}/issues/${prNumber}/comments`, - { - method: "POST", - headers: { - Authorization: `token ${token}`, - Accept: "application/vnd.github+json", - }, - body: JSON.stringify({ body }), + const response = await fetch(`https://api.github.com/repos/${repo}/issues/${prNumber}/comments`, { + method: "POST", + headers: { + Authorization: `token ${token}`, + Accept: "application/vnd.github+json", }, - ); + body: JSON.stringify({ body }), + }); if (!response.ok) { const text = await response.text(); diff --git a/lib/coding-agent/types.ts b/lib/coding-agent/types.ts index 76bb6461..8092e66b 100644 --- a/lib/coding-agent/types.ts +++ b/lib/coding-agent/types.ts @@ -6,8 +6,7 @@ export interface CodingAgentThreadState { status: "running" | "pr_created" | "updating" | "merged" | "failed" | "no_changes"; prompt: string; runId?: string; - /** Platform-agnostic thread identifier (Slack or WhatsApp thread ID). */ - threadId?: string; + slackThreadId?: string; branch?: string; snapshotId?: string; prs?: CodingAgentPR[]; diff --git a/lib/coding-agent/validateEnv.ts b/lib/coding-agent/validateEnv.ts index 5b893cb8..425a32d7 100644 --- a/lib/coding-agent/validateEnv.ts +++ b/lib/coding-agent/validateEnv.ts @@ -1,5 +1,3 @@ -import { WHATSAPP_ENV_VARS } from "./whatsApp/isWhatsAppConfigured"; - const REQUIRED_ENV_VARS = [ "SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET", @@ -11,17 +9,10 @@ const REQUIRED_ENV_VARS = [ /** * Validates that all required environment variables for the coding agent are set. - * WhatsApp variables are validated as a group — if any are set, all must be present. * Throws an error listing all missing variables. */ export function validateCodingAgentEnv(): void { - const missing: string[] = REQUIRED_ENV_VARS.filter(name => !process.env[name]); - - const whatsappSet = WHATSAPP_ENV_VARS.filter(name => process.env[name]); - if (whatsappSet.length > 0 && whatsappSet.length < WHATSAPP_ENV_VARS.length) { - const whatsappMissing = WHATSAPP_ENV_VARS.filter(name => !process.env[name]); - missing.push(...whatsappMissing); - } + const missing = REQUIRED_ENV_VARS.filter(name => !process.env[name]); if (missing.length > 0) { throw new Error( diff --git a/lib/composio/connectors/__tests__/getConnectorsHandler.test.ts b/lib/composio/connectors/__tests__/getConnectorsHandler.test.ts index c214bf71..d6c3fe31 100644 --- a/lib/composio/connectors/__tests__/getConnectorsHandler.test.ts +++ b/lib/composio/connectors/__tests__/getConnectorsHandler.test.ts @@ -62,9 +62,7 @@ describe("getConnectorsHandler", () => { { slug: "tiktok", name: "TikTok", isConnected: true }, ]); - const request = new NextRequest( - "http://localhost/api/connectors?account_id=account-456", - ); + const request = new NextRequest("http://localhost/api/connectors?account_id=account-456"); await getConnectorsHandler(request); // API is unopinionated — no allowedToolkits filtering diff --git a/lib/composio/connectors/__tests__/validateAuthorizeConnectorRequest.test.ts b/lib/composio/connectors/__tests__/validateAuthorizeConnectorRequest.test.ts index 87966100..294201c1 100644 --- a/lib/composio/connectors/__tests__/validateAuthorizeConnectorRequest.test.ts +++ b/lib/composio/connectors/__tests__/validateAuthorizeConnectorRequest.test.ts @@ -145,7 +145,10 @@ describe("validateAuthorizeConnectorRequest", () => { orgId: null, authToken: "test-token", }); - vi.mocked(checkAccountAccess).mockResolvedValue({ hasAccess: true, entityType: "organization" }); + vi.mocked(checkAccountAccess).mockResolvedValue({ + hasAccess: true, + entityType: "organization", + }); const request = new NextRequest("http://localhost/api/connectors/authorize", { method: "POST", diff --git a/lib/composio/connectors/__tests__/validateGetConnectorsRequest.test.ts b/lib/composio/connectors/__tests__/validateGetConnectorsRequest.test.ts index 5dc514ff..dc51dbec 100644 --- a/lib/composio/connectors/__tests__/validateGetConnectorsRequest.test.ts +++ b/lib/composio/connectors/__tests__/validateGetConnectorsRequest.test.ts @@ -62,7 +62,9 @@ describe("validateGetConnectorsRequest", () => { }); vi.mocked(checkAccountAccess).mockResolvedValue({ hasAccess: true, entityType: "artist" }); - const request = new NextRequest(`http://localhost/api/connectors?account_id=${mockTargetAccountId}`); + const request = new NextRequest( + `http://localhost/api/connectors?account_id=${mockTargetAccountId}`, + ); const result = await validateGetConnectorsRequest(request); expect(checkAccountAccess).toHaveBeenCalledWith(mockAccountId, mockTargetAccountId); @@ -82,7 +84,9 @@ describe("validateGetConnectorsRequest", () => { }); vi.mocked(checkAccountAccess).mockResolvedValue({ hasAccess: false }); - const request = new NextRequest(`http://localhost/api/connectors?account_id=${mockTargetAccountId}`); + const request = new NextRequest( + `http://localhost/api/connectors?account_id=${mockTargetAccountId}`, + ); const result = await validateGetConnectorsRequest(request); expect(result).toBeInstanceOf(NextResponse); diff --git a/lib/composio/connectors/disconnectConnectorHandler.ts b/lib/composio/connectors/disconnectConnectorHandler.ts index 02d93526..1bb348f6 100644 --- a/lib/composio/connectors/disconnectConnectorHandler.ts +++ b/lib/composio/connectors/disconnectConnectorHandler.ts @@ -36,7 +36,10 @@ export async function disconnectConnectorHandler(request: NextRequest): Promise< await disconnectConnector(connectedAccountId); } - return NextResponse.json({ success: true, message: "Connector disconnected" }, { status: 200, headers }); + return NextResponse.json( + { success: true, message: "Connector disconnected" }, + { status: 200, headers }, + ); } catch (error) { const message = error instanceof Error ? error.message : "Failed to disconnect connector"; return NextResponse.json({ error: message }, { status: 500, headers }); diff --git a/lib/composio/connectors/getConnectors.ts b/lib/composio/connectors/getConnectors.ts index be19f5c0..d89f5211 100644 --- a/lib/composio/connectors/getConnectors.ts +++ b/lib/composio/connectors/getConnectors.ts @@ -15,12 +15,7 @@ export interface ConnectorInfo { * Passed explicitly to composio.create() because session.toolkits() * only returns the first 20 by default. */ -const SUPPORTED_TOOLKITS = [ - "googlesheets", - "googledrive", - "googledocs", - "tiktok", -]; +const SUPPORTED_TOOLKITS = ["googlesheets", "googledrive", "googledocs", "tiktok"]; /** * Options for getting connectors. diff --git a/lib/composio/connectors/validateAuthorizeConnectorRequest.ts b/lib/composio/connectors/validateAuthorizeConnectorRequest.ts index 134cda3c..fa8e39f4 100644 --- a/lib/composio/connectors/validateAuthorizeConnectorRequest.ts +++ b/lib/composio/connectors/validateAuthorizeConnectorRequest.ts @@ -54,7 +54,10 @@ export async function validateAuthorizeConnectorRequest( if (account_id) { const accessResult = await checkAccountAccess(accountId, account_id); if (!accessResult.hasAccess) { - return NextResponse.json({ error: "Access denied to this account" }, { status: 403, headers }); + return NextResponse.json( + { error: "Access denied to this account" }, + { status: 403, headers }, + ); } // Build auth configs for custom OAuth diff --git a/lib/composio/connectors/validateDisconnectConnectorRequest.ts b/lib/composio/connectors/validateDisconnectConnectorRequest.ts index 6716f285..5c39de41 100644 --- a/lib/composio/connectors/validateDisconnectConnectorRequest.ts +++ b/lib/composio/connectors/validateDisconnectConnectorRequest.ts @@ -50,7 +50,10 @@ export async function validateDisconnectConnectorRequest( // Disconnecting for another account - verify access to that account const accessResult = await checkAccountAccess(accountId, account_id); if (!accessResult.hasAccess) { - return NextResponse.json({ error: "Access denied to this account" }, { status: 403, headers }); + return NextResponse.json( + { error: "Access denied to this account" }, + { status: 403, headers }, + ); } } else { // Disconnecting account's own connection - verify ownership diff --git a/lib/composio/connectors/validateGetConnectorsRequest.ts b/lib/composio/connectors/validateGetConnectorsRequest.ts index 916eb315..61fb90a9 100644 --- a/lib/composio/connectors/validateGetConnectorsRequest.ts +++ b/lib/composio/connectors/validateGetConnectorsRequest.ts @@ -51,7 +51,10 @@ export async function validateGetConnectorsRequest( if (account_id) { const accessResult = await checkAccountAccess(accountId, account_id); if (!accessResult.hasAccess) { - return NextResponse.json({ error: "Access denied to this account" }, { status: 403, headers }); + return NextResponse.json( + { error: "Access denied to this account" }, + { status: 403, headers }, + ); } return { accountId: account_id }; diff --git a/lib/composio/getCallbackUrl.ts b/lib/composio/getCallbackUrl.ts index 570c9251..8c83505a 100644 --- a/lib/composio/getCallbackUrl.ts +++ b/lib/composio/getCallbackUrl.ts @@ -19,6 +19,7 @@ interface CallbackOptions { * * @param options.destination - Where to redirect: "chat" or "connectors" * @param options.roomId - For chat destination, the room ID to return to + * @param options * @returns Full callback URL with success indicator */ export function getCallbackUrl(options: CallbackOptions): string { diff --git a/lib/composio/toolRouter/__tests__/createToolRouterSession.test.ts b/lib/composio/toolRouter/__tests__/createToolRouterSession.test.ts index 802de374..2287b91b 100644 --- a/lib/composio/toolRouter/__tests__/createToolRouterSession.test.ts +++ b/lib/composio/toolRouter/__tests__/createToolRouterSession.test.ts @@ -54,7 +54,12 @@ describe("createToolRouterSession", () => { it("should pass artist connections when account has no overlap", async () => { // Account has Google Sheets connected but NOT TikTok vi.mocked(getConnectors).mockResolvedValue([ - { slug: "googlesheets", name: "Google Sheets", isConnected: true, connectedAccountId: "gs-123" }, + { + slug: "googlesheets", + name: "Google Sheets", + isConnected: true, + connectedAccountId: "gs-123", + }, { slug: "tiktok", name: "TikTok", isConnected: false }, ]); diff --git a/lib/composio/toolRouter/__tests__/getComposioTools.test.ts b/lib/composio/toolRouter/__tests__/getComposioTools.test.ts index 594717e5..cb32d4ab 100644 --- a/lib/composio/toolRouter/__tests__/getComposioTools.test.ts +++ b/lib/composio/toolRouter/__tests__/getComposioTools.test.ts @@ -76,7 +76,11 @@ describe("getComposioTools", () => { expect(checkAccountArtistAccess).toHaveBeenCalledWith("account-123", "artist-456"); expect(getArtistConnectionsFromComposio).toHaveBeenCalledWith("artist-456"); - expect(createToolRouterSession).toHaveBeenCalledWith("account-123", "room-789", mockConnections); + expect(createToolRouterSession).toHaveBeenCalledWith( + "account-123", + "room-789", + mockConnections, + ); }); it("should skip artist connections when access is denied", async () => { diff --git a/lib/composio/toolRouter/createToolRouterSession.ts b/lib/composio/toolRouter/createToolRouterSession.ts index 2ecf2ec7..deb86406 100644 --- a/lib/composio/toolRouter/createToolRouterSession.ts +++ b/lib/composio/toolRouter/createToolRouterSession.ts @@ -49,9 +49,7 @@ export async function createToolRouterSession( // Only keep artist connections for toolkits the account doesn't have filteredConnections = Object.fromEntries( - Object.entries(artistConnections).filter( - ([slug]) => !accountConnectedSlugs.has(slug), - ), + Object.entries(artistConnections).filter(([slug]) => !accountConnectedSlugs.has(slug)), ); // If nothing left after filtering, don't pass overrides at all diff --git a/lib/const.ts b/lib/const.ts index a5cccfac..0201f548 100644 --- a/lib/const.ts +++ b/lib/const.ts @@ -22,7 +22,6 @@ export const OUTBOUND_EMAIL_DOMAIN = "@recoupable.com"; export const RECOUP_FROM_EMAIL = `Agent by Recoup `; export const SUPABASE_STORAGE_BUCKET = "user-files"; -export const CREATE_CONTENT_TASK_ID = "create-content"; /** * UUID of the Recoup admin organization. diff --git a/lib/contact/contactTeam.ts b/lib/contact/contactTeam.ts index e10af73c..f6880adb 100644 --- a/lib/contact/contactTeam.ts +++ b/lib/contact/contactTeam.ts @@ -41,4 +41,3 @@ ${message}`; }; } } - diff --git a/lib/credits/__tests__/getCreditUsage.test.ts b/lib/credits/__tests__/getCreditUsage.test.ts index fa55b85d..8aa29190 100644 --- a/lib/credits/__tests__/getCreditUsage.test.ts +++ b/lib/credits/__tests__/getCreditUsage.test.ts @@ -1,12 +1,12 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { getModel } from "@/lib/ai/getModel"; +import { getCreditUsage } from "../getCreditUsage"; + vi.mock("@/lib/ai/getModel", () => ({ getModel: vi.fn(), })); -import { getModel } from "@/lib/ai/getModel"; -import { getCreditUsage } from "../getCreditUsage"; - const mockGetModel = vi.mocked(getModel); describe("getCreditUsage", () => { diff --git a/lib/credits/__tests__/handleChatCredits.test.ts b/lib/credits/__tests__/handleChatCredits.test.ts index 27f6a9a9..500b45bb 100644 --- a/lib/credits/__tests__/handleChatCredits.test.ts +++ b/lib/credits/__tests__/handleChatCredits.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { getCreditUsage } from "@/lib/credits/getCreditUsage"; +import { deductCredits } from "@/lib/credits/deductCredits"; +import { handleChatCredits } from "../handleChatCredits"; + vi.mock("@/lib/credits/getCreditUsage", () => ({ getCreditUsage: vi.fn(), })); @@ -8,10 +12,6 @@ vi.mock("@/lib/credits/deductCredits", () => ({ deductCredits: vi.fn(), })); -import { getCreditUsage } from "@/lib/credits/getCreditUsage"; -import { deductCredits } from "@/lib/credits/deductCredits"; -import { handleChatCredits } from "../handleChatCredits"; - const mockGetCreditUsage = vi.mocked(getCreditUsage); const mockDeductCredits = vi.mocked(deductCredits); @@ -143,10 +143,7 @@ describe("handleChatCredits", () => { accountId: "account-123", }); - expect(consoleSpy).toHaveBeenCalledWith( - "Failed to handle chat credits:", - expect.any(Error), - ); + expect(consoleSpy).toHaveBeenCalledWith("Failed to handle chat credits:", expect.any(Error)); }); }); }); diff --git a/lib/credits/getCreditUsage.ts b/lib/credits/getCreditUsage.ts index 846314c8..a74d104a 100644 --- a/lib/credits/getCreditUsage.ts +++ b/lib/credits/getCreditUsage.ts @@ -3,6 +3,7 @@ import { LanguageModelUsage } from "ai"; /** * Calculates the total spend in USD for a given language model usage. + * * @param usage - The language model usage data * @param modelId - The ID of the model used * @returns The total spend in USD or 0 if calculation fails @@ -20,10 +21,8 @@ export const getCreditUsage = async ( // LanguageModelUsage uses inputTokens/outputTokens (SDK v3) // or promptTokens/completionTokens (SDK v2 compatibility) - const inputTokens = - (usage as any).inputTokens ?? (usage as any).promptTokens; - const outputTokens = - (usage as any).outputTokens ?? (usage as any).completionTokens; + const inputTokens = (usage as any).inputTokens ?? (usage as any).promptTokens; + const outputTokens = (usage as any).outputTokens ?? (usage as any).completionTokens; if (!inputTokens || !outputTokens) { console.error("No tokens found in usage"); diff --git a/lib/credits/handleChatCredits.ts b/lib/credits/handleChatCredits.ts index c0462eab..30587579 100644 --- a/lib/credits/handleChatCredits.ts +++ b/lib/credits/handleChatCredits.ts @@ -11,9 +11,13 @@ interface HandleChatCreditsParams { /** * Handles credit deduction after chat completion. * Calculates usage cost and deducts appropriate credits from the user's account. + * + * @param usage.usage * @param usage - The language model usage data * @param model - The model ID used for the chat * @param accountId - The account ID to deduct credits from (optional) + * @param usage.model + * @param usage.accountId */ export const handleChatCredits = async ({ usage, diff --git a/lib/emails/__tests__/validateInboundEmailEvent.test.ts b/lib/emails/__tests__/validateInboundEmailEvent.test.ts index 803818eb..fa14d42f 100644 --- a/lib/emails/__tests__/validateInboundEmailEvent.test.ts +++ b/lib/emails/__tests__/validateInboundEmailEvent.test.ts @@ -12,8 +12,7 @@ const baseEvent = { to: ["agent@mail.recoupable.com"], bcc: [], cc: [], - message_id: - "", + message_id: "", subject: "Add mp3s to Gatsby Grace files in sandbox", attachments: [], }, diff --git a/lib/emails/inbound/__tests__/extractRoomIdFromHtml.test.ts b/lib/emails/inbound/__tests__/extractRoomIdFromHtml.test.ts index 5fcf968b..d7aebeea 100644 --- a/lib/emails/inbound/__tests__/extractRoomIdFromHtml.test.ts +++ b/lib/emails/inbound/__tests__/extractRoomIdFromHtml.test.ts @@ -104,8 +104,7 @@ describe("extractRoomIdFromHtml", () => { }); it("returns undefined for invalid UUID format in link", () => { - const html = - 'link'; + const html = 'link'; const result = extractRoomIdFromHtml(html); diff --git a/lib/emails/inbound/__tests__/getFromWithName.test.ts b/lib/emails/inbound/__tests__/getFromWithName.test.ts index bb4efd98..3b0c867d 100644 --- a/lib/emails/inbound/__tests__/getFromWithName.test.ts +++ b/lib/emails/inbound/__tests__/getFromWithName.test.ts @@ -34,10 +34,7 @@ describe("getFromWithName", () => { }); it("falls back to cc array when not in to array", () => { - const result = getFromWithName( - ["other@example.com"], - ["support@mail.recoupable.com"], - ); + const result = getFromWithName(["other@example.com"], ["support@mail.recoupable.com"]); expect(result).toBe("Support by Recoup "); }); @@ -66,9 +63,7 @@ describe("getFromWithName", () => { }); it("throws error when arrays are empty", () => { - expect(() => getFromWithName([])).toThrow( - "No email found ending with @mail.recoupable.com", - ); + expect(() => getFromWithName([])).toThrow("No email found ending with @mail.recoupable.com"); }); }); diff --git a/lib/emails/inbound/extractRoomIdFromHtml.ts b/lib/emails/inbound/extractRoomIdFromHtml.ts index f637b17e..6a48f954 100644 --- a/lib/emails/inbound/extractRoomIdFromHtml.ts +++ b/lib/emails/inbound/extractRoomIdFromHtml.ts @@ -10,10 +10,7 @@ const CHAT_LINK_PATTERNS = [ // Pattern to find UUID after /chat/ or %2Fchat%2F in link text that may contain tags // The link text version: "https:///chat.recoupable.com/chat/uuid" -const WBR_STRIPPED_PATTERN = new RegExp( - `chat\\.recoupable\\.com/chat/(${UUID_PATTERN})`, - "i", -); +const WBR_STRIPPED_PATTERN = new RegExp(`chat\\.recoupable\\.com/chat/(${UUID_PATTERN})`, "i"); /** * Extracts the roomId from email HTML by looking for a Recoup chat link. diff --git a/lib/emails/processAndSendEmail.ts b/lib/emails/processAndSendEmail.ts index c70e20d0..f2129676 100644 --- a/lib/emails/processAndSendEmail.ts +++ b/lib/emails/processAndSendEmail.ts @@ -34,6 +34,8 @@ export type ProcessAndSendEmailResult = ProcessAndSendEmailSuccess | ProcessAndS * * Handles room lookup, footer generation, markdown-to-HTML conversion, * and the Resend API call. + * + * @param input */ export async function processAndSendEmail( input: ProcessAndSendEmailInput, @@ -58,7 +60,9 @@ export async function processAndSendEmail( const data = await result.json(); return { success: false, - error: data?.error?.message || `Failed to send email from ${RECOUP_FROM_EMAIL} to ${to.join(", ")}.`, + error: + data?.error?.message || + `Failed to send email from ${RECOUP_FROM_EMAIL} to ${to.join(", ")}.`, }; } diff --git a/lib/evals/callChatFunctions.ts b/lib/evals/callChatFunctions.ts index ef770a2b..23d64a06 100644 --- a/lib/evals/callChatFunctions.ts +++ b/lib/evals/callChatFunctions.ts @@ -6,6 +6,7 @@ import { extractTextFromResult } from "./extractTextFromResult"; * This function encapsulates the logic for calling the chat system * and can be reused across different evaluations. * + * @param input * @deprecated Use callChatFunctionsWithResult for access to tool calls */ export async function callChatFunctions(input: string): Promise { diff --git a/lib/evals/callChatFunctionsWithResult.ts b/lib/evals/callChatFunctionsWithResult.ts index 16330158..b80fcb58 100644 --- a/lib/evals/callChatFunctionsWithResult.ts +++ b/lib/evals/callChatFunctionsWithResult.ts @@ -8,6 +8,8 @@ import { ChatRequestBody } from "@/lib/chat/validateChatRequest"; * * Note: result.toolCalls only contains calls from the LAST step. When using multi-step * tool chains, we need to collect toolCalls from result.steps to capture all tool usage. + * + * @param input */ export async function callChatFunctionsWithResult(input: string) { const messages: UIMessage[] = [ @@ -38,8 +40,7 @@ export async function callChatFunctionsWithResult(input: string) { const result = await agent.generate({ messages: convertedMessages }); // Collect tool calls from ALL steps, not just the last one - const allToolCalls = - result.steps?.flatMap((step) => step.toolCalls || []) || result.toolCalls; + const allToolCalls = result.steps?.flatMap(step => step.toolCalls || []) || result.toolCalls; // Return result with all tool calls from all steps return { diff --git a/lib/evals/createToolsCalledScorer.ts b/lib/evals/createToolsCalledScorer.ts index dff87149..8a9ac7e7 100644 --- a/lib/evals/createToolsCalledScorer.ts +++ b/lib/evals/createToolsCalledScorer.ts @@ -3,16 +3,12 @@ import { ToolsCalled } from "./scorers/ToolsCalled"; /** * Creates a scorer that checks if required tools were called. * Handles extracting output text and toolCalls from the task result. + * + * @param requiredTools + * @param penalizedTools */ -export const createToolsCalledScorer = ( - requiredTools: string[], - penalizedTools: string[] = [] -) => { - return async (args: { - output: unknown; - expected?: string; - input: string; - }) => { +export const createToolsCalledScorer = (requiredTools: string[], penalizedTools: string[] = []) => { + return async (args: { output: unknown; expected?: string; input: string }) => { // Extract output text and toolCalls const outputText = typeof args.output === "object" && args.output && "output" in args.output @@ -20,9 +16,7 @@ export const createToolsCalledScorer = ( : (args.output as string); const toolCalls = - typeof args.output === "object" && - args.output && - "toolCalls" in args.output + typeof args.output === "object" && args.output && "toolCalls" in args.output ? (args.output.toolCalls as Array<{ toolName: string; args: Record; diff --git a/lib/evals/extractTextFromResult.ts b/lib/evals/extractTextFromResult.ts index 09e7a69f..dc67f3ab 100644 --- a/lib/evals/extractTextFromResult.ts +++ b/lib/evals/extractTextFromResult.ts @@ -3,10 +3,10 @@ import { extractTextResultFromSteps } from "./extractTextResultFromSteps"; /** * Extract text from a GenerateTextResult + * + * @param result */ -export function extractTextFromResult( - result: Awaited> -): string { +export function extractTextFromResult(result: Awaited>): string { // Handle multi-step responses (when maxSteps > 1) const stepsText = extractTextResultFromSteps(result); if (stepsText) return stepsText; diff --git a/lib/evals/extractTextResultFromSteps.ts b/lib/evals/extractTextResultFromSteps.ts index 9c4606f0..16881677 100644 --- a/lib/evals/extractTextResultFromSteps.ts +++ b/lib/evals/extractTextResultFromSteps.ts @@ -4,15 +4,13 @@ import type { TextPart } from "ai"; /** * Extract text from multi-step GenerateTextResult * Handles responses where maxSteps > 1 + * + * @param result */ export function extractTextResultFromSteps( - result: Awaited> + result: Awaited>, ): string | null { - if ( - !result.steps || - !Array.isArray(result.steps) || - result.steps.length === 0 - ) { + if (!result.steps || !Array.isArray(result.steps) || result.steps.length === 0) { return null; } @@ -25,10 +23,7 @@ export function extractTextResultFromSteps( const textParts = lastStep.content .filter( (part: unknown): part is TextPart => - typeof part === "object" && - part !== null && - "type" in part && - part.type === "text" + typeof part === "object" && part !== null && "type" in part && part.type === "text", ) .map((part: TextPart) => part.text) .join(""); diff --git a/lib/evals/getCatalogSongsCountExpected.ts b/lib/evals/getCatalogSongsCountExpected.ts index 6f04e59c..d94383ef 100644 --- a/lib/evals/getCatalogSongsCountExpected.ts +++ b/lib/evals/getCatalogSongsCountExpected.ts @@ -2,6 +2,9 @@ import { getCatalogs } from "@/lib/catalog/getCatalogs"; import { getCatalogSongs } from "@/lib/catalog/getCatalogSongs"; import { EVAL_ACCOUNT_ID } from "@/lib/consts"; +/** + * + */ async function getCatalogSongsCountExpected() { try { const catalogsData = await getCatalogs(EVAL_ACCOUNT_ID); diff --git a/lib/evals/getSpotifyFollowersData.ts b/lib/evals/getSpotifyFollowersData.ts index 2edb87cc..6074aceb 100644 --- a/lib/evals/getSpotifyFollowersData.ts +++ b/lib/evals/getSpotifyFollowersData.ts @@ -3,7 +3,7 @@ import { EVAL_ARTISTS } from "@/lib/consts"; const getSpotifyFollowersData = async () => { const testCases = await Promise.all( - EVAL_ARTISTS.map(async (artist) => { + EVAL_ARTISTS.map(async artist => { const { expected } = await getSpotifyFollowersExpected(artist); return { input: `how many total followers does ${artist} have on Spotify`, @@ -15,7 +15,7 @@ const getSpotifyFollowersData = async () => { data_type: "spotify_followers", }, }; - }) + }), ); return testCases; diff --git a/lib/evals/getSpotifyFollowersExpected.ts b/lib/evals/getSpotifyFollowersExpected.ts index ef96e248..f5221937 100644 --- a/lib/evals/getSpotifyFollowersExpected.ts +++ b/lib/evals/getSpotifyFollowersExpected.ts @@ -1,5 +1,9 @@ import { getSpotifyFollowers } from "@/lib/spotify/getSpotifyFollowers"; +/** + * + * @param artist + */ async function getSpotifyFollowersExpected(artist: string) { try { const followerCount = await getSpotifyFollowers(artist); diff --git a/lib/evals/scorers/CatalogAvailability.ts b/lib/evals/scorers/CatalogAvailability.ts index 206053f2..8cf292d9 100644 --- a/lib/evals/scorers/CatalogAvailability.ts +++ b/lib/evals/scorers/CatalogAvailability.ts @@ -5,6 +5,11 @@ import { z } from "zod"; /** * Custom scorer that uses AI to check if recommended songs are actually in the catalog + * + * @param root0 + * @param root0.output + * @param root0.expected + * @param root0.input */ export const CatalogAvailability = async ({ output, @@ -40,29 +45,19 @@ ${catalog} IMPORTANT: Return ONLY valid JSON. Use double quotes for all strings. Escape any quotes within strings with backslashes. Example: "reasoning": "This is a test with \\"quotes\\" inside"`, schema: z.object({ - analysis: z - .string() - .describe("Detailed analysis of the song recommendations"), + analysis: z.string().describe("Detailed analysis of the song recommendations"), totalSongs: z.number().describe("Total number of songs recommended"), - songsInCatalog: z - .number() - .describe("Number of songs that are in the catalog"), + songsInCatalog: z.number().describe("Number of songs that are in the catalog"), matchedSongs: z .array( z.object({ recommended: z.string().describe("The song as recommended"), catalogMatch: z.string().describe("The matching song in catalog"), - confidence: z - .number() - .min(0) - .max(1) - .describe("Confidence in the match (0-1)"), - }) + confidence: z.number().min(0).max(1).describe("Confidence in the match (0-1)"), + }), ) .describe("Songs that were matched to the catalog"), - unmatchedSongs: z - .array(z.string()) - .describe("Songs that were not found in the catalog"), + unmatchedSongs: z.array(z.string()).describe("Songs that were not found in the catalog"), score: z .number() .min(0) @@ -77,9 +72,7 @@ IMPORTANT: Return ONLY valid JSON. Use double quotes for all strings. Escape any } const score = - typeof result.object.score === "number" - ? Math.max(0, Math.min(1, result.object.score)) - : 0; + typeof result.object.score === "number" ? Math.max(0, Math.min(1, result.object.score)) : 0; return { name: "catalog_availability", diff --git a/lib/evals/scorers/QuestionAnswered.ts b/lib/evals/scorers/QuestionAnswered.ts index 787eca4b..a7bafd1d 100644 --- a/lib/evals/scorers/QuestionAnswered.ts +++ b/lib/evals/scorers/QuestionAnswered.ts @@ -5,6 +5,11 @@ import { z } from "zod"; /** * Custom scorer that checks if the AI actually answered the customer's question * with a specific answer, or if it deflected/explained why it couldn't answer + * + * @param root0 + * @param root0.output + * @param root0.expected + * @param root0.input */ export const QuestionAnswered = async ({ output, @@ -44,37 +49,29 @@ Return your evaluation as JSON.`, schema: z.object({ answered: z .boolean() - .describe( - "Did the AI provide the specific information requested in the question?" - ), + .describe("Did the AI provide the specific information requested in the question?"), hasSpecificAnswer: z .boolean() .describe( - "Does the response contain specific data/numbers/facts that directly address the question?" + "Does the response contain specific data/numbers/facts that directly address the question?", ), deflected: z .boolean() .describe( - "Did the AI deflect by explaining why it can't answer or offering alternatives instead of answering?" + "Did the AI deflect by explaining why it can't answer or offering alternatives instead of answering?", ), score: z .number() .min(0) .max(1) .describe( - "Score from 0-1 where 1=fully answered, 0.5-0.9=partially answered, 0-0.4=not answered" + "Score from 0-1 where 1=fully answered, 0.5-0.9=partially answered, 0-0.4=not answered", ), reasoning: z .string() - .describe( - "Detailed explanation of why the response did or did not answer the question" - ), - whatWasAsked: z - .string() - .describe("What specific information did the customer ask for?"), - whatWasProvided: z - .string() - .describe("What information did the AI actually provide?"), + .describe("Detailed explanation of why the response did or did not answer the question"), + whatWasAsked: z.string().describe("What specific information did the customer ask for?"), + whatWasProvided: z.string().describe("What information did the AI actually provide?"), }), }); @@ -83,9 +80,7 @@ Return your evaluation as JSON.`, } const score = - typeof result.object.score === "number" - ? Math.max(0, Math.min(1, result.object.score)) - : 0; + typeof result.object.score === "number" ? Math.max(0, Math.min(1, result.object.score)) : 0; return { name: "question_answered", diff --git a/lib/evals/scorers/ToolsCalled.ts b/lib/evals/scorers/ToolsCalled.ts index bcbc9ef6..6a451100 100644 --- a/lib/evals/scorers/ToolsCalled.ts +++ b/lib/evals/scorers/ToolsCalled.ts @@ -1,5 +1,13 @@ /** * Generic scorer that checks if specific tools were called + * + * @param root0 + * @param root0.output + * @param root0.expected + * @param root0.input + * @param root0.toolCalls + * @param root0.requiredTools + * @param root0.penalizedTools */ export const ToolsCalled = async ({ toolCalls, @@ -14,20 +22,16 @@ export const ToolsCalled = async ({ penalizedTools?: string[]; }) => { try { - const calledTools = toolCalls?.map((tc) => tc.toolName) || []; + const calledTools = toolCalls?.map(tc => tc.toolName) || []; // Check if required tools were called - const calledRequiredTools = requiredTools.filter((requiredTool) => - calledTools.some((tool) => - tool.toLowerCase().includes(requiredTool.toLowerCase()) - ) + const calledRequiredTools = requiredTools.filter(requiredTool => + calledTools.some(tool => tool.toLowerCase().includes(requiredTool.toLowerCase())), ); // Check if penalized tools were called - const calledPenalizedTools = penalizedTools.filter((penalizedTool) => - calledTools.some((tool) => - tool.toLowerCase().includes(penalizedTool.toLowerCase()) - ) + const calledPenalizedTools = penalizedTools.filter(penalizedTool => + calledTools.some(tool => tool.toLowerCase().includes(penalizedTool.toLowerCase())), ); // Calculate score based on required tools @@ -64,9 +68,7 @@ export const ToolsCalled = async ({ calledRequiredTools, penalizedTools, calledPenalizedTools, - missingRequiredTools: requiredTools.filter( - (tool) => !calledRequiredTools.includes(tool) - ), + missingRequiredTools: requiredTools.filter(tool => !calledRequiredTools.includes(tool)), }, }; } catch (error) { diff --git a/lib/files/__tests__/getKnowledgeBaseText.test.ts b/lib/files/__tests__/getKnowledgeBaseText.test.ts index 5dbb9583..e9815e95 100644 --- a/lib/files/__tests__/getKnowledgeBaseText.test.ts +++ b/lib/files/__tests__/getKnowledgeBaseText.test.ts @@ -47,7 +47,9 @@ describe("getKnowledgeBaseText", () => { text: () => Promise.resolve("Plain text content"), } as Response); - const knowledges = [{ name: "notes.txt", url: "https://example.com/notes.txt", type: "text/plain" }]; + const knowledges = [ + { name: "notes.txt", url: "https://example.com/notes.txt", type: "text/plain" }, + ]; const result = await getKnowledgeBaseText(knowledges); expect(result).toContain("Plain text content"); diff --git a/lib/files/generateAndStoreTxtFile.ts b/lib/files/generateAndStoreTxtFile.ts index 331505dd..c5b35cd8 100644 --- a/lib/files/generateAndStoreTxtFile.ts +++ b/lib/files/generateAndStoreTxtFile.ts @@ -65,4 +65,3 @@ export async function generateAndStoreTxtFile(contents: string): Promise file.data && file.mediaType); } - diff --git a/lib/flamingo/__tests__/callFlamingoGenerate.test.ts b/lib/flamingo/__tests__/callFlamingoGenerate.test.ts index db36ec3a..6e9e2da8 100644 --- a/lib/flamingo/__tests__/callFlamingoGenerate.test.ts +++ b/lib/flamingo/__tests__/callFlamingoGenerate.test.ts @@ -87,9 +87,9 @@ describe("callFlamingoGenerate", () => { text: async () => "Service Unavailable", }); - await expect( - callFlamingoGenerate({ prompt: "Describe this." }), - ).rejects.toThrow("Flamingo model returned 503: Service Unavailable"); + await expect(callFlamingoGenerate({ prompt: "Describe this." })).rejects.toThrow( + "Flamingo model returned 503: Service Unavailable", + ); }); it("throws error with fallback message when error text cannot be read", async () => { @@ -101,9 +101,9 @@ describe("callFlamingoGenerate", () => { }, }); - await expect( - callFlamingoGenerate({ prompt: "Describe this." }), - ).rejects.toThrow("Flamingo model returned 500: Unknown error"); + await expect(callFlamingoGenerate({ prompt: "Describe this." })).rejects.toThrow( + "Flamingo model returned 500: Unknown error", + ); }); it("throws when response shape is invalid", async () => { @@ -112,8 +112,8 @@ describe("callFlamingoGenerate", () => { json: async () => ({ response: 42 }), }); - await expect( - callFlamingoGenerate({ prompt: "Describe this." }), - ).rejects.toThrow("Flamingo model returned an unexpected response shape"); + await expect(callFlamingoGenerate({ prompt: "Describe this." })).rejects.toThrow( + "Flamingo model returned an unexpected response shape", + ); }); }); diff --git a/lib/flamingo/__tests__/getFlamingoPresetsHandler.test.ts b/lib/flamingo/__tests__/getFlamingoPresetsHandler.test.ts index 19109b2d..1c30d8fc 100644 --- a/lib/flamingo/__tests__/getFlamingoPresetsHandler.test.ts +++ b/lib/flamingo/__tests__/getFlamingoPresetsHandler.test.ts @@ -17,6 +17,9 @@ vi.mock("../presets", () => ({ getPresetSummaries: vi.fn(), })); +/** + * + */ function createMockRequest(): NextRequest { return { headers: new Headers({ "x-api-key": "test-key" }), diff --git a/lib/flamingo/__tests__/postProcessors.test.ts b/lib/flamingo/__tests__/postProcessors.test.ts index ccbcaf11..cd422235 100644 --- a/lib/flamingo/__tests__/postProcessors.test.ts +++ b/lib/flamingo/__tests__/postProcessors.test.ts @@ -23,9 +23,7 @@ describe("parseJsonLike", () => { }); it("handles nested objects with single quotes", () => { - const result = parseJsonLike( - "{'tags': ['dreamy', 'sad'], 'mood': 'melancholic'}", - ); + const result = parseJsonLike("{'tags': ['dreamy', 'sad'], 'mood': 'melancholic'}"); expect(result).toEqual({ tags: ["dreamy", "sad"], mood: "melancholic" }); }); @@ -46,9 +44,7 @@ describe("condenseRepetitions", () => { }); it("condenses within larger text", () => { - const result = condenseRepetitions( - "Ah, oh, oh, oh, oh, oh, yeah", - ); + const result = condenseRepetitions("Ah, oh, oh, oh, oh, oh, yeah"); expect(result).toBe("Ah, (oh x5), yeah"); }); diff --git a/lib/flamingo/__tests__/presets.test.ts b/lib/flamingo/__tests__/presets.test.ts index 04c903ac..740664a0 100644 --- a/lib/flamingo/__tests__/presets.test.ts +++ b/lib/flamingo/__tests__/presets.test.ts @@ -1,10 +1,5 @@ import { describe, it, expect } from "vitest"; -import { - getPreset, - getAllPresets, - getPresetSummaries, - PRESET_NAMES, -} from "../presets"; +import { getPreset, getAllPresets, getPresetSummaries, PRESET_NAMES } from "../presets"; import { FULL_REPORT_SECTIONS, FULL_REPORT_PRESET_NAME } from "../presets/fullReport"; describe("getPreset", () => { @@ -62,7 +57,7 @@ describe("getPresetSummaries", () => { it("includes full_report in summaries", () => { const summaries = getPresetSummaries(); - const fullReport = summaries.find((s) => s.name === "full_report"); + const fullReport = summaries.find(s => s.name === "full_report"); expect(fullReport).toBeDefined(); expect(fullReport?.label).toBe("Full Report"); }); diff --git a/lib/flamingo/__tests__/processAnalyzeMusicRequest.test.ts b/lib/flamingo/__tests__/processAnalyzeMusicRequest.test.ts index 6cf968a3..9cd72f1b 100644 --- a/lib/flamingo/__tests__/processAnalyzeMusicRequest.test.ts +++ b/lib/flamingo/__tests__/processAnalyzeMusicRequest.test.ts @@ -6,8 +6,7 @@ const mockGetPreset = vi.fn(); const mockExecuteFullReport = vi.fn(); vi.mock("@/lib/flamingo/callFlamingoGenerate", () => ({ - callFlamingoGenerate: (...args: unknown[]) => - mockCallFlamingoGenerate(...args), + callFlamingoGenerate: (...args: unknown[]) => mockCallFlamingoGenerate(...args), })); vi.mock("@/lib/flamingo/presets", () => ({ @@ -77,9 +76,7 @@ describe("processAnalyzeMusicRequest", () => { report: { metadata: { title: "Song" } }, elapsed_seconds: 30.5, }); - expect(mockExecuteFullReport).toHaveBeenCalledWith( - "https://example.com/song.mp3", - ); + expect(mockExecuteFullReport).toHaveBeenCalledWith("https://example.com/song.mp3"); }); it("returns error when full_report preset has no audio_url", async () => { @@ -155,7 +152,7 @@ describe("processAnalyzeMusicRequest", () => { expect(result).toEqual({ type: "error", - error: 'Unknown preset: nonexistent_preset', + error: "Unknown preset: nonexistent_preset", }); }); @@ -215,9 +212,7 @@ describe("processAnalyzeMusicRequest", () => { describe("inference failure", () => { it("throws when callFlamingoGenerate fails", async () => { - mockCallFlamingoGenerate.mockRejectedValue( - new Error("Modal returned 503"), - ); + mockCallFlamingoGenerate.mockRejectedValue(new Error("Modal returned 503")); await expect( processAnalyzeMusicRequest({ diff --git a/lib/flamingo/callFlamingoGenerate.ts b/lib/flamingo/callFlamingoGenerate.ts index 14cd6712..b94ce2ba 100644 --- a/lib/flamingo/callFlamingoGenerate.ts +++ b/lib/flamingo/callFlamingoGenerate.ts @@ -33,9 +33,7 @@ export async function callFlamingoGenerate( if (!response.ok) { const errorText = await response.text().catch(() => "Unknown error"); - throw new Error( - `Flamingo model returned ${response.status}: ${errorText}`, - ); + throw new Error(`Flamingo model returned ${response.status}: ${errorText}`); } const data = await response.json(); diff --git a/lib/flamingo/executeFullReport.ts b/lib/flamingo/executeFullReport.ts index 87635e34..fa5605ce 100644 --- a/lib/flamingo/executeFullReport.ts +++ b/lib/flamingo/executeFullReport.ts @@ -32,46 +32,39 @@ export async function executeFullReport(audioUrl: string): Promise<{ const startTime = Date.now(); // Build all preset calls in parallel - const promises: Promise[] = FULL_REPORT_SECTIONS.map( - async (section) => { - const preset = getPreset(section.preset); - if (!preset) return null; + const promises: Promise[] = FULL_REPORT_SECTIONS.map(async section => { + const preset = getPreset(section.preset); + if (!preset) return null; - try { - const result = await callFlamingoGenerate({ - prompt: preset.prompt, - audio_url: audioUrl, - max_new_tokens: preset.params.max_new_tokens, - temperature: preset.params.temperature, - top_p: 1.0, - do_sample: preset.params.do_sample, - }); + try { + const result = await callFlamingoGenerate({ + prompt: preset.prompt, + audio_url: audioUrl, + max_new_tokens: preset.params.max_new_tokens, + temperature: preset.params.temperature, + top_p: 1.0, + do_sample: preset.params.do_sample, + }); - // Apply post-processing if the preset defines one - const data = preset.parseResponse - ? preset.parseResponse(result.response) - : result.response; + // Apply post-processing if the preset defines one + const data = preset.parseResponse ? preset.parseResponse(result.response) : result.response; - return { - reportKey: section.reportKey, - data, - elapsed_seconds: result.elapsed_seconds, - }; - } catch (error) { - console.error( - `[WARN] Full report section "${section.preset}" failed:`, - error, - ); - return { - reportKey: section.reportKey, - data: { - error: `Section failed: ${error instanceof Error ? error.message : "unknown error"}`, - }, - elapsed_seconds: 0, - }; - } - }, - ); + return { + reportKey: section.reportKey, + data, + elapsed_seconds: result.elapsed_seconds, + }; + } catch (error) { + console.error(`[WARN] Full report section "${section.preset}" failed:`, error); + return { + reportKey: section.reportKey, + data: { + error: `Section failed: ${error instanceof Error ? error.message : "unknown error"}`, + }, + elapsed_seconds: 0, + }; + } + }); // Execute all presets in parallel const results = await Promise.all(promises); @@ -84,7 +77,7 @@ export async function executeFullReport(audioUrl: string): Promise<{ } } - const totalElapsed = Math.round((Date.now() - startTime) / 1000 * 100) / 100; + const totalElapsed = Math.round(((Date.now() - startTime) / 1000) * 100) / 100; return { report, elapsed_seconds: totalElapsed }; } diff --git a/lib/flamingo/getFlamingoPresetsHandler.ts b/lib/flamingo/getFlamingoPresetsHandler.ts index 1ab0f8de..f33d491d 100644 --- a/lib/flamingo/getFlamingoPresetsHandler.ts +++ b/lib/flamingo/getFlamingoPresetsHandler.ts @@ -10,11 +10,10 @@ import { validateAuthContext } from "@/lib/auth/validateAuthContext"; * Returns a list of all available analysis presets. * Requires authentication via x-api-key header or Authorization bearer token. * + * @param request * @returns A NextResponse with the list of available presets. */ -export async function getFlamingoPresetsHandler( - request: NextRequest, -): Promise { +export async function getFlamingoPresetsHandler(request: NextRequest): Promise { const authResult = await validateAuthContext(request); if (authResult instanceof NextResponse) { return authResult; diff --git a/lib/flamingo/isFlamingoGenerateResult.ts b/lib/flamingo/isFlamingoGenerateResult.ts index a3f87c0a..288acdd0 100644 --- a/lib/flamingo/isFlamingoGenerateResult.ts +++ b/lib/flamingo/isFlamingoGenerateResult.ts @@ -14,14 +14,8 @@ export interface FlamingoGenerateResult { * @param value - Unknown parsed JSON payload * @returns True when payload has the expected response and elapsed_seconds fields */ -export function isFlamingoGenerateResult( - value: unknown, -): value is FlamingoGenerateResult { +export function isFlamingoGenerateResult(value: unknown): value is FlamingoGenerateResult { if (!value || typeof value !== "object") return false; const candidate = value as Record; - return ( - typeof candidate.response === "string" && - typeof candidate.elapsed_seconds === "number" - ); + return typeof candidate.response === "string" && typeof candidate.elapsed_seconds === "number"; } - diff --git a/lib/flamingo/postFlamingoGenerateHandler.ts b/lib/flamingo/postFlamingoGenerateHandler.ts index 11dd6219..bb3c1ef9 100644 --- a/lib/flamingo/postFlamingoGenerateHandler.ts +++ b/lib/flamingo/postFlamingoGenerateHandler.ts @@ -14,9 +14,7 @@ import { processAnalyzeMusicRequest } from "@/lib/flamingo/processAnalyzeMusicRe * @param request - The incoming request with a JSON body. * @returns A NextResponse with the model output or an error. */ -export async function postFlamingoGenerateHandler( - request: NextRequest, -): Promise { +export async function postFlamingoGenerateHandler(request: NextRequest): Promise { let body: unknown; try { body = await request.json(); @@ -44,8 +42,7 @@ export async function postFlamingoGenerateHandler( try { result = await processAnalyzeMusicRequest(validated); } catch (err) { - const message = - err instanceof Error ? err.message : "Flamingo inference failed"; + const message = err instanceof Error ? err.message : "Flamingo inference failed"; return NextResponse.json( { status: "error", error: message }, { status: 500, headers: getCorsHeaders() }, diff --git a/lib/flamingo/presets/audienceProfile.ts b/lib/flamingo/presets/audienceProfile.ts index 54b1f3d6..125738a7 100644 --- a/lib/flamingo/presets/audienceProfile.ts +++ b/lib/flamingo/presets/audienceProfile.ts @@ -11,5 +11,5 @@ export const audienceProfilePreset: PresetConfig = { params: { max_new_tokens: 1024, temperature: 0.5, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => parseJsonLike(raw), + parseResponse: raw => parseJsonLike(raw), }; diff --git a/lib/flamingo/presets/catalogMetadata.ts b/lib/flamingo/presets/catalogMetadata.ts index 600d35a4..0bcc4b2c 100644 --- a/lib/flamingo/presets/catalogMetadata.ts +++ b/lib/flamingo/presets/catalogMetadata.ts @@ -11,5 +11,5 @@ export const catalogMetadataPreset: PresetConfig = { params: { max_new_tokens: 1024, temperature: 0.3, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => parseJsonLike(raw), + parseResponse: raw => parseJsonLike(raw), }; diff --git a/lib/flamingo/presets/condenseRepetitions.ts b/lib/flamingo/presets/condenseRepetitions.ts index 5011cfbd..cd851e4d 100644 --- a/lib/flamingo/presets/condenseRepetitions.ts +++ b/lib/flamingo/presets/condenseRepetitions.ts @@ -9,10 +9,7 @@ * @param minRepeats - Minimum repetitions to trigger condensing (default 3) * @returns Text with repetitions condensed */ -export function condenseRepetitions( - text: string, - minRepeats: number = 3, -): string { +export function condenseRepetitions(text: string, minRepeats: number = 3): string { // Split on comma+space boundaries const tokens = text.split(/,\s*/); const result: string[] = []; @@ -39,4 +36,3 @@ export function condenseRepetitions( return result.join(", "); } - diff --git a/lib/flamingo/presets/contentAdvisory.ts b/lib/flamingo/presets/contentAdvisory.ts index 60aa8e90..2e2a83f1 100644 --- a/lib/flamingo/presets/contentAdvisory.ts +++ b/lib/flamingo/presets/contentAdvisory.ts @@ -11,5 +11,5 @@ export const contentAdvisoryPreset: PresetConfig = { params: { max_new_tokens: 256, temperature: 0.2, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => parseJsonLike(raw), + parseResponse: raw => parseJsonLike(raw), }; diff --git a/lib/flamingo/presets/deduplicateArray.ts b/lib/flamingo/presets/deduplicateArray.ts index 7d57ad40..f8a2785f 100644 --- a/lib/flamingo/presets/deduplicateArray.ts +++ b/lib/flamingo/presets/deduplicateArray.ts @@ -7,4 +7,3 @@ export function deduplicateArray(items: T[]): T[] { return [...new Set(items)]; } - diff --git a/lib/flamingo/presets/extractOneCycle.ts b/lib/flamingo/presets/extractOneCycle.ts index c01d3ad6..68ebb11a 100644 --- a/lib/flamingo/presets/extractOneCycle.ts +++ b/lib/flamingo/presets/extractOneCycle.ts @@ -27,4 +27,3 @@ export function extractOneCycle(items: T[]): T[] { return items; } - diff --git a/lib/flamingo/presets/getAllPresets.ts b/lib/flamingo/presets/getAllPresets.ts index f9012f2f..511c6f69 100644 --- a/lib/flamingo/presets/getAllPresets.ts +++ b/lib/flamingo/presets/getAllPresets.ts @@ -10,4 +10,3 @@ import { PRESETS } from "./presetRegistry"; export function getAllPresets(): PresetConfig[] { return Object.values(PRESETS); } - diff --git a/lib/flamingo/presets/getPreset.ts b/lib/flamingo/presets/getPreset.ts index 044055af..5ef73d45 100644 --- a/lib/flamingo/presets/getPreset.ts +++ b/lib/flamingo/presets/getPreset.ts @@ -10,4 +10,3 @@ import { PRESETS } from "./presetRegistry"; export function getPreset(name: string): PresetConfig | undefined { return PRESETS[name]; } - diff --git a/lib/flamingo/presets/getPresetSummaries.ts b/lib/flamingo/presets/getPresetSummaries.ts index d43077df..f47cb5cb 100644 --- a/lib/flamingo/presets/getPresetSummaries.ts +++ b/lib/flamingo/presets/getPresetSummaries.ts @@ -15,7 +15,7 @@ interface PresetSummary { * @returns Array of preset summaries (name, label, description, requiresAudio, responseFormat) */ export function getPresetSummaries(): PresetSummary[] { - const individual = Object.values(PRESETS).map((p) => ({ + const individual = Object.values(PRESETS).map(p => ({ name: p.name, label: p.label, description: p.description, @@ -35,4 +35,3 @@ export function getPresetSummaries(): PresetSummary[] { }, ]; } - diff --git a/lib/flamingo/presets/lyricTranscription.ts b/lib/flamingo/presets/lyricTranscription.ts index 01ff0f69..629c6dd1 100644 --- a/lib/flamingo/presets/lyricTranscription.ts +++ b/lib/flamingo/presets/lyricTranscription.ts @@ -5,11 +5,10 @@ import { condenseRepetitions } from "./postProcessors"; export const lyricTranscriptionPreset: PresetConfig = { name: "lyric_transcription", label: "Lyric Transcription", - description: - "Transcribes complete lyrics with section headers (Verse, Chorus, Bridge, etc).", + description: "Transcribes complete lyrics with section headers (Verse, Chorus, Bridge, etc).", prompt: `Transcribe the complete lyrics of this song. Format with section headers in brackets like [Verse 1], [Chorus], [Bridge], [Outro]. Include every distinct lyric line. Break lines where the singer naturally pauses.`, params: { max_new_tokens: 2048, temperature: 0.1, do_sample: true }, requiresAudio: true, responseFormat: "text", - parseResponse: (raw) => condenseRepetitions(raw), + parseResponse: raw => condenseRepetitions(raw), }; diff --git a/lib/flamingo/presets/moodTags.ts b/lib/flamingo/presets/moodTags.ts index 555ab5ef..5cf6129d 100644 --- a/lib/flamingo/presets/moodTags.ts +++ b/lib/flamingo/presets/moodTags.ts @@ -5,11 +5,10 @@ import { parseJsonLike } from "./postProcessors"; export const moodTagsPreset: PresetConfig = { name: "mood_tags", label: "Mood Tags", - description: - "Returns mood/vibe/energy tags for playlist curation and music discovery.", + description: "Returns mood/vibe/energy tags for playlist curation and music discovery.", prompt: `Listen to this track and return ONLY a valid JSON object (use double quotes) with two fields: "tags" (array of 8-10 mood/vibe/energy tags that could be used for playlist curation and music discovery, e.g. "dreamy", "late-night", "heartbreak", "chill"), and "primary_mood" (single word that best captures the overall feeling). No other text.`, params: { max_new_tokens: 256, temperature: 0.3, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => parseJsonLike(raw), + parseResponse: raw => parseJsonLike(raw), }; diff --git a/lib/flamingo/presets/musicTheory.ts b/lib/flamingo/presets/musicTheory.ts index 7e5d5e46..cbaf4711 100644 --- a/lib/flamingo/presets/musicTheory.ts +++ b/lib/flamingo/presets/musicTheory.ts @@ -11,13 +11,11 @@ export const musicTheoryPreset: PresetConfig = { params: { max_new_tokens: 512, temperature: 0.3, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => { + parseResponse: raw => { const parsed = parseJsonLike(raw) as Record; // Extract one cycle from potentially repeated chord progression if (Array.isArray(parsed.chord_progression)) { - parsed.chord_progression = extractOneCycle( - parsed.chord_progression as string[], - ); + parsed.chord_progression = extractOneCycle(parsed.chord_progression as string[]); } return parsed; }, diff --git a/lib/flamingo/presets/parseJsonLike.ts b/lib/flamingo/presets/parseJsonLike.ts index 638d715c..fec3169e 100644 --- a/lib/flamingo/presets/parseJsonLike.ts +++ b/lib/flamingo/presets/parseJsonLike.ts @@ -27,4 +27,3 @@ export function parseJsonLike(raw: string): unknown { return JSON.parse(fixed); } - diff --git a/lib/flamingo/presets/presetRegistry.ts b/lib/flamingo/presets/presetRegistry.ts index 9de07509..062890bb 100644 --- a/lib/flamingo/presets/presetRegistry.ts +++ b/lib/flamingo/presets/presetRegistry.ts @@ -35,8 +35,4 @@ export const PRESETS: Record = { * All valid preset names (including full_report). * Used for Zod enum validation in the request schema. */ -export const PRESET_NAMES = [ - ...Object.keys(PRESETS), - FULL_REPORT_PRESET_NAME, -] as const; - +export const PRESET_NAMES = [...Object.keys(PRESETS), FULL_REPORT_PRESET_NAME] as const; diff --git a/lib/flamingo/presets/similarArtists.ts b/lib/flamingo/presets/similarArtists.ts index 62214ed7..92c02933 100644 --- a/lib/flamingo/presets/similarArtists.ts +++ b/lib/flamingo/presets/similarArtists.ts @@ -11,5 +11,5 @@ export const similarArtistsPreset: PresetConfig = { params: { max_new_tokens: 512, temperature: 0.5, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => parseJsonLike(raw), + parseResponse: raw => parseJsonLike(raw), }; diff --git a/lib/flamingo/presets/syncBriefMatch.ts b/lib/flamingo/presets/syncBriefMatch.ts index 59c2bfda..92075548 100644 --- a/lib/flamingo/presets/syncBriefMatch.ts +++ b/lib/flamingo/presets/syncBriefMatch.ts @@ -11,5 +11,5 @@ export const syncBriefMatchPreset: PresetConfig = { params: { max_new_tokens: 1024, temperature: 0.4, do_sample: true }, requiresAudio: true, responseFormat: "json", - parseResponse: (raw) => parseJsonLike(raw), + parseResponse: raw => parseJsonLike(raw), }; diff --git a/lib/flamingo/processAnalyzeMusicRequest.ts b/lib/flamingo/processAnalyzeMusicRequest.ts index 263c4e74..cd89a479 100644 --- a/lib/flamingo/processAnalyzeMusicRequest.ts +++ b/lib/flamingo/processAnalyzeMusicRequest.ts @@ -26,10 +26,7 @@ interface AnalysisError { error: string; } -export type AnalyzeMusicResult = - | FullReportSuccess - | AnalysisSuccess - | AnalysisError; +export type AnalyzeMusicResult = FullReportSuccess | AnalysisSuccess | AnalysisError; /** * Shared business logic for music analysis. @@ -49,9 +46,7 @@ export async function processAnalyzeMusicRequest( error: "audio_url is required for the full_report preset", }; } - const { report, elapsed_seconds } = await executeFullReport( - params.audio_url, - ); + const { report, elapsed_seconds } = await executeFullReport(params.audio_url); return { type: "success", preset: "full_report", report, elapsed_seconds }; } diff --git a/lib/flamingo/validateFlamingoGenerateBody.ts b/lib/flamingo/validateFlamingoGenerateBody.ts index 691c66f0..e158e650 100644 --- a/lib/flamingo/validateFlamingoGenerateBody.ts +++ b/lib/flamingo/validateFlamingoGenerateBody.ts @@ -23,11 +23,11 @@ export const flamingoGenerateBodySchema = z top_p: z.number().min(0).max(1).optional().default(1.0), do_sample: z.boolean().optional().default(false), }) - .refine((data) => data.preset || data.prompt, { + .refine(data => data.preset || data.prompt, { message: "Either 'preset' or 'prompt' is required", path: ["preset"], }) - .refine((data) => !(data.preset && data.prompt), { + .refine(data => !(data.preset && data.prompt), { message: "Provide either 'preset' or 'prompt', not both", path: ["prompt"], }); @@ -41,9 +41,7 @@ export type FlamingoGenerateBody = z.infer; * @param body - The raw request body (parsed JSON). * @returns A NextResponse with an error if validation fails, or the validated body if it passes. */ -export function validateFlamingoGenerateBody( - body: unknown, -): NextResponse | FlamingoGenerateBody { +export function validateFlamingoGenerateBody(body: unknown): NextResponse | FlamingoGenerateBody { const result = flamingoGenerateBodySchema.safeParse(body); if (!result.success) { diff --git a/lib/github/__tests__/deleteAccountGithubRepos.test.ts b/lib/github/__tests__/deleteAccountGithubRepos.test.ts index 39af4ac1..2043b092 100644 --- a/lib/github/__tests__/deleteAccountGithubRepos.test.ts +++ b/lib/github/__tests__/deleteAccountGithubRepos.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { deleteAccountGithubRepos } from "../deleteAccountGithubRepos"; +import { deleteGithubRepo } from "../deleteGithubRepo"; +import { findOrgReposByAccountId } from "../findOrgReposByAccountId"; + vi.mock("@/lib/github/deleteGithubRepo", () => ({ deleteGithubRepo: vi.fn(), })); @@ -8,10 +12,6 @@ vi.mock("@/lib/github/findOrgReposByAccountId", () => ({ findOrgReposByAccountId: vi.fn(), })); -import { deleteAccountGithubRepos } from "../deleteAccountGithubRepos"; -import { deleteGithubRepo } from "../deleteGithubRepo"; -import { findOrgReposByAccountId } from "../findOrgReposByAccountId"; - describe("deleteAccountGithubRepos", () => { const mockAccountId = "550e8400-e29b-41d4-a716-446655440000"; const mockRepoUrl = "https://github.com/recoupable/test-repo"; diff --git a/lib/github/__tests__/findOrgReposByAccountId.test.ts b/lib/github/__tests__/findOrgReposByAccountId.test.ts index 4b3d8b4f..3d36e26a 100644 --- a/lib/github/__tests__/findOrgReposByAccountId.test.ts +++ b/lib/github/__tests__/findOrgReposByAccountId.test.ts @@ -15,7 +15,11 @@ describe("findOrgReposByAccountId", () => { vi.mocked(global.fetch).mockResolvedValue({ ok: true, json: async () => [ - { name: "artist-name-550e8400-e29b-41d4-a716-446655440000", html_url: "https://github.com/recoupable/artist-name-550e8400-e29b-41d4-a716-446655440000" }, + { + name: "artist-name-550e8400-e29b-41d4-a716-446655440000", + html_url: + "https://github.com/recoupable/artist-name-550e8400-e29b-41d4-a716-446655440000", + }, { name: "other-repo", html_url: "https://github.com/recoupable/other-repo" }, ], } as Response); diff --git a/lib/github/__tests__/getRepoGitModules.test.ts b/lib/github/__tests__/getRepoGitModules.test.ts index 6cc87f95..8000a0c9 100644 --- a/lib/github/__tests__/getRepoGitModules.test.ts +++ b/lib/github/__tests__/getRepoGitModules.test.ts @@ -25,10 +25,8 @@ describe("getRepoGitModules", () => { expect(result).toEqual([{ path: "orgs/my-org", url: "https://github.com/recoupable/org-abc" }]); expect(fetch).toHaveBeenCalledWith( - "https://api.github.com/repos/owner/repo/contents/.gitmodules?ref=main", - expect.objectContaining({ - headers: expect.objectContaining({ Authorization: "Bearer test-token" }), - }), + "https://raw.githubusercontent.com/owner/repo/main/.gitmodules", + expect.objectContaining({ headers: { Authorization: "Bearer test-token" } }), ); }); @@ -43,20 +41,17 @@ describe("getRepoGitModules", () => { it("works without GITHUB_TOKEN", async () => { delete process.env.GITHUB_TOKEN; vi.spyOn(global, "fetch").mockResolvedValueOnce( - new Response( - `[submodule "sub"]\n\tpath = sub\n\turl = https://github.com/owner/sub`, - { status: 200 }, - ), + new Response(`[submodule "sub"]\n\tpath = sub\n\turl = https://github.com/owner/sub`, { + status: 200, + }), ); const result = await getRepoGitModules({ owner: "owner", repo: "repo", branch: "develop" }); expect(result).toEqual([{ path: "sub", url: "https://github.com/owner/sub" }]); expect(fetch).toHaveBeenCalledWith( - "https://api.github.com/repos/owner/repo/contents/.gitmodules?ref=develop", - expect.objectContaining({ - headers: expect.objectContaining({ Accept: "application/vnd.github.v3.raw" }), - }), + "https://raw.githubusercontent.com/owner/repo/develop/.gitmodules", + {}, ); }); }); diff --git a/lib/github/expandSubmoduleEntries.ts b/lib/github/expandSubmoduleEntries.ts index 9531bee1..3082c63b 100644 --- a/lib/github/expandSubmoduleEntries.ts +++ b/lib/github/expandSubmoduleEntries.ts @@ -11,9 +11,15 @@ interface SubmoduleRef { * Resolves submodule URLs from .gitmodules, fetches each submodule's tree, * and merges the results into the regular entries with correct path prefixes. * + * @param regularEntries.regularEntries * @param regularEntries - Non-submodule file tree entries * @param submoduleEntries - Submodule references (type "commit" from GitHub Trees API) * @param repo - Repository context for fetching .gitmodules + * @param regularEntries.submoduleEntries + * @param regularEntries.repo + * @param regularEntries.repo.owner + * @param regularEntries.repo.repo + * @param regularEntries.repo.branch * @returns Combined file tree entries with submodules expanded as directories */ export async function expandSubmoduleEntries({ diff --git a/lib/github/findOrgReposByAccountId.ts b/lib/github/findOrgReposByAccountId.ts index 18a50e7b..be37bba6 100644 --- a/lib/github/findOrgReposByAccountId.ts +++ b/lib/github/findOrgReposByAccountId.ts @@ -13,16 +13,13 @@ export async function findOrgReposByAccountId(accountId: string): Promise; - return repos - .filter((repo) => repo.name.includes(accountId)) - .map((repo) => repo.html_url); + return repos.filter(repo => repo.name.includes(accountId)).map(repo => repo.html_url); } catch (error) { console.error("Error searching org repos:", error); return []; diff --git a/lib/github/getRepoFileTree.ts b/lib/github/getRepoFileTree.ts index bceeaae5..6ce2c94b 100644 --- a/lib/github/getRepoFileTree.ts +++ b/lib/github/getRepoFileTree.ts @@ -40,7 +40,7 @@ export async function getRepoFileTree(githubRepoUrl: string): Promise ({ experimental_createMCPClient: vi.fn(), })); @@ -8,9 +11,6 @@ vi.mock("@/lib/networking/getBaseUrl", () => ({ getBaseUrl: vi.fn().mockReturnValue("https://test.vercel.app"), })); -import { getMcpTools } from "../getMcpTools"; -import { experimental_createMCPClient } from "@ai-sdk/mcp"; - const mockCreateMCPClient = vi.mocked(experimental_createMCPClient); describe("getMcpTools", () => { diff --git a/lib/mcp/__tests__/verifyApiKey.test.ts b/lib/mcp/__tests__/verifyApiKey.test.ts index 4b244d2c..137250ca 100644 --- a/lib/mcp/__tests__/verifyApiKey.test.ts +++ b/lib/mcp/__tests__/verifyApiKey.test.ts @@ -1,6 +1,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { verifyBearerToken } from "../verifyApiKey"; +import { getAccountIdByAuthToken } from "@/lib/privy/getAccountIdByAuthToken"; +import { getApiKeyDetails } from "@/lib/keys/getApiKeyDetails"; + vi.mock("@/lib/privy/getAccountIdByAuthToken", () => ({ getAccountIdByAuthToken: vi.fn(), })); @@ -9,9 +12,6 @@ vi.mock("@/lib/keys/getApiKeyDetails", () => ({ getApiKeyDetails: vi.fn(), })); -import { getAccountIdByAuthToken } from "@/lib/privy/getAccountIdByAuthToken"; -import { getApiKeyDetails } from "@/lib/keys/getApiKeyDetails"; - describe("verifyBearerToken", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/mcp/resolveAccountId.ts b/lib/mcp/resolveAccountId.ts index 100dcafd..1e0e7bcc 100644 --- a/lib/mcp/resolveAccountId.ts +++ b/lib/mcp/resolveAccountId.ts @@ -16,6 +16,8 @@ export interface ResolveAccountIdResult { * Validates access when an org API key attempts to use an account_id override. * * @param params - The auth info and optional account_id override. + * @param params.authInfo + * @param params.accountIdOverride * @returns The resolved accountId or an error message. */ export async function resolveAccountId({ diff --git a/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts b/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts index 1d880847..0da1be9e 100644 --- a/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts +++ b/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts @@ -37,7 +37,8 @@ describe("registerSendEmailTool", () => { it("returns success when email is sent successfully", async () => { mockProcessAndSendEmail.mockResolvedValue({ success: true, - message: "Email sent successfully from Agent by Recoup to test@example.com. CC: none.", + message: + "Email sent successfully from Agent by Recoup to test@example.com. CC: none.", id: "email-123", }); diff --git a/lib/mcp/tools/__tests__/registerWebDeepResearchTool.test.ts b/lib/mcp/tools/__tests__/registerWebDeepResearchTool.test.ts index 64ee2767..2474e309 100644 --- a/lib/mcp/tools/__tests__/registerWebDeepResearchTool.test.ts +++ b/lib/mcp/tools/__tests__/registerWebDeepResearchTool.test.ts @@ -115,10 +115,7 @@ describe("registerWebDeepResearchTool", () => { messages: [{ role: "user", content: "Research this topic" }], }); - expect(mockChatWithPerplexity).toHaveBeenCalledWith( - expect.any(Array), - "sonar-deep-research", - ); + expect(mockChatWithPerplexity).toHaveBeenCalledWith(expect.any(Array), "sonar-deep-research"); }); it("handles no citations gracefully", async () => { diff --git a/lib/mcp/tools/artistSocials/registerGetArtistSocialsTool.ts b/lib/mcp/tools/artistSocials/registerGetArtistSocialsTool.ts index 54d216ea..4fe6b10c 100644 --- a/lib/mcp/tools/artistSocials/registerGetArtistSocialsTool.ts +++ b/lib/mcp/tools/artistSocials/registerGetArtistSocialsTool.ts @@ -26,4 +26,3 @@ export function registerGetArtistSocialsTool(server: McpServer): void { }, ); } - diff --git a/lib/mcp/tools/artistSocials/registerUpdateArtistSocialsTool.ts b/lib/mcp/tools/artistSocials/registerUpdateArtistSocialsTool.ts index 898e1dc5..864b0f68 100644 --- a/lib/mcp/tools/artistSocials/registerUpdateArtistSocialsTool.ts +++ b/lib/mcp/tools/artistSocials/registerUpdateArtistSocialsTool.ts @@ -55,12 +55,9 @@ export function registerUpdateArtistSocialsTool(server: McpServer): void { } catch (error) { console.error("Error updating artist socials:", error); const errorMessage = - error instanceof Error - ? error.message - : "Failed to update artist socials."; + error instanceof Error ? error.message : "Failed to update artist socials."; return getToolResultError(errorMessage); } }, ); } - diff --git a/lib/mcp/tools/artists/__tests__/registerCreateNewArtistTool.test.ts b/lib/mcp/tools/artists/__tests__/registerCreateNewArtistTool.test.ts index 42056f75..d438bdd3 100644 --- a/lib/mcp/tools/artists/__tests__/registerCreateNewArtistTool.test.ts +++ b/lib/mcp/tools/artists/__tests__/registerCreateNewArtistTool.test.ts @@ -3,6 +3,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; import type { ServerRequest, ServerNotification } from "@modelcontextprotocol/sdk/types.js"; +import { registerCreateNewArtistTool } from "../registerCreateNewArtistTool"; + const mockCreateArtistInDb = vi.fn(); const mockCopyRoom = vi.fn(); const mockCanAccessAccount = vi.fn(); @@ -19,12 +21,14 @@ vi.mock("@/lib/organizations/canAccessAccount", () => ({ canAccessAccount: (...args: unknown[]) => mockCanAccessAccount(...args), })); -import { registerCreateNewArtistTool } from "../registerCreateNewArtistTool"; - type ServerRequestHandlerExtra = RequestHandlerExtra; /** * Creates a mock extra object with optional authInfo. + * + * @param authInfo + * @param authInfo.accountId + * @param authInfo.orgId */ function createMockExtra(authInfo?: { accountId?: string; diff --git a/lib/mcp/tools/artists/registerCreateNewArtistTool.ts b/lib/mcp/tools/artists/registerCreateNewArtistTool.ts index cad17806..7495bcbf 100644 --- a/lib/mcp/tools/artists/registerCreateNewArtistTool.ts +++ b/lib/mcp/tools/artists/registerCreateNewArtistTool.ts @@ -4,10 +4,7 @@ import type { ServerRequest, ServerNotification } from "@modelcontextprotocol/sd import { z } from "zod"; import type { McpAuthInfo } from "@/lib/mcp/verifyApiKey"; import { resolveAccountId } from "@/lib/mcp/resolveAccountId"; -import { - createArtistInDb, - type CreateArtistResult, -} from "@/lib/artists/createArtistInDb"; +import { createArtistInDb, type CreateArtistResult } from "@/lib/artists/createArtistInDb"; import { copyRoom } from "@/lib/rooms/copyRoom"; import { getToolResultSuccess } from "@/lib/mcp/getToolResultSuccess"; import { getToolResultError } from "@/lib/mcp/getToolResultError"; @@ -69,7 +66,10 @@ export function registerCreateNewArtistTool(server: McpServer): void { "The organization_id parameter is optional — use the organization_id from the system prompt context to link the artist to the user's selected organization.", inputSchema: createNewArtistSchema, }, - async (args: CreateNewArtistArgs, extra: RequestHandlerExtra) => { + async ( + args: CreateNewArtistArgs, + extra: RequestHandlerExtra, + ) => { try { const { name, account_id, active_conversation_id, organization_id } = args; diff --git a/lib/mcp/tools/flamingo/registerAnalyzeMusicTool.ts b/lib/mcp/tools/flamingo/registerAnalyzeMusicTool.ts index 08936e50..d4a0d84d 100644 --- a/lib/mcp/tools/flamingo/registerAnalyzeMusicTool.ts +++ b/lib/mcp/tools/flamingo/registerAnalyzeMusicTool.ts @@ -50,8 +50,7 @@ export function registerAnalyzeMusicTool(server: McpServer): void { try { result = await processAnalyzeMusicRequest(args); } catch (err) { - const message = - err instanceof Error ? err.message : "Flamingo inference failed"; + const message = err instanceof Error ? err.message : "Flamingo inference failed"; return getToolResultError(`Music analysis failed: ${message}`); } diff --git a/lib/mcp/tools/registerWebDeepResearchTool.ts b/lib/mcp/tools/registerWebDeepResearchTool.ts index df9f5d8e..ae84f499 100644 --- a/lib/mcp/tools/registerWebDeepResearchTool.ts +++ b/lib/mcp/tools/registerWebDeepResearchTool.ts @@ -58,7 +58,9 @@ export function registerWebDeepResearchTool(server: McpServer): void { return getToolResultSuccess(finalContent); } catch (error) { return getToolResultError( - error instanceof Error ? `Deep research failed: ${error.message}` : "Deep research failed", + error instanceof Error + ? `Deep research failed: ${error.message}` + : "Deep research failed", ); } }, diff --git a/lib/mcp/tools/sandbox/__tests__/registerPromptSandboxTool.test.ts b/lib/mcp/tools/sandbox/__tests__/registerPromptSandboxTool.test.ts index 1b344d25..054b0807 100644 --- a/lib/mcp/tools/sandbox/__tests__/registerPromptSandboxTool.test.ts +++ b/lib/mcp/tools/sandbox/__tests__/registerPromptSandboxTool.test.ts @@ -73,13 +73,11 @@ describe("registerPromptSandboxTool", () => { it("returns error when resolveAccountId returns an error", async () => { mockResolveAccountId.mockResolvedValue({ accountId: null, - error: "Authentication required. Provide an API key via Authorization: Bearer header, or provide account_id from the system prompt context.", + error: + "Authentication required. Provide an API key via Authorization: Bearer header, or provide account_id from the system prompt context.", }); - const result = await registeredHandler( - { prompt: "say hello" }, - createMockExtra(), - ); + const result = await registeredHandler({ prompt: "say hello" }, createMockExtra()); expect(result).toEqual({ content: [ @@ -97,10 +95,7 @@ describe("registerPromptSandboxTool", () => { error: null, }); - const result = await registeredHandler( - { prompt: "say hello" }, - createMockExtra(), - ); + const result = await registeredHandler({ prompt: "say hello" }, createMockExtra()); expect(result).toEqual({ content: [ diff --git a/lib/mcp/tools/search/registerSearchGoogleImagesTool.ts b/lib/mcp/tools/search/registerSearchGoogleImagesTool.ts index 2853f500..d1e18d6e 100644 --- a/lib/mcp/tools/search/registerSearchGoogleImagesTool.ts +++ b/lib/mcp/tools/search/registerSearchGoogleImagesTool.ts @@ -21,15 +21,21 @@ const searchGoogleImagesSchema = z.object({ imageSize: z .enum(["l", "m", "i"]) .optional() - .describe("Image size: 'l' (large, recommended), 'm' (medium), 'i' (icon/small). Leave unset if unsure."), + .describe( + "Image size: 'l' (large, recommended), 'm' (medium), 'i' (icon/small). Leave unset if unsure.", + ), imageType: z .enum(["photo", "clipart", "lineart", "animated"]) .optional() - .describe("Type of image: 'photo' (default, recommended), 'clipart', 'lineart', 'animated'. Leave unset if unsure."), + .describe( + "Type of image: 'photo' (default, recommended), 'clipart', 'lineart', 'animated'. Leave unset if unsure.", + ), aspectRatio: z .enum(["square", "wide", "tall", "panoramic"]) .optional() - .describe("Aspect ratio filter. Only use if specifically requested. Leave unset for general searches."), + .describe( + "Aspect ratio filter. Only use if specifically requested. Leave unset for general searches.", + ), }); type SearchGoogleImagesArgs = z.infer; @@ -64,9 +70,15 @@ export function registerSearchGoogleImagesTool(server: McpServer): void { const { query, limit = DEFAULT_IMAGE_LIMIT, imageSize, imageType, aspectRatio } = args; try { - const response = await searchGoogleImages({ query, limit, imageSize, imageType, aspectRatio }); + const response = await searchGoogleImages({ + query, + limit, + imageSize, + imageType, + aspectRatio, + }); - const images = (response.images_results ?? []).map((img) => ({ + const images = (response.images_results ?? []).map(img => ({ position: img.position, thumbnail: img.thumbnail, original: img.original, diff --git a/lib/mcp/tools/tasks/__tests__/registerGetTaskRunStatusTool.test.ts b/lib/mcp/tools/tasks/__tests__/registerGetTaskRunStatusTool.test.ts index 8557432f..f9847265 100644 --- a/lib/mcp/tools/tasks/__tests__/registerGetTaskRunStatusTool.test.ts +++ b/lib/mcp/tools/tasks/__tests__/registerGetTaskRunStatusTool.test.ts @@ -73,13 +73,11 @@ describe("registerGetTaskRunStatusTool", () => { it("returns error when resolveAccountId returns an error", async () => { mockResolveAccountId.mockResolvedValue({ accountId: null, - error: "Authentication required. Provide an API key via Authorization: Bearer header, or provide account_id from the system prompt context.", + error: + "Authentication required. Provide an API key via Authorization: Bearer header, or provide account_id from the system prompt context.", }); - const result = await registeredHandler( - { runId: "run_123" }, - createMockExtra(), - ); + const result = await registeredHandler({ runId: "run_123" }, createMockExtra()); expect(result).toEqual({ content: [ @@ -97,10 +95,7 @@ describe("registerGetTaskRunStatusTool", () => { error: null, }); - const result = await registeredHandler( - { runId: "run_123" }, - createMockExtra(), - ); + const result = await registeredHandler({ runId: "run_123" }, createMockExtra()); expect(result).toEqual({ content: [ diff --git a/lib/mcp/tools/transcribe/index.ts b/lib/mcp/tools/transcribe/index.ts index 01ff8e19..dedbb17c 100644 --- a/lib/mcp/tools/transcribe/index.ts +++ b/lib/mcp/tools/transcribe/index.ts @@ -9,4 +9,3 @@ import { registerTranscribeAudioTool } from "./registerTranscribeAudioTool"; export function registerTranscribeTools(server: McpServer): void { registerTranscribeAudioTool(server); } - diff --git a/lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts b/lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts index 0d781822..d8a64f79 100644 --- a/lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts +++ b/lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts @@ -15,6 +15,10 @@ const transcribeAudioSchema = z.object({ type TranscribeAudioArgs = z.infer; +/** + * + * @param server + */ export function registerTranscribeAudioTool(server: McpServer): void { server.registerTool( "transcribe_audio", @@ -48,4 +52,3 @@ export function registerTranscribeAudioTool(server: McpServer): void { }, ); } - diff --git a/lib/messages/__tests__/convertToUiMessages.test.ts b/lib/messages/__tests__/convertToUiMessages.test.ts index 75280068..a5410430 100644 --- a/lib/messages/__tests__/convertToUiMessages.test.ts +++ b/lib/messages/__tests__/convertToUiMessages.test.ts @@ -1,13 +1,13 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import convertToUiMessages from "../convertToUiMessages"; + // Mock generateUUID before importing the module vi.mock("@/lib/uuid/generateUUID", () => ({ default: vi.fn(() => "generated-uuid"), generateUUID: vi.fn(() => "generated-uuid"), })); -import convertToUiMessages from "../convertToUiMessages"; - describe("convertToUiMessages", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/messages/__tests__/extractImageUrlsFromMessages.test.ts b/lib/messages/__tests__/extractImageUrlsFromMessages.test.ts index ccc125ea..f1bbbd55 100644 --- a/lib/messages/__tests__/extractImageUrlsFromMessages.test.ts +++ b/lib/messages/__tests__/extractImageUrlsFromMessages.test.ts @@ -10,9 +10,7 @@ describe("extractImageUrlsFromMessages", () => { }); it("returns empty array for messages without parts", () => { - const messages: UIMessage[] = [ - { id: "1", role: "user", content: "Hello" } as UIMessage, - ]; + const messages: UIMessage[] = [{ id: "1", role: "user", content: "Hello" } as UIMessage]; const result = extractImageUrlsFromMessages(messages); expect(result).toEqual([]); }); @@ -23,9 +21,7 @@ describe("extractImageUrlsFromMessages", () => { id: "1", role: "user", content: "Check this image", - parts: [ - { type: "file", mediaType: "image/png", url: "https://example.com/image.png" }, - ], + parts: [{ type: "file", mediaType: "image/png", url: "https://example.com/image.png" }], } as UIMessage, ]; const result = extractImageUrlsFromMessages(messages); @@ -38,17 +34,13 @@ describe("extractImageUrlsFromMessages", () => { id: "1", role: "user", content: "Image 1", - parts: [ - { type: "file", mediaType: "image/png", url: "https://example.com/1.png" }, - ], + parts: [{ type: "file", mediaType: "image/png", url: "https://example.com/1.png" }], } as UIMessage, { id: "2", role: "user", content: "Image 2", - parts: [ - { type: "file", mediaType: "image/jpeg", url: "https://example.com/2.jpg" }, - ], + parts: [{ type: "file", mediaType: "image/jpeg", url: "https://example.com/2.jpg" }], } as UIMessage, ]; const result = extractImageUrlsFromMessages(messages); @@ -240,10 +232,7 @@ describe("extractImageUrlsFromMessages", () => { } as UIMessage, ]; const result = extractImageUrlsFromMessages(messages); - expect(result).toEqual([ - "https://example.com/valid.png", - "https://example.com/valid.gif", - ]); + expect(result).toEqual(["https://example.com/valid.png", "https://example.com/valid.gif"]); }); }); }); diff --git a/lib/messages/__tests__/getLatestUserMessageText.test.ts b/lib/messages/__tests__/getLatestUserMessageText.test.ts index 855cd33e..f99dd7b3 100644 --- a/lib/messages/__tests__/getLatestUserMessageText.test.ts +++ b/lib/messages/__tests__/getLatestUserMessageText.test.ts @@ -112,9 +112,7 @@ describe("getLatestUserMessageText", () => { id: "1", role: "user", content: "Hello", - parts: [ - { type: "file", mediaType: "image/png", url: "https://example.com/image.png" }, - ], + parts: [{ type: "file", mediaType: "image/png", url: "https://example.com/image.png" }], }, ]; diff --git a/lib/messages/__tests__/getTextContent.test.ts b/lib/messages/__tests__/getTextContent.test.ts index 9820e6ac..1822bb4a 100644 --- a/lib/messages/__tests__/getTextContent.test.ts +++ b/lib/messages/__tests__/getTextContent.test.ts @@ -40,9 +40,7 @@ describe("getTextContent", () => { }); it("returns empty string when no text parts exist", () => { - const content = [ - { type: "image" as const, image: "data:image/png;base64,..." }, - ] as any; + const content = [{ type: "image" as const, image: "data:image/png;base64,..." }] as any; expect(getTextContent(content)).toBe(""); }); }); diff --git a/lib/messages/convertToUiMessages.ts b/lib/messages/convertToUiMessages.ts index 7318f238..c517f39f 100644 --- a/lib/messages/convertToUiMessages.ts +++ b/lib/messages/convertToUiMessages.ts @@ -23,7 +23,7 @@ type InputMessage = UIMessage | ModelMessage; * @returns Array of messages in UIMessage format */ export default function convertToUiMessages(messages: InputMessage[]): UIMessage[] { - return messages.map((message) => { + return messages.map(message => { if (isUiMessage(message)) { return message; } diff --git a/lib/messages/getLatestUserMessageText.ts b/lib/messages/getLatestUserMessageText.ts index 2d83f506..9433ffbf 100644 --- a/lib/messages/getLatestUserMessageText.ts +++ b/lib/messages/getLatestUserMessageText.ts @@ -7,7 +7,7 @@ import { UIMessage } from "ai"; * @returns The text content of the latest user message, or empty string if none found */ export default function getLatestUserMessageText(messages: UIMessage[]): string { - const userMessages = messages.filter((msg) => msg.role === "user"); + const userMessages = messages.filter(msg => msg.role === "user"); const latestUserMessage = userMessages[userMessages.length - 1]; - return latestUserMessage?.parts?.find((part) => part.type === "text")?.text || ""; + return latestUserMessage?.parts?.find(part => part.type === "text")?.text || ""; } diff --git a/lib/messages/getTextContent.ts b/lib/messages/getTextContent.ts index 8f54bd1a..8423c154 100644 --- a/lib/messages/getTextContent.ts +++ b/lib/messages/getTextContent.ts @@ -13,6 +13,6 @@ export default function getTextContent(content: ModelMessage["content"]): string // Content is an array of parts - extract and join text parts return content .filter((part): part is { type: "text"; text: string } => part.type === "text") - .map((part) => part.text) + .map(part => part.text) .join(""); } diff --git a/lib/notifications/__tests__/createNotificationHandler.test.ts b/lib/notifications/__tests__/createNotificationHandler.test.ts index 7d95a758..60b6e5ba 100644 --- a/lib/notifications/__tests__/createNotificationHandler.test.ts +++ b/lib/notifications/__tests__/createNotificationHandler.test.ts @@ -26,6 +26,10 @@ vi.mock("@/lib/networking/safeParseJson", () => ({ safeParseJson: vi.fn(async (req: Request) => req.json()), })); +/** + * + * @param body + */ function createRequest(body: unknown): NextRequest { return new NextRequest("https://recoup-api.vercel.app/api/notifications", { method: "POST", @@ -84,7 +88,8 @@ describe("createNotificationHandler", () => { it("sends email to account owner with text body", async () => { mockProcessAndSendEmail.mockResolvedValue({ success: true, - message: "Email sent successfully from Agent by Recoup to owner@example.com. CC: none.", + message: + "Email sent successfully from Agent by Recoup to owner@example.com. CC: none.", id: "email-123", }); diff --git a/lib/notifications/__tests__/validateCreateNotificationBody.test.ts b/lib/notifications/__tests__/validateCreateNotificationBody.test.ts index ba0c15ef..645ccedc 100644 --- a/lib/notifications/__tests__/validateCreateNotificationBody.test.ts +++ b/lib/notifications/__tests__/validateCreateNotificationBody.test.ts @@ -16,6 +16,11 @@ vi.mock("@/lib/networking/safeParseJson", () => ({ safeParseJson: vi.fn(async (req: Request) => req.json()), })); +/** + * + * @param body + * @param headers + */ function createRequest(body: unknown, headers: Record = {}): NextRequest { const defaultHeaders: Record = { "Content-Type": "application/json" }; return new NextRequest("http://localhost/api/notifications", { @@ -175,10 +180,7 @@ describe("validateCreateNotificationBody", () => { describe("auth errors", () => { it("returns 401 when auth is missing", async () => { mockValidateAuthContext.mockResolvedValue( - NextResponse.json( - { status: "error", error: "Unauthorized" }, - { status: 401 }, - ), + NextResponse.json({ status: "error", error: "Unauthorized" }, { status: 401 }), ); const request = createRequest({ subject: "Test" }); diff --git a/lib/organizations/__tests__/canAccessAccount.test.ts b/lib/organizations/__tests__/canAccessAccount.test.ts index 54f5a231..b7d93047 100644 --- a/lib/organizations/__tests__/canAccessAccount.test.ts +++ b/lib/organizations/__tests__/canAccessAccount.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { canAccessAccount } from "../canAccessAccount"; +import { getAccountOrganizations } from "@/lib/supabase/account_organization_ids/getAccountOrganizations"; + // Mock RECOUP_ORG_ID constant vi.mock("@/lib/const", () => ({ RECOUP_ORG_ID: "recoup-admin-org-id", @@ -11,8 +13,6 @@ vi.mock("@/lib/supabase/account_organization_ids/getAccountOrganizations", () => getAccountOrganizations: vi.fn(), })); -import { getAccountOrganizations } from "@/lib/supabase/account_organization_ids/getAccountOrganizations"; - describe("canAccessAccount", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/organizations/__tests__/validateOrganizationAccess.test.ts b/lib/organizations/__tests__/validateOrganizationAccess.test.ts index 27f26fdb..2182d5f1 100644 --- a/lib/organizations/__tests__/validateOrganizationAccess.test.ts +++ b/lib/organizations/__tests__/validateOrganizationAccess.test.ts @@ -1,13 +1,13 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { validateOrganizationAccess } from "../validateOrganizationAccess"; +import { getAccountOrganizations } from "@/lib/supabase/account_organization_ids/getAccountOrganizations"; + // Mock getAccountOrganizations supabase lib vi.mock("@/lib/supabase/account_organization_ids/getAccountOrganizations", () => ({ getAccountOrganizations: vi.fn(), })); -import { getAccountOrganizations } from "@/lib/supabase/account_organization_ids/getAccountOrganizations"; - const mockGetAccountOrganizations = vi.mocked(getAccountOrganizations); describe("validateOrganizationAccess", () => { diff --git a/lib/organizations/addArtistToOrgHandler.ts b/lib/organizations/addArtistToOrgHandler.ts index c422bcdc..561cd9ce 100644 --- a/lib/organizations/addArtistToOrgHandler.ts +++ b/lib/organizations/addArtistToOrgHandler.ts @@ -62,4 +62,3 @@ export async function addArtistToOrgHandler(request: NextRequest): Promise { +export async function canAccessAccount(params: CanAccessAccountParams): Promise { const { orgId, targetAccountId } = params; if (!orgId || !targetAccountId) { diff --git a/lib/organizations/createOrganizationHandler.ts b/lib/organizations/createOrganizationHandler.ts index 92fc2b23..81548908 100644 --- a/lib/organizations/createOrganizationHandler.ts +++ b/lib/organizations/createOrganizationHandler.ts @@ -61,4 +61,3 @@ export async function createOrganizationHandler(request: NextRequest): Promise(); return rawOrgs @@ -37,4 +39,3 @@ export function formatAccountOrganizations(rawOrgs: AccountOrganization[]): Form organization_image: org.organization?.account_info?.[0]?.image || null, })); } - diff --git a/lib/organizations/validateAddArtistToOrgBody.ts b/lib/organizations/validateAddArtistToOrgBody.ts index 6b57dc13..42cd49c8 100644 --- a/lib/organizations/validateAddArtistToOrgBody.ts +++ b/lib/organizations/validateAddArtistToOrgBody.ts @@ -4,7 +4,9 @@ import { z } from "zod"; export const addArtistToOrgBodySchema = z.object({ artistId: z.string({ message: "artistId is required" }).uuid("artistId must be a valid UUID"), - organizationId: z.string({ message: "organizationId is required" }).uuid("organizationId must be a valid UUID"), + organizationId: z + .string({ message: "organizationId is required" }) + .uuid("organizationId must be a valid UUID"), }); export type AddArtistToOrgBody = z.infer; @@ -35,4 +37,3 @@ export function validateAddArtistToOrgBody(body: unknown): NextResponse | AddArt return result.data; } - diff --git a/lib/organizations/validateCreateOrganizationBody.ts b/lib/organizations/validateCreateOrganizationBody.ts index 1ae1f480..5026b2a3 100644 --- a/lib/organizations/validateCreateOrganizationBody.ts +++ b/lib/organizations/validateCreateOrganizationBody.ts @@ -37,4 +37,3 @@ export function validateCreateOrganizationBody( return result.data; } - diff --git a/lib/perplexity/chatWithPerplexity.ts b/lib/perplexity/chatWithPerplexity.ts index 476d1c37..cf779d05 100644 --- a/lib/perplexity/chatWithPerplexity.ts +++ b/lib/perplexity/chatWithPerplexity.ts @@ -44,7 +44,9 @@ export async function chatWithPerplexity( if (!response.ok) { const errorText = await response.text(); - throw new Error(`Perplexity API error: ${response.status} ${response.statusText}\n${errorText}`); + throw new Error( + `Perplexity API error: ${response.status} ${response.statusText}\n${errorText}`, + ); } const data = await response.json(); diff --git a/lib/prompts/getSystemPrompt.ts b/lib/prompts/getSystemPrompt.ts index 54964670..5077609a 100644 --- a/lib/prompts/getSystemPrompt.ts +++ b/lib/prompts/getSystemPrompt.ts @@ -13,6 +13,7 @@ import { AccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetail * @param params.artistInstruction - The artist instruction * @param params.conversationName - The name of the conversation * @param params.accountWithDetails - The account with details + * @param params.orgId * @returns The system prompt */ export function getSystemPrompt({ diff --git a/lib/pulse/__tests__/buildGetPulsesParams.test.ts b/lib/pulse/__tests__/buildGetPulsesParams.test.ts index 1ff1e852..50cd90e7 100644 --- a/lib/pulse/__tests__/buildGetPulsesParams.test.ts +++ b/lib/pulse/__tests__/buildGetPulsesParams.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { buildGetPulsesParams } from "../buildGetPulsesParams"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; + vi.mock("@/lib/organizations/canAccessAccount", () => ({ canAccessAccount: vi.fn(), })); @@ -9,8 +11,6 @@ vi.mock("@/lib/const", () => ({ RECOUP_ORG_ID: "recoup-org-id", })); -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; - describe("buildGetPulsesParams", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/pulse/__tests__/getPulsesHandler.test.ts b/lib/pulse/__tests__/getPulsesHandler.test.ts index f63cba29..028d1af7 100644 --- a/lib/pulse/__tests__/getPulsesHandler.test.ts +++ b/lib/pulse/__tests__/getPulsesHandler.test.ts @@ -2,6 +2,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; import { getPulsesHandler } from "../getPulsesHandler"; +import { validateGetPulsesRequest } from "../validateGetPulsesRequest"; +import { selectPulseAccounts } from "@/lib/supabase/pulse_accounts/selectPulseAccounts"; + // Mock dependencies vi.mock("../validateGetPulsesRequest", () => ({ validateGetPulsesRequest: vi.fn(), @@ -15,9 +18,6 @@ vi.mock("@/lib/networking/getCorsHeaders", () => ({ getCorsHeaders: vi.fn(() => new Headers()), })); -import { validateGetPulsesRequest } from "../validateGetPulsesRequest"; -import { selectPulseAccounts } from "@/lib/supabase/pulse_accounts/selectPulseAccounts"; - describe("getPulsesHandler", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/pulse/__tests__/updatePulsesHandler.test.ts b/lib/pulse/__tests__/updatePulsesHandler.test.ts index 825bf9cb..64d796a1 100644 --- a/lib/pulse/__tests__/updatePulsesHandler.test.ts +++ b/lib/pulse/__tests__/updatePulsesHandler.test.ts @@ -2,6 +2,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; import { updatePulsesHandler } from "../updatePulsesHandler"; +import { validateUpdatePulsesRequest } from "../validateUpdatePulsesRequest"; +import { upsertPulseAccount } from "@/lib/supabase/pulse_accounts/upsertPulseAccount"; + // Mock dependencies vi.mock("../validateUpdatePulsesRequest", () => ({ validateUpdatePulsesRequest: vi.fn(), @@ -15,9 +18,6 @@ vi.mock("@/lib/networking/getCorsHeaders", () => ({ getCorsHeaders: vi.fn(() => new Headers()), })); -import { validateUpdatePulsesRequest } from "../validateUpdatePulsesRequest"; -import { upsertPulseAccount } from "@/lib/supabase/pulse_accounts/upsertPulseAccount"; - describe("updatePulsesHandler", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/pulse/__tests__/validateGetPulsesRequest.test.ts b/lib/pulse/__tests__/validateGetPulsesRequest.test.ts index 7219d9c9..6e1ecc68 100644 --- a/lib/pulse/__tests__/validateGetPulsesRequest.test.ts +++ b/lib/pulse/__tests__/validateGetPulsesRequest.test.ts @@ -2,6 +2,9 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest, NextResponse } from "next/server"; import { validateGetPulsesRequest } from "../validateGetPulsesRequest"; +import { validateAuthContext } from "@/lib/auth/validateAuthContext"; +import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; + // Mock dependencies vi.mock("@/lib/auth/validateAuthContext", () => ({ validateAuthContext: vi.fn(), @@ -19,9 +22,6 @@ vi.mock("@/lib/const", () => ({ RECOUP_ORG_ID: "recoup-org-id", })); -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { canAccessAccount } from "@/lib/organizations/canAccessAccount"; - describe("validateGetPulsesRequest", () => { beforeEach(() => { vi.clearAllMocks(); @@ -144,12 +144,9 @@ describe("validateGetPulsesRequest", () => { authToken: "test-token", }); - const request = new NextRequest( - `http://localhost/api/pulses?account_id=${otherAccountId}`, - { - headers: { "x-api-key": "test-api-key" }, - }, - ); + const request = new NextRequest(`http://localhost/api/pulses?account_id=${otherAccountId}`, { + headers: { "x-api-key": "test-api-key" }, + }); const result = await validateGetPulsesRequest(request); expect(result).toBeInstanceOf(NextResponse); @@ -167,12 +164,9 @@ describe("validateGetPulsesRequest", () => { }); vi.mocked(canAccessAccount).mockResolvedValue(true); - const request = new NextRequest( - `http://localhost/api/pulses?account_id=${targetAccountId}`, - { - headers: { "x-api-key": "test-api-key" }, - }, - ); + const request = new NextRequest(`http://localhost/api/pulses?account_id=${targetAccountId}`, { + headers: { "x-api-key": "test-api-key" }, + }); const result = await validateGetPulsesRequest(request); expect(canAccessAccount).toHaveBeenCalledWith({ @@ -194,12 +188,9 @@ describe("validateGetPulsesRequest", () => { }); vi.mocked(canAccessAccount).mockResolvedValue(false); - const request = new NextRequest( - `http://localhost/api/pulses?account_id=${notInOrgId}`, - { - headers: { "x-api-key": "test-api-key" }, - }, - ); + const request = new NextRequest(`http://localhost/api/pulses?account_id=${notInOrgId}`, { + headers: { "x-api-key": "test-api-key" }, + }); const result = await validateGetPulsesRequest(request); expect(canAccessAccount).toHaveBeenCalledWith({ @@ -221,12 +212,9 @@ describe("validateGetPulsesRequest", () => { }); vi.mocked(canAccessAccount).mockResolvedValue(true); // Admin always has access - const request = new NextRequest( - `http://localhost/api/pulses?account_id=${anyAccountId}`, - { - headers: { "x-api-key": "recoup-admin-key" }, - }, - ); + const request = new NextRequest(`http://localhost/api/pulses?account_id=${anyAccountId}`, { + headers: { "x-api-key": "recoup-admin-key" }, + }); const result = await validateGetPulsesRequest(request); expect(canAccessAccount).toHaveBeenCalledWith({ diff --git a/lib/rooms/__tests__/copyRoom.test.ts b/lib/rooms/__tests__/copyRoom.test.ts index 0f1a876e..94d32b4d 100644 --- a/lib/rooms/__tests__/copyRoom.test.ts +++ b/lib/rooms/__tests__/copyRoom.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { copyRoom } from "../copyRoom"; + const mockSelectRoom = vi.fn(); const mockUpsertRoom = vi.fn(); @@ -15,8 +17,6 @@ vi.mock("@/lib/uuid/generateUUID", () => ({ default: () => "generated-uuid-123", })); -import { copyRoom } from "../copyRoom"; - describe("copyRoom", () => { const mockSourceRoom = { id: "source-room-123", diff --git a/lib/rooms/copyRoom.ts b/lib/rooms/copyRoom.ts index 1cf8e5d4..b5d4f0f6 100644 --- a/lib/rooms/copyRoom.ts +++ b/lib/rooms/copyRoom.ts @@ -10,10 +10,7 @@ import generateUUID from "@/lib/uuid/generateUUID"; * @param artistId - The ID of the artist for the new room * @returns The ID of the new room or null if operation failed */ -export async function copyRoom( - sourceRoomId: string, - artistId: string, -): Promise { +export async function copyRoom(sourceRoomId: string, artistId: string): Promise { try { // Get the source room data const sourceRoom = await selectRoom(sourceRoomId); diff --git a/lib/sandbox/__tests__/createSandboxFromSnapshot.test.ts b/lib/sandbox/__tests__/createSandboxFromSnapshot.test.ts index a4db1b61..887d136c 100644 --- a/lib/sandbox/__tests__/createSandboxFromSnapshot.test.ts +++ b/lib/sandbox/__tests__/createSandboxFromSnapshot.test.ts @@ -12,13 +12,11 @@ vi.mock("@/lib/sandbox/createSandbox", () => ({ })); vi.mock("@/lib/supabase/account_snapshots/selectAccountSnapshots", () => ({ - selectAccountSnapshots: (...args: unknown[]) => - mockSelectAccountSnapshots(...args), + selectAccountSnapshots: (...args: unknown[]) => mockSelectAccountSnapshots(...args), })); vi.mock("@/lib/supabase/account_sandboxes/insertAccountSandbox", () => ({ - insertAccountSandbox: (...args: unknown[]) => - mockInsertAccountSandbox(...args), + insertAccountSandbox: (...args: unknown[]) => mockInsertAccountSandbox(...args), })); describe("createSandboxFromSnapshot", () => { diff --git a/lib/sandbox/__tests__/getActiveSandbox.test.ts b/lib/sandbox/__tests__/getActiveSandbox.test.ts index 9cfe3dba..58133707 100644 --- a/lib/sandbox/__tests__/getActiveSandbox.test.ts +++ b/lib/sandbox/__tests__/getActiveSandbox.test.ts @@ -12,8 +12,7 @@ vi.mock("@vercel/sandbox", () => ({ })); vi.mock("@/lib/supabase/account_sandboxes/selectAccountSandboxes", () => ({ - selectAccountSandboxes: (...args: unknown[]) => - mockSelectAccountSandboxes(...args), + selectAccountSandboxes: (...args: unknown[]) => mockSelectAccountSandboxes(...args), })); describe("getActiveSandbox", () => { @@ -22,18 +21,14 @@ describe("getActiveSandbox", () => { }); it("returns sandbox when most recent is running", async () => { - mockSelectAccountSandboxes.mockResolvedValue([ - { sandbox_id: "sbx_123", account_id: "acc_1" }, - ]); + mockSelectAccountSandboxes.mockResolvedValue([{ sandbox_id: "sbx_123", account_id: "acc_1" }]); const mockSandbox = { sandboxId: "sbx_123", status: "running", runCommand: vi.fn(), }; - vi.mocked(Sandbox.get).mockResolvedValue( - mockSandbox as unknown as Sandbox, - ); + vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as unknown as Sandbox); const result = await getActiveSandbox("acc_1"); @@ -62,9 +57,7 @@ describe("getActiveSandbox", () => { sandboxId: "sbx_stopped", status: "stopped", }; - vi.mocked(Sandbox.get).mockResolvedValue( - mockSandbox as unknown as Sandbox, - ); + vi.mocked(Sandbox.get).mockResolvedValue(mockSandbox as unknown as Sandbox); const result = await getActiveSandbox("acc_1"); diff --git a/lib/sandbox/__tests__/getOrCreateSandbox.test.ts b/lib/sandbox/__tests__/getOrCreateSandbox.test.ts index 749ae70f..56e641bc 100644 --- a/lib/sandbox/__tests__/getOrCreateSandbox.test.ts +++ b/lib/sandbox/__tests__/getOrCreateSandbox.test.ts @@ -11,8 +11,7 @@ vi.mock("../getActiveSandbox", () => ({ })); vi.mock("../createSandboxFromSnapshot", () => ({ - createSandboxFromSnapshot: (...args: unknown[]) => - mockCreateSandboxFromSnapshot(...args), + createSandboxFromSnapshot: (...args: unknown[]) => mockCreateSandboxFromSnapshot(...args), })); describe("getOrCreateSandbox", () => { diff --git a/lib/sandbox/__tests__/getSandboxesFileHandler.test.ts b/lib/sandbox/__tests__/getSandboxesFileHandler.test.ts index 69690ac0..b8a77260 100644 --- a/lib/sandbox/__tests__/getSandboxesFileHandler.test.ts +++ b/lib/sandbox/__tests__/getSandboxesFileHandler.test.ts @@ -41,7 +41,7 @@ describe("getSandboxesFileHandler", () => { beforeEach(() => { vi.clearAllMocks(); // Default: resolveSubmodulePath passes through unchanged - vi.mocked(resolveSubmodulePath).mockImplementation(async (params) => params); + vi.mocked(resolveSubmodulePath).mockImplementation(async params => params); }); it("returns error response when validation fails", async () => { diff --git a/lib/sandbox/getActiveSandbox.ts b/lib/sandbox/getActiveSandbox.ts index 24fdf8f3..faa220ad 100644 --- a/lib/sandbox/getActiveSandbox.ts +++ b/lib/sandbox/getActiveSandbox.ts @@ -7,9 +7,7 @@ import { selectAccountSandboxes } from "@/lib/supabase/account_sandboxes/selectA * @param accountId - The account ID to find an active sandbox for * @returns The running Sandbox instance, or null if none found */ -export async function getActiveSandbox( - accountId: string, -): Promise { +export async function getActiveSandbox(accountId: string): Promise { const sandboxes = await selectAccountSandboxes({ accountIds: [accountId], }); diff --git a/lib/sandbox/runClaudeCode.ts b/lib/sandbox/runClaudeCode.ts index ccf83eec..196fed22 100644 --- a/lib/sandbox/runClaudeCode.ts +++ b/lib/sandbox/runClaudeCode.ts @@ -9,7 +9,7 @@ import type { Sandbox } from "@vercel/sandbox"; */ export async function runClaudeCode(sandbox: Sandbox, prompt: string): Promise { const script = `claude --permission-mode acceptEdits --model opus '${prompt}'`; - + await sandbox.writeFiles([ { path: "/vercel/sandbox/ralph-once.sh", diff --git a/lib/segments/createSegmentResponses.ts b/lib/segments/createSegmentResponses.ts index c9269ab0..ec25b5db 100644 --- a/lib/segments/createSegmentResponses.ts +++ b/lib/segments/createSegmentResponses.ts @@ -27,4 +27,3 @@ export const errorResponse = (message: string) => ({ data: [], count: 0, }); - diff --git a/lib/spotify/getSpotifyFollowers.ts b/lib/spotify/getSpotifyFollowers.ts index da490368..acd1c3be 100644 --- a/lib/spotify/getSpotifyFollowers.ts +++ b/lib/spotify/getSpotifyFollowers.ts @@ -37,6 +37,7 @@ interface SpotifySearchResponse { /** * Get Spotify follower count for an artist + * * @param artistName - The name of the artist to search for * @returns Promise - The follower count of the first matching artist */ @@ -48,9 +49,7 @@ export async function getSpotifyFollowers(artistName: string): Promise { const response = await fetch(url); if (!response.ok) { - throw new Error( - `API request failed: ${response.status} ${response.statusText}` - ); + throw new Error(`API request failed: ${response.status} ${response.statusText}`); } const data: SpotifySearchResponse = await response.json(); @@ -61,10 +60,7 @@ export async function getSpotifyFollowers(artistName: string): Promise { return data.artists.items[0].followers.total; } catch (error) { - console.error( - `Error fetching Spotify followers for "${artistName}":`, - error - ); + console.error(`Error fetching Spotify followers for "${artistName}":`, error); throw error; } } diff --git a/lib/supabase/account_artist_ids/__tests__/insertAccountArtistId.test.ts b/lib/supabase/account_artist_ids/__tests__/insertAccountArtistId.test.ts index 2087aeda..725b8892 100644 --- a/lib/supabase/account_artist_ids/__tests__/insertAccountArtistId.test.ts +++ b/lib/supabase/account_artist_ids/__tests__/insertAccountArtistId.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { insertAccountArtistId } from "../insertAccountArtistId"; + const mockFrom = vi.fn(); const mockInsert = vi.fn(); const mockSelect = vi.fn(); @@ -11,8 +13,6 @@ vi.mock("@/lib/supabase/serverClient", () => ({ }, })); -import { insertAccountArtistId } from "../insertAccountArtistId"; - describe("insertAccountArtistId", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/account_artist_ids/__tests__/selectAccountArtistId.test.ts b/lib/supabase/account_artist_ids/__tests__/selectAccountArtistId.test.ts index 12c6935b..cd06f9b9 100644 --- a/lib/supabase/account_artist_ids/__tests__/selectAccountArtistId.test.ts +++ b/lib/supabase/account_artist_ids/__tests__/selectAccountArtistId.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { selectAccountArtistId } from "../selectAccountArtistId"; +import supabase from "../../serverClient"; + vi.mock("../../serverClient", () => { const mockFrom = vi.fn(); return { @@ -8,8 +10,6 @@ vi.mock("../../serverClient", () => { }; }); -import supabase from "../../serverClient"; - describe("selectAccountArtistId", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/account_artist_ids/getAccountArtistIds.ts b/lib/supabase/account_artist_ids/getAccountArtistIds.ts index 834c48ab..42b550d0 100644 --- a/lib/supabase/account_artist_ids/getAccountArtistIds.ts +++ b/lib/supabase/account_artist_ids/getAccountArtistIds.ts @@ -8,7 +8,9 @@ export type AccountArtistRow = ArtistQueryRow & { artist_id: string; pinned: boo * Get all artists for an array of artist IDs or account IDs, with full info. * Returns raw data - formatting should be done by caller. * - * @param params Object with artistIds or accountIds array + * @param params - Object with artistIds or accountIds array + * @param params.artistIds + * @param params.accountIds * @returns Array of raw artist rows from database */ export async function getAccountArtistIds(params: { @@ -46,4 +48,3 @@ export async function getAccountArtistIds(params: { return (data || []) as unknown as AccountArtistRow[]; } - diff --git a/lib/supabase/account_artist_ids/selectAccountArtistId.ts b/lib/supabase/account_artist_ids/selectAccountArtistId.ts index 8031957e..baddf777 100644 --- a/lib/supabase/account_artist_ids/selectAccountArtistId.ts +++ b/lib/supabase/account_artist_ids/selectAccountArtistId.ts @@ -7,10 +7,7 @@ import supabase from "../serverClient"; * @param artistId - The artist ID * @returns The row if found, null if not found or on error */ -export async function selectAccountArtistId( - accountId: string, - artistId: string, -) { +export async function selectAccountArtistId(accountId: string, artistId: string) { const { data, error } = await supabase .from("account_artist_ids") .select("artist_id") diff --git a/lib/supabase/account_info/insertAccountInfo.ts b/lib/supabase/account_info/insertAccountInfo.ts index b83bb4a3..b9ae6e34 100644 --- a/lib/supabase/account_info/insertAccountInfo.ts +++ b/lib/supabase/account_info/insertAccountInfo.ts @@ -19,4 +19,3 @@ export async function insertAccountInfo( return data || null; } - diff --git a/lib/supabase/account_organization_ids/__tests__/selectAccountOrganizationIds.test.ts b/lib/supabase/account_organization_ids/__tests__/selectAccountOrganizationIds.test.ts index d4c7a595..de672a2a 100644 --- a/lib/supabase/account_organization_ids/__tests__/selectAccountOrganizationIds.test.ts +++ b/lib/supabase/account_organization_ids/__tests__/selectAccountOrganizationIds.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { selectAccountOrganizationIds } from "../selectAccountOrganizationIds"; +import supabase from "../../serverClient"; + vi.mock("../../serverClient", () => { const mockFrom = vi.fn(); return { @@ -8,8 +10,6 @@ vi.mock("../../serverClient", () => { }; }); -import supabase from "../../serverClient"; - describe("selectAccountOrganizationIds", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/account_organization_ids/addAccountToOrganization.ts b/lib/supabase/account_organization_ids/addAccountToOrganization.ts index 846ae7b4..115a5710 100644 --- a/lib/supabase/account_organization_ids/addAccountToOrganization.ts +++ b/lib/supabase/account_organization_ids/addAccountToOrganization.ts @@ -27,4 +27,3 @@ export async function addAccountToOrganization( return data?.id || null; } - diff --git a/lib/supabase/account_organization_ids/selectAccountOrganizationIds.ts b/lib/supabase/account_organization_ids/selectAccountOrganizationIds.ts index 68fd6618..5be4a4c1 100644 --- a/lib/supabase/account_organization_ids/selectAccountOrganizationIds.ts +++ b/lib/supabase/account_organization_ids/selectAccountOrganizationIds.ts @@ -7,10 +7,7 @@ import supabase from "../serverClient"; * @param orgIds - Organization IDs to check membership against * @returns Array of matching rows, or null on error */ -export async function selectAccountOrganizationIds( - accountId: string, - orgIds: string[], -) { +export async function selectAccountOrganizationIds(accountId: string, orgIds: string[]) { if (!orgIds.length) return []; const { data, error } = await supabase diff --git a/lib/supabase/account_sandboxes/__tests__/insertAccountSandbox.test.ts b/lib/supabase/account_sandboxes/__tests__/insertAccountSandbox.test.ts index 15e605d9..f370a34d 100644 --- a/lib/supabase/account_sandboxes/__tests__/insertAccountSandbox.test.ts +++ b/lib/supabase/account_sandboxes/__tests__/insertAccountSandbox.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { insertAccountSandbox } from "../insertAccountSandbox"; + const mockFrom = vi.fn(); const mockInsert = vi.fn(); const mockSelect = vi.fn(); @@ -11,8 +13,6 @@ vi.mock("@/lib/supabase/serverClient", () => ({ }, })); -import { insertAccountSandbox } from "../insertAccountSandbox"; - describe("insertAccountSandbox", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/account_snapshots/__tests__/upsertAccountSnapshot.test.ts b/lib/supabase/account_snapshots/__tests__/upsertAccountSnapshot.test.ts index de71fd24..cf198d03 100644 --- a/lib/supabase/account_snapshots/__tests__/upsertAccountSnapshot.test.ts +++ b/lib/supabase/account_snapshots/__tests__/upsertAccountSnapshot.test.ts @@ -79,8 +79,7 @@ describe("upsertAccountSnapshot", () => { it("returns error when account_id foreign key constraint fails", async () => { const mockError = { - message: - 'insert or update on table "account_snapshots" violates foreign key constraint', + message: 'insert or update on table "account_snapshots" violates foreign key constraint', code: "23503", }; mockSingle.mockResolvedValue({ data: null, error: mockError }); diff --git a/lib/supabase/account_workspace_ids/__tests__/selectAccountWorkspaceId.test.ts b/lib/supabase/account_workspace_ids/__tests__/selectAccountWorkspaceId.test.ts index e3611d22..135134c3 100644 --- a/lib/supabase/account_workspace_ids/__tests__/selectAccountWorkspaceId.test.ts +++ b/lib/supabase/account_workspace_ids/__tests__/selectAccountWorkspaceId.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { selectAccountWorkspaceId } from "../selectAccountWorkspaceId"; +import supabase from "../../serverClient"; + vi.mock("../../serverClient", () => { const mockFrom = vi.fn(); return { @@ -8,8 +10,6 @@ vi.mock("../../serverClient", () => { }; }); -import supabase from "../../serverClient"; - describe("selectAccountWorkspaceId", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/account_workspace_ids/getAccountWorkspaceIds.ts b/lib/supabase/account_workspace_ids/getAccountWorkspaceIds.ts index 989fe9ba..4ca7ad8e 100644 --- a/lib/supabase/account_workspace_ids/getAccountWorkspaceIds.ts +++ b/lib/supabase/account_workspace_ids/getAccountWorkspaceIds.ts @@ -10,7 +10,7 @@ export type AccountWorkspaceRow = Omit & { * Get all workspaces for an account, with full info. * Returns raw data - formatting should be done by caller. * - * @param accountId The owner's account ID + * @param accountId - The owner's account ID * @returns Array of raw workspace rows from database */ export async function getAccountWorkspaceIds(accountId: string): Promise { @@ -39,4 +39,3 @@ export async function getAccountWorkspaceIds(accountId: string): Promise ({ }, })); -import { selectAccountWithSocials } from "../selectAccountWithSocials"; - describe("selectAccountWithSocials", () => { beforeEach(() => { vi.clearAllMocks(); @@ -27,7 +27,13 @@ describe("selectAccountWithSocials", () => { name: "Test Artist", timestamp: 1704067200000, account_socials: [{ id: "social-1", platform: "spotify" }], - account_info: [{ id: "info-1", image: "https://example.com/image.jpg", updated_at: "2024-01-01T12:00:00Z" }], + account_info: [ + { + id: "info-1", + image: "https://example.com/image.jpg", + updated_at: "2024-01-01T12:00:00Z", + }, + ], }; mockSingle.mockResolvedValue({ data: mockData, error: null }); diff --git a/lib/supabase/accounts/selectAccounts.ts b/lib/supabase/accounts/selectAccounts.ts index 1770011f..8b54c3bd 100644 --- a/lib/supabase/accounts/selectAccounts.ts +++ b/lib/supabase/accounts/selectAccounts.ts @@ -10,19 +10,14 @@ import type { Tables } from "@/types/database.types"; * @param accountId - A single account ID string, or an array of account IDs * @returns Array of account records (empty array if not found or on error) */ -export async function selectAccounts( - accountId: string | string[], -): Promise[]> { +export async function selectAccounts(accountId: string | string[]): Promise[]> { const ids = Array.isArray(accountId) ? accountId : [accountId]; if (ids.length === 0) { return []; } - const { data, error } = await supabase - .from("accounts") - .select("*") - .in("id", ids); + const { data, error } = await supabase.from("accounts").select("*").in("id", ids); if (error) { throw error; diff --git a/lib/supabase/artist_organization_ids/__tests__/selectArtistOrganizationIds.test.ts b/lib/supabase/artist_organization_ids/__tests__/selectArtistOrganizationIds.test.ts index b1df5714..405eac75 100644 --- a/lib/supabase/artist_organization_ids/__tests__/selectArtistOrganizationIds.test.ts +++ b/lib/supabase/artist_organization_ids/__tests__/selectArtistOrganizationIds.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { selectArtistOrganizationIds } from "../selectArtistOrganizationIds"; +import supabase from "../../serverClient"; + vi.mock("../../serverClient", () => { const mockFrom = vi.fn(); return { @@ -8,8 +10,6 @@ vi.mock("../../serverClient", () => { }; }); -import supabase from "../../serverClient"; - describe("selectArtistOrganizationIds", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/artist_organization_ids/addArtistToOrganization.ts b/lib/supabase/artist_organization_ids/addArtistToOrganization.ts index 5f8f4feb..7714adbe 100644 --- a/lib/supabase/artist_organization_ids/addArtistToOrganization.ts +++ b/lib/supabase/artist_organization_ids/addArtistToOrganization.ts @@ -31,4 +31,3 @@ export async function addArtistToOrganization( return data?.id || null; } - diff --git a/lib/supabase/artist_organization_ids/getArtistsByOrganization.ts b/lib/supabase/artist_organization_ids/getArtistsByOrganization.ts index d67a8556..3a3fb49e 100644 --- a/lib/supabase/artist_organization_ids/getArtistsByOrganization.ts +++ b/lib/supabase/artist_organization_ids/getArtistsByOrganization.ts @@ -43,4 +43,3 @@ export async function getArtistsByOrganization(organizationIds: string[]): Promi return (data || []) as unknown as ArtistOrgRow[]; } - diff --git a/lib/supabase/files/createFileRecord.ts b/lib/supabase/files/createFileRecord.ts index 6b06744b..3182de11 100644 --- a/lib/supabase/files/createFileRecord.ts +++ b/lib/supabase/files/createFileRecord.ts @@ -25,10 +25,10 @@ export interface CreateFileRecordParams { /** * Create a file record in the database + * + * @param params */ -export async function createFileRecord( - params: CreateFileRecordParams -): Promise { +export async function createFileRecord(params: CreateFileRecordParams): Promise { const { ownerAccountId, artistAccountId, @@ -61,4 +61,3 @@ export async function createFileRecord( return data; } - diff --git a/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts b/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts index be7cfd6f..3bb90258 100644 --- a/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts +++ b/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { selectPulseAccounts } from "../selectPulseAccounts"; + const mockFrom = vi.fn(); const mockSelect = vi.fn(); const mockIn = vi.fn(); @@ -11,8 +13,6 @@ vi.mock("@/lib/supabase/serverClient", () => ({ }, })); -import { selectPulseAccounts } from "../selectPulseAccounts"; - describe("selectPulseAccounts", () => { beforeEach(() => { vi.clearAllMocks(); @@ -107,16 +107,31 @@ describe("selectPulseAccounts", () => { mockEq.mockReturnValue({ eq: mockChainedEq }); const pulses = [ - { id: "pulse-1", account_id: "member-1", active: true, accounts: { account_organization_ids: { organization_id: "org-123" } } }, - { id: "pulse-2", account_id: "member-2", active: false, accounts: { account_organization_ids: { organization_id: "org-123" } } }, + { + id: "pulse-1", + account_id: "member-1", + active: true, + accounts: { account_organization_ids: { organization_id: "org-123" } }, + }, + { + id: "pulse-2", + account_id: "member-2", + active: false, + accounts: { account_organization_ids: { organization_id: "org-123" } }, + }, ]; mockEq.mockResolvedValue({ data: pulses, error: null }); const result = await selectPulseAccounts({ orgId: "org-123" }); expect(mockFrom).toHaveBeenCalledWith("pulse_accounts"); - expect(mockSelect).toHaveBeenCalledWith("*, accounts!inner(account_organization_ids!account_organization_ids_account_id_fkey!inner(organization_id))"); - expect(mockEq).toHaveBeenCalledWith("accounts.account_organization_ids.organization_id", "org-123"); + expect(mockSelect).toHaveBeenCalledWith( + "*, accounts!inner(account_organization_ids!account_organization_ids_account_id_fkey!inner(organization_id))", + ); + expect(mockEq).toHaveBeenCalledWith( + "accounts.account_organization_ids.organization_id", + "org-123", + ); // Result should strip the joined data expect(result).toEqual([ { id: "pulse-1", account_id: "member-1", active: true }, @@ -128,13 +143,25 @@ describe("selectPulseAccounts", () => { const mockChainedEq = vi.fn(); mockEq.mockReturnValue({ eq: mockChainedEq }); - const pulses = [{ id: "pulse-1", account_id: "member-1", active: true, accounts: { account_organization_ids: { organization_id: "org-123" } } }]; + const pulses = [ + { + id: "pulse-1", + account_id: "member-1", + active: true, + accounts: { account_organization_ids: { organization_id: "org-123" } }, + }, + ]; mockChainedEq.mockResolvedValue({ data: pulses, error: null }); const result = await selectPulseAccounts({ orgId: "org-123", active: true }); - expect(mockSelect).toHaveBeenCalledWith("*, accounts!inner(account_organization_ids!account_organization_ids_account_id_fkey!inner(organization_id))"); - expect(mockEq).toHaveBeenCalledWith("accounts.account_organization_ids.organization_id", "org-123"); + expect(mockSelect).toHaveBeenCalledWith( + "*, accounts!inner(account_organization_ids!account_organization_ids_account_id_fkey!inner(organization_id))", + ); + expect(mockEq).toHaveBeenCalledWith( + "accounts.account_organization_ids.organization_id", + "org-123", + ); expect(mockChainedEq).toHaveBeenCalledWith("active", true); // Result should strip the joined data expect(result).toEqual([{ id: "pulse-1", account_id: "member-1", active: true }]); diff --git a/lib/supabase/pulse_accounts/__tests__/upsertPulseAccount.test.ts b/lib/supabase/pulse_accounts/__tests__/upsertPulseAccount.test.ts index 0e19bcda..4873e2e4 100644 --- a/lib/supabase/pulse_accounts/__tests__/upsertPulseAccount.test.ts +++ b/lib/supabase/pulse_accounts/__tests__/upsertPulseAccount.test.ts @@ -1,5 +1,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +import { upsertPulseAccount } from "../upsertPulseAccount"; + const mockFrom = vi.fn(); const mockUpsert = vi.fn(); const mockSelect = vi.fn(); @@ -10,8 +12,6 @@ vi.mock("@/lib/supabase/serverClient", () => ({ }, })); -import { upsertPulseAccount } from "../upsertPulseAccount"; - describe("upsertPulseAccount", () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/lib/supabase/song_artists/insertSongArtists.ts b/lib/supabase/song_artists/insertSongArtists.ts index bec45fae..69878d6d 100644 --- a/lib/supabase/song_artists/insertSongArtists.ts +++ b/lib/supabase/song_artists/insertSongArtists.ts @@ -5,13 +5,12 @@ export type SongArtistInsert = TablesInsert<"song_artists">; /** * Inserts song-artist relationships, skipping duplicates. + * + * @param songArtists */ -export async function insertSongArtists( - songArtists: SongArtistInsert[] -): Promise { +export async function insertSongArtists(songArtists: SongArtistInsert[]): Promise { const records = songArtists.filter( - (record): record is SongArtistInsert => - Boolean(record.song) && Boolean(record.artist) + (record): record is SongArtistInsert => Boolean(record.song) && Boolean(record.artist), ); if (records.length === 0) { @@ -19,16 +18,12 @@ export async function insertSongArtists( } const deduped = [ - ...new Map( - records.map((record) => [`${record.song}-${record.artist}`, record]) - ).values(), + ...new Map(records.map(record => [`${record.song}-${record.artist}`, record])).values(), ]; - const { error } = await supabase - .from("song_artists") - .upsert(deduped, { - onConflict: "song,artist", - }); + const { error } = await supabase.from("song_artists").upsert(deduped, { + onConflict: "song,artist", + }); if (error) { throw new Error(`Failed to insert song artists: ${error.message}`); diff --git a/lib/supabase/storage/uploadFileByKey.ts b/lib/supabase/storage/uploadFileByKey.ts index c04f2bd3..ae149173 100644 --- a/lib/supabase/storage/uploadFileByKey.ts +++ b/lib/supabase/storage/uploadFileByKey.ts @@ -3,6 +3,12 @@ import { SUPABASE_STORAGE_BUCKET } from "@/lib/const"; /** * Upload file to Supabase storage by key + * + * @param key + * @param file + * @param options + * @param options.contentType + * @param options.upsert */ export async function uploadFileByKey( key: string, @@ -10,17 +16,14 @@ export async function uploadFileByKey( options: { contentType?: string; upsert?: boolean; - } = {} + } = {}, ): Promise { - const { error } = await supabase.storage - .from(SUPABASE_STORAGE_BUCKET) - .upload(key, file, { - contentType: options.contentType || "application/octet-stream", - upsert: options.upsert ?? false, - }); + const { error } = await supabase.storage.from(SUPABASE_STORAGE_BUCKET).upload(key, file, { + contentType: options.contentType || "application/octet-stream", + upsert: options.upsert ?? false, + }); if (error) { throw new Error(`Failed to upload file: ${error.message}`); } } - diff --git a/lib/tasks/__tests__/deleteTask.test.ts b/lib/tasks/__tests__/deleteTask.test.ts index 79d2cb0e..df95eb63 100644 --- a/lib/tasks/__tests__/deleteTask.test.ts +++ b/lib/tasks/__tests__/deleteTask.test.ts @@ -1,5 +1,11 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; +// Import after mocks +import { deleteTask } from "../deleteTask"; +import { selectScheduledActions } from "@/lib/supabase/scheduled_actions/selectScheduledActions"; +import { deleteScheduledAction } from "@/lib/supabase/scheduled_actions/deleteScheduledAction"; +import { deleteSchedule } from "@/lib/trigger/deleteSchedule"; + // Mock external dependencies vi.mock("@/lib/supabase/scheduled_actions/selectScheduledActions", () => ({ selectScheduledActions: vi.fn(), @@ -13,12 +19,6 @@ vi.mock("@/lib/trigger/deleteSchedule", () => ({ deleteSchedule: vi.fn(), })); -// Import after mocks -import { deleteTask } from "../deleteTask"; -import { selectScheduledActions } from "@/lib/supabase/scheduled_actions/selectScheduledActions"; -import { deleteScheduledAction } from "@/lib/supabase/scheduled_actions/deleteScheduledAction"; -import { deleteSchedule } from "@/lib/trigger/deleteSchedule"; - const mockSelectScheduledActions = vi.mocked(selectScheduledActions); const mockDeleteScheduledAction = vi.mocked(deleteScheduledAction); const mockDeleteSchedule = vi.mocked(deleteSchedule); @@ -129,13 +129,13 @@ describe("deleteTask", () => { // Track when each operation starts and completes mockDeleteSchedule.mockImplementation(async () => { executionOrder.push("deleteSchedule:start"); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise(resolve => setTimeout(resolve, 10)); executionOrder.push("deleteSchedule:end"); }); mockDeleteScheduledAction.mockImplementation(async () => { executionOrder.push("deleteScheduledAction:start"); - await new Promise((resolve) => setTimeout(resolve, 10)); + await new Promise(resolve => setTimeout(resolve, 10)); executionOrder.push("deleteScheduledAction:end"); }); diff --git a/lib/tasks/__tests__/getTaskRunHandler.test.ts b/lib/tasks/__tests__/getTaskRunHandler.test.ts index bce2ce57..6f764de6 100644 --- a/lib/tasks/__tests__/getTaskRunHandler.test.ts +++ b/lib/tasks/__tests__/getTaskRunHandler.test.ts @@ -23,6 +23,9 @@ vi.mock("@/lib/networking/getCorsHeaders", () => ({ getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), })); +/** + * + */ function createMockRequest(): NextRequest { return { url: "http://localhost:3000/api/tasks/runs", @@ -94,7 +97,11 @@ describe("getTaskRunHandler", () => { describe("list mode", () => { it("returns empty runs array", async () => { - vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ mode: "list", accountId: "acc_123", limit: 20 }); + vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ + mode: "list", + accountId: "acc_123", + limit: 20, + }); vi.mocked(listTaskRuns).mockResolvedValue([]); const response = await getTaskRunHandler(createMockRequest()); @@ -105,7 +112,11 @@ describe("getTaskRunHandler", () => { }); it("returns populated runs array", async () => { - vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ mode: "list", accountId: "acc_123", limit: 20 }); + vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ + mode: "list", + accountId: "acc_123", + limit: 20, + }); vi.mocked(listTaskRuns).mockResolvedValue([mockRun]); const response = await getTaskRunHandler(createMockRequest()); @@ -116,7 +127,11 @@ describe("getTaskRunHandler", () => { }); it("calls listTaskRuns with accountId and limit", async () => { - vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ mode: "list", accountId: "acc_456", limit: 50 }); + vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ + mode: "list", + accountId: "acc_456", + limit: 50, + }); vi.mocked(listTaskRuns).mockResolvedValue([]); await getTaskRunHandler(createMockRequest()); @@ -125,7 +140,11 @@ describe("getTaskRunHandler", () => { }); it("returns 500 when listTaskRuns throws", async () => { - vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ mode: "list", accountId: "acc_123", limit: 20 }); + vi.mocked(validateGetTaskRunQuery).mockResolvedValue({ + mode: "list", + accountId: "acc_123", + limit: 20, + }); vi.mocked(listTaskRuns).mockRejectedValue(new Error("API error")); const response = await getTaskRunHandler(createMockRequest()); diff --git a/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts b/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts index 132e6afc..5e4f72d4 100644 --- a/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts +++ b/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts @@ -14,6 +14,8 @@ vi.mock("@/lib/auth/validateAuthContext", () => ({ /** * Creates a mock NextRequest with the given URL. + * + * @param url */ function createMockRequest(url: string): NextRequest { return { diff --git a/lib/tasks/validateGetTaskRunQuery.ts b/lib/tasks/validateGetTaskRunQuery.ts index b83f4a16..ca59abf0 100644 --- a/lib/tasks/validateGetTaskRunQuery.ts +++ b/lib/tasks/validateGetTaskRunQuery.ts @@ -10,12 +10,7 @@ const getTaskRunQuerySchema = z.object({ .min(1) .transform(val => val.trim()) .optional(), - limit: z.coerce - .number() - .int() - .min(1) - .max(100) - .default(20), + limit: z.coerce.number().int().min(1).max(100).default(20), }); export type ValidatedRetrieveQuery = { mode: "retrieve"; runId: string }; diff --git a/lib/telegram/sendErrorNotification.ts b/lib/telegram/sendErrorNotification.ts index ad9fe2e5..1e6404e7 100644 --- a/lib/telegram/sendErrorNotification.ts +++ b/lib/telegram/sendErrorNotification.ts @@ -18,12 +18,7 @@ export interface ErrorContext { */ function formatErrorMessage(context: ErrorContext): string { const { path, error, roomId, email } = context; - const lines = [ - `*Error in ${path}*`, - "", - `*Error:* ${error.name}`, - `*Message:* ${error.message}`, - ]; + const lines = [`*Error in ${path}*`, "", `*Error:* ${error.name}`, `*Message:* ${error.message}`]; if (roomId) { lines.push(`*Room ID:* ${roomId}`); diff --git a/lib/transcribe/formatTranscriptMd.ts b/lib/transcribe/formatTranscriptMd.ts index dc052de1..77a5b727 100644 --- a/lib/transcribe/formatTranscriptMd.ts +++ b/lib/transcribe/formatTranscriptMd.ts @@ -33,4 +33,3 @@ export function formatTranscriptMd( return md; } - diff --git a/lib/transcribe/index.ts b/lib/transcribe/index.ts index 38430b56..df8cd373 100644 --- a/lib/transcribe/index.ts +++ b/lib/transcribe/index.ts @@ -11,4 +11,3 @@ export { saveAudioToFiles } from "./saveAudioToFiles"; export { saveTranscriptToFiles } from "./saveTranscriptToFiles"; export { processAudioTranscription } from "./processAudioTranscription"; export * from "./types"; - diff --git a/lib/transcribe/processAudioTranscription.ts b/lib/transcribe/processAudioTranscription.ts index 5663be02..0e05905a 100644 --- a/lib/transcribe/processAudioTranscription.ts +++ b/lib/transcribe/processAudioTranscription.ts @@ -7,6 +7,8 @@ import { ProcessTranscriptionParams, ProcessTranscriptionResult } from "./types" /** * Fetches audio from URL, transcribes it with OpenAI Whisper, and saves both * the original audio and transcript markdown to the customer's files. + * + * @param params */ export async function processAudioTranscription( params: ProcessTranscriptionParams, @@ -64,10 +66,13 @@ export async function processAudioTranscription( }; } +/** + * + * @param contentType + */ function getExtensionFromContentType(contentType: string): string { if (contentType.includes("wav")) return "wav"; if (contentType.includes("m4a") || contentType.includes("mp4")) return "m4a"; if (contentType.includes("webm")) return "webm"; return "mp3"; } - diff --git a/lib/transcribe/saveAudioToFiles.ts b/lib/transcribe/saveAudioToFiles.ts index 4c082e61..2124e512 100644 --- a/lib/transcribe/saveAudioToFiles.ts +++ b/lib/transcribe/saveAudioToFiles.ts @@ -2,9 +2,19 @@ import { uploadFileByKey } from "@/lib/supabase/storage/uploadFileByKey"; import { createFileRecord } from "@/lib/supabase/files/createFileRecord"; import { SaveAudioParams, FileRecord } from "./types"; +/** + * + * @param params + */ export async function saveAudioToFiles(params: SaveAudioParams): Promise { - const { audioBlob, contentType, fileName, ownerAccountId, artistAccountId, title = "Audio" } = - params; + const { + audioBlob, + contentType, + fileName, + ownerAccountId, + artistAccountId, + title = "Audio", + } = params; const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_"); const storageKey = `files/${ownerAccountId}/${artistAccountId}/${safeFileName}`; diff --git a/lib/transcribe/saveTranscriptToFiles.ts b/lib/transcribe/saveTranscriptToFiles.ts index 627feb6d..fa7518c5 100644 --- a/lib/transcribe/saveTranscriptToFiles.ts +++ b/lib/transcribe/saveTranscriptToFiles.ts @@ -2,6 +2,10 @@ import { uploadFileByKey } from "@/lib/supabase/storage/uploadFileByKey"; import { createFileRecord } from "@/lib/supabase/files/createFileRecord"; import { SaveTranscriptParams, FileRecord } from "./types"; +/** + * + * @param params + */ export async function saveTranscriptToFiles(params: SaveTranscriptParams): Promise { const { markdown, ownerAccountId, artistAccountId, title = "Transcription" } = params; diff --git a/lib/transcribe/transcribeAudio.ts b/lib/transcribe/transcribeAudio.ts index 2dcf31b6..429eee42 100644 --- a/lib/transcribe/transcribeAudio.ts +++ b/lib/transcribe/transcribeAudio.ts @@ -56,7 +56,7 @@ export async function transcribeAudio( const data: WhisperVerboseResponse = await response.json(); // Map OpenAI segments to our chunk format - const chunks = data.segments?.map((seg) => ({ + const chunks = data.segments?.map(seg => ({ timestamp: [seg.start, seg.end] as [number, number], text: seg.text, })); @@ -67,4 +67,3 @@ export async function transcribeAudio( language: data.language, }; } - diff --git a/lib/transcribe/types.ts b/lib/transcribe/types.ts index 68b134f2..916e699c 100644 --- a/lib/transcribe/types.ts +++ b/lib/transcribe/types.ts @@ -56,6 +56,8 @@ export interface ProcessTranscriptionResult { /** * Formats transcription errors into user-friendly messages. * Centralizes error message logic to avoid duplication. + * + * @param error */ export function formatTranscriptionError(error: unknown): { message: string; status: number } { const rawMessage = error instanceof Error ? error.message : "Transcription failed"; @@ -64,7 +66,10 @@ export function formatTranscriptionError(error: unknown): { message: string; sta return { message: "OpenAI API key is not configured", status: 500 }; } if (rawMessage.includes("fetch audio") || rawMessage.includes("Failed to fetch")) { - return { message: "Could not fetch the audio file. Please check the URL is accessible.", status: 400 }; + return { + message: "Could not fetch the audio file. Please check the URL is accessible.", + status: 400, + }; } if (rawMessage.includes("25 MB") || rawMessage.includes("file size")) { return { message: "Audio file exceeds the 25MB limit", status: 413 }; @@ -75,4 +80,3 @@ export function formatTranscriptionError(error: unknown): { message: string; sta return { message: rawMessage, status: 500 }; } - diff --git a/lib/workspaces/createWorkspacePostHandler.ts b/lib/workspaces/createWorkspacePostHandler.ts index 8acf4894..0a7c7ac6 100644 --- a/lib/workspaces/createWorkspacePostHandler.ts +++ b/lib/workspaces/createWorkspacePostHandler.ts @@ -20,9 +20,7 @@ import { createWorkspaceInDb } from "@/lib/workspaces/createWorkspaceInDb"; * @param request - The request object containing JSON body * @returns A NextResponse with workspace data or error */ -export async function createWorkspacePostHandler( - request: NextRequest, -): Promise { +export async function createWorkspacePostHandler(request: NextRequest): Promise { const validated = await validateCreateWorkspaceBody(request); if (validated instanceof NextResponse) { return validated; @@ -42,10 +40,7 @@ export async function createWorkspacePostHandler( ); } - return NextResponse.json( - { workspace }, - { status: 201, headers: getCorsHeaders() }, - ); + return NextResponse.json({ workspace }, { status: 201, headers: getCorsHeaders() }); } catch (error) { const message = error instanceof Error ? error.message : "Failed to create workspace"; return NextResponse.json( diff --git a/lib/youtube/fetchYouTubeChannelInfo.ts b/lib/youtube/fetchYouTubeChannelInfo.ts index 2ffd976e..15155512 100644 --- a/lib/youtube/fetchYouTubeChannelInfo.ts +++ b/lib/youtube/fetchYouTubeChannelInfo.ts @@ -148,4 +148,3 @@ export async function fetchYouTubeChannelInfo({ }; } } - diff --git a/lib/youtube/getDefaultDateRange.ts b/lib/youtube/getDefaultDateRange.ts index 4e6e92bd..d2f54954 100644 --- a/lib/youtube/getDefaultDateRange.ts +++ b/lib/youtube/getDefaultDateRange.ts @@ -14,4 +14,3 @@ export function getDefaultDateRange(): { startDate: string; endDate: string } { endDate: endDate.toISOString().split("T")[0], }; } - diff --git a/lib/youtube/isTokenExpired.ts b/lib/youtube/isTokenExpired.ts index 81a044a6..d2862913 100644 --- a/lib/youtube/isTokenExpired.ts +++ b/lib/youtube/isTokenExpired.ts @@ -10,4 +10,3 @@ export function isTokenExpired(expiresAt: string): boolean { const oneMinuteInMs = 60 * 1000; return expirationTime <= now + oneMinuteInMs; } - diff --git a/next.config.ts b/next.config.ts index 6880abd5..7160da7f 100644 --- a/next.config.ts +++ b/next.config.ts @@ -5,12 +5,7 @@ const nextConfig: NextConfig = { RESOURCE_WALLET_ADDRESS: process.env.RESOURCE_WALLET_ADDRESS, }, experimental: { - optimizePackageImports: [ - 'date-fns', - '@ai-sdk/anthropic', - '@ai-sdk/openai', - '@ai-sdk/google', - ], + optimizePackageImports: ["date-fns", "@ai-sdk/anthropic", "@ai-sdk/openai", "@ai-sdk/google"], }, async headers() { return [ @@ -19,7 +14,10 @@ const nextConfig: NextConfig = { headers: [ { key: "Access-Control-Allow-Origin", value: "*" }, { key: "Access-Control-Allow-Methods", value: "GET, POST, PUT, DELETE, OPTIONS, PATCH" }, - { key: "Access-Control-Allow-Headers", value: "Content-Type, Authorization, X-Requested-With, x-api-key" }, + { + key: "Access-Control-Allow-Headers", + value: "Content-Type, Authorization, X-Requested-With, x-api-key", + }, ], }, ]; diff --git a/package.json b/package.json index 3a204ccb..e2c8ba47 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "@chat-adapter/github": "^4.15.0", "@chat-adapter/slack": "^4.15.0", "@chat-adapter/state-ioredis": "^4.15.0", - "@chat-adapter/whatsapp": "^4.20.0", "@coinbase/cdp-sdk": "^1.38.6", "@coinbase/x402": "^0.7.3", "@composio/core": "^0.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e51e443..b7c591bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,21 +32,18 @@ importers: '@chat-adapter/state-ioredis': specifier: ^4.15.0 version: 4.15.0 - '@chat-adapter/whatsapp': - specifier: ^4.20.0 - version: 4.20.0 '@coinbase/cdp-sdk': specifier: ^1.38.6 - version: 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@coinbase/x402': specifier: ^0.7.3 - version: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@composio/core': specifier: ^0.3.4 - version: 0.3.4(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + version: 0.3.4(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) '@composio/vercel': specifier: ^0.3.4 - version: 0.3.4(@composio/core@0.3.4(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ai@6.0.0-beta.122(zod@4.1.13)) + version: 0.3.4(@composio/core@0.3.4(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ai@6.0.0-beta.122(zod@4.1.13)) '@modelcontextprotocol/sdk': specifier: ^1.24.3 version: 1.24.3(zod@4.1.13) @@ -73,7 +70,7 @@ importers: version: 1.15.7 autoevals: specifier: ^0.0.129 - version: 0.0.129(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.0.129(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) braintrust: specifier: ^0.4.9 version: 0.4.10(zod@4.1.13) @@ -124,10 +121,10 @@ importers: version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13) x402-fetch: specifier: ^0.7.3 - version: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) x402-next: specifier: ^0.7.3 - version: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: specifier: ^4.1.13 version: 4.1.13 @@ -306,18 +303,12 @@ packages: '@chat-adapter/shared@4.15.0': resolution: {integrity: sha512-otIS9zf0wY3DMc5WGK1sOJiASSeMHcdEqmuxDWW/6tK7gdRE7FOVE+dfo/74ykj9QZwv+a9cByh1cimaeHbiAw==} - '@chat-adapter/shared@4.20.0': - resolution: {integrity: sha512-PaN3TADZUswD5VNV5qMS5M316PP9hp14lGHNa5SB8s7LN5PGXuXUroO0e7rjyo18i7jNKsalJUjpD/uoEg0u3Q==} - '@chat-adapter/slack@4.15.0': resolution: {integrity: sha512-DE2puxMXitt2AVLBvMAldZ4KKUKpKWxSd62nWLDX6xXtOo6z9uXAggZD3FnMOGITqw+oQypdFopvm/Wu6OxeFg==} '@chat-adapter/state-ioredis@4.15.0': resolution: {integrity: sha512-1892j1GCaxwDDMBc7gzUQsbohtf6SArlnr0PYLSIXeSguNl/V9X36WBLbE3ExgjBhyqCBfsDzicGA8e0HIco+A==} - '@chat-adapter/whatsapp@4.20.0': - resolution: {integrity: sha512-B9Z6FN9tP6J4g6PU+jLPK21hLmOsIbpeTZNNdi0d3C1U4gZgJrKA88ppnW5POA0St6qzGlmCpLjqezZpvBdD1A==} - '@coinbase/cdp-sdk@1.38.6': resolution: {integrity: sha512-l9gGGZqhCryuD3nfqB4Y+i8kfBtsnPJoKB5jxx5lKgXhVJw7/BPhgscKkVhP81115Srq3bFegD1IBwUkJ0JFMw==} @@ -3092,9 +3083,6 @@ packages: chat@4.15.0: resolution: {integrity: sha512-fE1m3UEiKQoiYkhXWT0rpH1+ZhKqJGZYkS+1gNCZWNkLv0QWTZhzdzBM5qYWe5A/Y0wJ8yLVjaqvGO4zZFxZqg==} - chat@4.20.0: - resolution: {integrity: sha512-9/SvX0wM1AZa7VyibyVjRkb3WsbmSbIm54i1MMtzFQEj96oAisFdxkRoBKQDZ4R9tfLe7f6SOwWIHqwCGaCxoA==} - check-error@2.1.3: resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} @@ -5435,9 +5423,6 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - remend@1.2.2: - resolution: {integrity: sha512-4ZJgIB9EG9fQE41mOJCRHMmnxDTKHWawQoJWZyUbZuj680wVyogu2ihnj8Edqm7vh2mo/TWHyEZpn2kqeDvS7w==} - request-promise-core@1.1.3: resolution: {integrity: sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==} engines: {node: '>=0.10.0'} @@ -6673,9 +6658,9 @@ snapshots: '@babel/runtime@7.28.4': {} - '@base-org/account@2.4.0(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)': + '@base-org/account@2.4.0(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)': dependencies: - '@coinbase/cdp-sdk': 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@coinbase/cdp-sdk': 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 @@ -6721,12 +6706,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@chat-adapter/shared@4.20.0': - dependencies: - chat: 4.20.0 - transitivePeerDependencies: - - supports-color - '@chat-adapter/slack@4.15.0': dependencies: '@chat-adapter/shared': 4.15.0 @@ -6743,18 +6722,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@chat-adapter/whatsapp@4.20.0': + '@coinbase/cdp-sdk@1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@chat-adapter/shared': 4.20.0 - chat: 4.20.0 - transitivePeerDependencies: - - supports-color - - '@coinbase/cdp-sdk@1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana-program/system': 0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token': 0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana-program/system': 0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token': 0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10) abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) axios: 1.13.2 @@ -6807,11 +6779,11 @@ snapshots: - utf-8-validate - zod - '@coinbase/x402@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@coinbase/x402@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@coinbase/cdp-sdk': 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@coinbase/cdp-sdk': 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + x402: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -6854,13 +6826,13 @@ snapshots: '@composio/client@0.1.0-alpha.52': {} - '@composio/core@0.3.4(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)': + '@composio/core@0.3.4(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)': dependencies: '@composio/client': 0.1.0-alpha.52 '@composio/json-schema-to-zod': 0.1.19(zod@4.1.13) '@types/json-schema': 7.0.15 chalk: 4.1.2 - openai: 5.23.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + openai: 5.23.2(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) pusher-js: 8.4.0 semver: 7.7.3 uuid: 13.0.0 @@ -6873,9 +6845,9 @@ snapshots: dependencies: zod: 4.1.13 - '@composio/vercel@0.3.4(@composio/core@0.3.4(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ai@6.0.0-beta.122(zod@4.1.13))': + '@composio/vercel@0.3.4(@composio/core@0.3.4(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ai@6.0.0-beta.122(zod@4.1.13))': dependencies: - '@composio/core': 0.3.4(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + '@composio/core': 0.3.4(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) ai: 6.0.0-beta.122(zod@4.1.13) '@crawlee/types@3.15.3': @@ -8357,26 +8329,26 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@solana-program/compute-budget@0.11.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/compute-budget@0.11.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/system@0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/system@0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': + '@solana-program/token-2022@0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': dependencies: - '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana-program/token@0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/token@0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token@0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/token@0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/accounts@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: @@ -8613,7 +8585,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -8627,11 +8599,11 @@ snapshots: '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 3.0.3(typescript@5.9.3) '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) - '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) typescript: 5.9.3 @@ -8639,7 +8611,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -8653,11 +8625,11 @@ snapshots: '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-parsed-types': 5.0.0(typescript@5.9.3) '@solana/rpc-spec-types': 5.0.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/signers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/sysvars': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) typescript: 5.9.3 @@ -8807,23 +8779,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@3.0.3(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@3.0.3(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 3.0.3(typescript@5.9.3) '@solana/functional': 3.0.3(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.9.3) '@solana/subscribable': 3.0.3(typescript@5.9.3) typescript: 5.9.3 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 5.0.0(typescript@5.9.3) '@solana/functional': 5.0.0(typescript@5.9.3) '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.3) '@solana/subscribable': 5.0.0(typescript@5.9.3) typescript: 5.9.3 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@solana/rpc-subscriptions-spec@3.0.3(typescript@5.9.3)': dependencies: @@ -8841,7 +8813,7 @@ snapshots: '@solana/subscribable': 5.0.0(typescript@5.9.3) typescript: 5.9.3 - '@solana/rpc-subscriptions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 3.0.3(typescript@5.9.3) '@solana/fast-stable-stringify': 3.0.3(typescript@5.9.3) @@ -8849,7 +8821,7 @@ snapshots: '@solana/promises': 3.0.3(typescript@5.9.3) '@solana/rpc-spec-types': 3.0.3(typescript@5.9.3) '@solana/rpc-subscriptions-api': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 3.0.3(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 3.0.3(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.9.3) '@solana/rpc-transformers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -8859,7 +8831,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 5.0.0(typescript@5.9.3) '@solana/fast-stable-stringify': 5.0.0(typescript@5.9.3) @@ -8867,7 +8839,7 @@ snapshots: '@solana/promises': 5.0.0(typescript@5.9.3) '@solana/rpc-spec-types': 5.0.0(typescript@5.9.3) '@solana/rpc-subscriptions-api': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 5.0.0(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 5.0.0(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.3) '@solana/rpc-transformers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -9027,7 +8999,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -9035,7 +9007,7 @@ snapshots: '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 3.0.3(typescript@5.9.3) '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -9044,7 +9016,7 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -9052,7 +9024,7 @@ snapshots: '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/promises': 5.0.0(typescript@5.9.3) '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) @@ -9575,9 +9547,9 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@wagmi/connectors@6.2.0(f24b5967e73156fd3352de3935505300)': + '@wagmi/connectors@6.2.0(f9b48ec37945e79979f3753b8b12d2d4)': dependencies: - '@base-org/account': 2.4.0(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + '@base-org/account': 2.4.0(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.7)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.76) '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -9586,7 +9558,7 @@ snapshots: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.7)(bufferutil@4.0.9)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13) optionalDependencies: typescript: 5.9.3 @@ -10456,7 +10428,7 @@ snapshots: atomic-sleep@1.0.0: {} - autoevals@0.0.129(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + autoevals@0.0.129(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@braintrust/core': 0.0.87 ajv: 8.17.1 @@ -10465,7 +10437,7 @@ snapshots: js-yaml: 4.1.1 linear-sum-assignment: 1.0.9 mustache: 4.2.0 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 4.104.0(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: @@ -10726,18 +10698,6 @@ snapshots: transitivePeerDependencies: - supports-color - chat@4.20.0: - dependencies: - '@workflow/serde': 4.1.0-beta.2 - mdast-util-to-string: 4.0.0 - remark-gfm: 4.0.1 - remark-parse: 11.0.0 - remark-stringify: 11.0.0 - remend: 1.2.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - check-error@2.1.3: {} cheminfo-types@1.10.0: {} @@ -13043,7 +13003,7 @@ snapshots: dependencies: mimic-fn: 4.0.0 - openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@4.104.0(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): dependencies: '@types/node': 18.19.130 '@types/node-fetch': 2.6.13 @@ -13053,14 +13013,14 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0 optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - encoding - openai@5.23.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): + openai@5.23.2(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 4.1.13 openapi-fetch@0.13.8: @@ -13300,7 +13260,7 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)) hono: 4.10.7 @@ -13314,7 +13274,7 @@ snapshots: '@tanstack/react-query': 5.90.11(react@19.2.1) react: 19.2.1 typescript: 5.9.3 - wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) transitivePeerDependencies: - '@types/react' - immer @@ -13579,8 +13539,6 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 - remend@1.2.2: {} - request-promise-core@1.1.3(request@2.88.2): dependencies: lodash: 4.17.21 @@ -14690,10 +14648,10 @@ snapshots: - tsx - yaml - wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): dependencies: '@tanstack/react-query': 5.90.11(react@19.2.1) - '@wagmi/connectors': 6.2.0(f24b5967e73156fd3352de3935505300) + '@wagmi/connectors': 6.2.0(f9b48ec37945e79979f3753b8b12d2d4) '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.7)(react@19.2.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13)) react: 19.2.1 use-sync-external-store: 1.4.0(react@19.2.1) @@ -14843,10 +14801,10 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - x402-fetch@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + x402-fetch@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + x402: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -14887,13 +14845,13 @@ snapshots: - utf-8-validate - ws - x402-next@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + x402-next@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(next@16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1))(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - '@coinbase/cdp-sdk': 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@coinbase/cdp-sdk': 1.38.6(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) next: 16.0.10(@opentelemetry/api@1.9.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + x402: 0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -14934,20 +14892,20 @@ snapshots: - utf-8-validate - ws - x402@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + x402@0.7.3(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@scure/base': 1.2.6 - '@solana-program/compute-budget': 0.11.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token': 0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) - '@solana-program/token-2022': 0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) - '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana-program/compute-budget': 0.11.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token': 0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token-2022': 0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + '@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': 1.3.0 '@wallet-standard/app': 1.1.0 '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.2.7)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(ioredis@5.8.2)(react@19.2.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' diff --git a/types/database.types.ts b/types/database.types.ts index 27d5db1a..50b0a8ef 100644 --- a/types/database.types.ts +++ b/types/database.types.ts @@ -1,3869 +1,3863 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[] +export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]; export type Database = { // Allows to automatically instantiate createClient with right options // instead of createClient(URL, KEY) __InternalSupabase: { - PostgrestVersion: "12.2.3 (519615d)" - } + PostgrestVersion: "12.2.3 (519615d)"; + }; public: { Tables: { account_api_keys: { Row: { - account: string | null - created_at: string - id: string - key_hash: string | null - last_used: string | null - name: string - } - Insert: { - account?: string | null - created_at?: string - id?: string - key_hash?: string | null - last_used?: string | null - name: string - } - Update: { - account?: string | null - created_at?: string - id?: string - key_hash?: string | null - last_used?: string | null - name?: string - } + account: string | null; + created_at: string; + id: string; + key_hash: string | null; + last_used: string | null; + name: string; + }; + Insert: { + account?: string | null; + created_at?: string; + id?: string; + key_hash?: string | null; + last_used?: string | null; + name: string; + }; + Update: { + account?: string | null; + created_at?: string; + id?: string; + key_hash?: string | null; + last_used?: string | null; + name?: string; + }; Relationships: [ { - foreignKeyName: "account_api_keys_account_fkey" - columns: ["account"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_api_keys_account_fkey"; + columns: ["account"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_artist_ids: { Row: { - account_id: string | null - artist_id: string | null - id: string - pinned: boolean - updated_at: string | null - } - Insert: { - account_id?: string | null - artist_id?: string | null - id?: string - pinned?: boolean - updated_at?: string | null - } - Update: { - account_id?: string | null - artist_id?: string | null - id?: string - pinned?: boolean - updated_at?: string | null - } + account_id: string | null; + artist_id: string | null; + id: string; + pinned: boolean; + updated_at: string | null; + }; + Insert: { + account_id?: string | null; + artist_id?: string | null; + id?: string; + pinned?: boolean; + updated_at?: string | null; + }; + Update: { + account_id?: string | null; + artist_id?: string | null; + id?: string; + pinned?: boolean; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "account_artist_ids_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_artist_ids_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "account_artist_ids_artist_id_fkey" - columns: ["artist_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_artist_ids_artist_id_fkey"; + columns: ["artist_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_catalogs: { Row: { - account: string - catalog: string - created_at: string - id: string - updated_at: string - } - Insert: { - account: string - catalog: string - created_at?: string - id?: string - updated_at?: string - } - Update: { - account?: string - catalog?: string - created_at?: string - id?: string - updated_at?: string - } + account: string; + catalog: string; + created_at: string; + id: string; + updated_at: string; + }; + Insert: { + account: string; + catalog: string; + created_at?: string; + id?: string; + updated_at?: string; + }; + Update: { + account?: string; + catalog?: string; + created_at?: string; + id?: string; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "account_catalogs_account_fkey" - columns: ["account"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_catalogs_account_fkey"; + columns: ["account"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "account_catalogs_catalog_fkey" - columns: ["catalog"] - isOneToOne: false - referencedRelation: "catalogs" - referencedColumns: ["id"] + foreignKeyName: "account_catalogs_catalog_fkey"; + columns: ["catalog"]; + isOneToOne: false; + referencedRelation: "catalogs"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_emails: { Row: { - account_id: string | null - email: string | null - id: string - updated_at: string - } - Insert: { - account_id?: string | null - email?: string | null - id?: string - updated_at?: string - } - Update: { - account_id?: string | null - email?: string | null - id?: string - updated_at?: string - } + account_id: string | null; + email: string | null; + id: string; + updated_at: string; + }; + Insert: { + account_id?: string | null; + email?: string | null; + id?: string; + updated_at?: string; + }; + Update: { + account_id?: string | null; + email?: string | null; + id?: string; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "account_emails_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_emails_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_info: { Row: { - account_id: string | null - company_name: string | null - id: string - image: string | null - instruction: string | null - job_title: string | null - knowledges: Json | null - label: string | null - organization: string | null - role_type: string | null - updated_at: string - } - Insert: { - account_id?: string | null - company_name?: string | null - id?: string - image?: string | null - instruction?: string | null - job_title?: string | null - knowledges?: Json | null - label?: string | null - organization?: string | null - role_type?: string | null - updated_at?: string - } - Update: { - account_id?: string | null - company_name?: string | null - id?: string - image?: string | null - instruction?: string | null - job_title?: string | null - knowledges?: Json | null - label?: string | null - organization?: string | null - role_type?: string | null - updated_at?: string - } + account_id: string | null; + company_name: string | null; + id: string; + image: string | null; + instruction: string | null; + job_title: string | null; + knowledges: Json | null; + label: string | null; + organization: string | null; + role_type: string | null; + updated_at: string; + }; + Insert: { + account_id?: string | null; + company_name?: string | null; + id?: string; + image?: string | null; + instruction?: string | null; + job_title?: string | null; + knowledges?: Json | null; + label?: string | null; + organization?: string | null; + role_type?: string | null; + updated_at?: string; + }; + Update: { + account_id?: string | null; + company_name?: string | null; + id?: string; + image?: string | null; + instruction?: string | null; + job_title?: string | null; + knowledges?: Json | null; + label?: string | null; + organization?: string | null; + role_type?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "account_info_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_info_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_organization_ids: { Row: { - account_id: string | null - id: string - organization_id: string | null - updated_at: string | null - } - Insert: { - account_id?: string | null - id?: string - organization_id?: string | null - updated_at?: string | null - } - Update: { - account_id?: string | null - id?: string - organization_id?: string | null - updated_at?: string | null - } + account_id: string | null; + id: string; + organization_id: string | null; + updated_at: string | null; + }; + Insert: { + account_id?: string | null; + id?: string; + organization_id?: string | null; + updated_at?: string | null; + }; + Update: { + account_id?: string | null; + id?: string; + organization_id?: string | null; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "account_organization_ids_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_organization_ids_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "account_organization_ids_organization_id_fkey" - columns: ["organization_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_organization_ids_organization_id_fkey"; + columns: ["organization_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_phone_numbers: { Row: { - account_id: string - id: string - phone_number: string - updated_at: string | null - } - Insert: { - account_id: string - id?: string - phone_number: string - updated_at?: string | null - } - Update: { - account_id?: string - id?: string - phone_number?: string - updated_at?: string | null - } + account_id: string; + id: string; + phone_number: string; + updated_at: string | null; + }; + Insert: { + account_id: string; + id?: string; + phone_number: string; + updated_at?: string | null; + }; + Update: { + account_id?: string; + id?: string; + phone_number?: string; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "account_phone_numbers_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_phone_numbers_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_sandboxes: { Row: { - account_id: string - created_at: string - id: string - sandbox_id: string - } - Insert: { - account_id: string - created_at?: string - id?: string - sandbox_id: string - } - Update: { - account_id?: string - created_at?: string - id?: string - sandbox_id?: string - } + account_id: string; + created_at: string; + id: string; + sandbox_id: string; + }; + Insert: { + account_id: string; + created_at?: string; + id?: string; + sandbox_id: string; + }; + Update: { + account_id?: string; + created_at?: string; + id?: string; + sandbox_id?: string; + }; Relationships: [ { - foreignKeyName: "account_sandboxes_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_sandboxes_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_snapshots: { Row: { - account_id: string - created_at: string | null - expires_at: string - github_repo: string | null - snapshot_id: string - } - Insert: { - account_id: string - created_at?: string | null - expires_at: string - github_repo?: string | null - snapshot_id: string - } - Update: { - account_id?: string - created_at?: string | null - expires_at?: string - github_repo?: string | null - snapshot_id?: string - } + account_id: string; + created_at: string | null; + expires_at: string; + github_repo: string | null; + snapshot_id: string; + }; + Insert: { + account_id: string; + created_at?: string | null; + expires_at: string; + github_repo?: string | null; + snapshot_id: string; + }; + Update: { + account_id?: string; + created_at?: string | null; + expires_at?: string; + github_repo?: string | null; + snapshot_id?: string; + }; Relationships: [ { - foreignKeyName: "account_snapshots_account_id_fkey" - columns: ["account_id"] - isOneToOne: true - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_snapshots_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: true; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_socials: { Row: { - account_id: string | null - id: string - social_id: string - } + account_id: string | null; + id: string; + social_id: string; + }; Insert: { - account_id?: string | null - id?: string - social_id?: string - } + account_id?: string | null; + id?: string; + social_id?: string; + }; Update: { - account_id?: string | null - id?: string - social_id?: string - } + account_id?: string | null; + id?: string; + social_id?: string; + }; Relationships: [ { - foreignKeyName: "account_socials_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_socials_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "account_socials_social_id_fkey" - columns: ["social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "account_socials_social_id_fkey"; + columns: ["social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_wallets: { Row: { - account_id: string - id: string - updated_at: string | null - wallet: string - } - Insert: { - account_id: string - id?: string - updated_at?: string | null - wallet: string - } - Update: { - account_id?: string - id?: string - updated_at?: string | null - wallet?: string - } + account_id: string; + id: string; + updated_at: string | null; + wallet: string; + }; + Insert: { + account_id: string; + id?: string; + updated_at?: string | null; + wallet: string; + }; + Update: { + account_id?: string; + id?: string; + updated_at?: string | null; + wallet?: string; + }; Relationships: [ { - foreignKeyName: "account_wallets_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_wallets_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; account_workspace_ids: { Row: { - account_id: string | null - id: string - updated_at: string | null - workspace_id: string | null - } - Insert: { - account_id?: string | null - id?: string - updated_at?: string | null - workspace_id?: string | null - } - Update: { - account_id?: string | null - id?: string - updated_at?: string | null - workspace_id?: string | null - } + account_id: string | null; + id: string; + updated_at: string | null; + workspace_id: string | null; + }; + Insert: { + account_id?: string | null; + id?: string; + updated_at?: string | null; + workspace_id?: string | null; + }; + Update: { + account_id?: string | null; + id?: string; + updated_at?: string | null; + workspace_id?: string | null; + }; Relationships: [ { - foreignKeyName: "account_workspace_ids_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_workspace_ids_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "account_workspace_ids_workspace_id_fkey" - columns: ["workspace_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_workspace_ids_workspace_id_fkey"; + columns: ["workspace_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; accounts: { Row: { - id: string - name: string | null - timestamp: number | null - } - Insert: { - id?: string - name?: string | null - timestamp?: number | null - } - Update: { - id?: string - name?: string | null - timestamp?: number | null - } - Relationships: [] - } + id: string; + name: string | null; + timestamp: number | null; + }; + Insert: { + id?: string; + name?: string | null; + timestamp?: number | null; + }; + Update: { + id?: string; + name?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; accounts_memberships: { Row: { - account_id: string - account_role: string - created_at: string - created_by: string | null - updated_at: string - updated_by: string | null - user_id: string - } - Insert: { - account_id: string - account_role: string - created_at?: string - created_by?: string | null - updated_at?: string - updated_by?: string | null - user_id: string - } - Update: { - account_id?: string - account_role?: string - created_at?: string - created_by?: string | null - updated_at?: string - updated_by?: string | null - user_id?: string - } + account_id: string; + account_role: string; + created_at: string; + created_by: string | null; + updated_at: string; + updated_by: string | null; + user_id: string; + }; + Insert: { + account_id: string; + account_role: string; + created_at?: string; + created_by?: string | null; + updated_at?: string; + updated_by?: string | null; + user_id: string; + }; + Update: { + account_id?: string; + account_role?: string; + created_at?: string; + created_by?: string | null; + updated_at?: string; + updated_by?: string | null; + user_id?: string; + }; Relationships: [ { - foreignKeyName: "accounts_memberships_account_role_fkey" - columns: ["account_role"] - isOneToOne: false - referencedRelation: "roles" - referencedColumns: ["name"] + foreignKeyName: "accounts_memberships_account_role_fkey"; + columns: ["account_role"]; + isOneToOne: false; + referencedRelation: "roles"; + referencedColumns: ["name"]; }, - ] - } + ]; + }; admin_expenses: { Row: { - amount: number - category: string - created_at: string | null - created_by: string | null - id: string - is_active: boolean | null - item_name: string - updated_at: string | null - } - Insert: { - amount?: number - category: string - created_at?: string | null - created_by?: string | null - id?: string - is_active?: boolean | null - item_name: string - updated_at?: string | null - } - Update: { - amount?: number - category?: string - created_at?: string | null - created_by?: string | null - id?: string - is_active?: boolean | null - item_name?: string - updated_at?: string | null - } - Relationships: [] - } + amount: number; + category: string; + created_at: string | null; + created_by: string | null; + id: string; + is_active: boolean | null; + item_name: string; + updated_at: string | null; + }; + Insert: { + amount?: number; + category: string; + created_at?: string | null; + created_by?: string | null; + id?: string; + is_active?: boolean | null; + item_name: string; + updated_at?: string | null; + }; + Update: { + amount?: number; + category?: string; + created_at?: string | null; + created_by?: string | null; + id?: string; + is_active?: boolean | null; + item_name?: string; + updated_at?: string | null; + }; + Relationships: []; + }; admin_user_profiles: { Row: { - company: string | null - context_notes: string | null - created_at: string | null - email: string - id: string - job_title: string | null - last_contact_date: string | null - meeting_notes: string | null - observations: string | null - opportunities: string | null - pain_points: string | null - sentiment: string | null - tags: string[] | null - updated_at: string | null - } - Insert: { - company?: string | null - context_notes?: string | null - created_at?: string | null - email: string - id?: string - job_title?: string | null - last_contact_date?: string | null - meeting_notes?: string | null - observations?: string | null - opportunities?: string | null - pain_points?: string | null - sentiment?: string | null - tags?: string[] | null - updated_at?: string | null - } - Update: { - company?: string | null - context_notes?: string | null - created_at?: string | null - email?: string - id?: string - job_title?: string | null - last_contact_date?: string | null - meeting_notes?: string | null - observations?: string | null - opportunities?: string | null - pain_points?: string | null - sentiment?: string | null - tags?: string[] | null - updated_at?: string | null - } - Relationships: [] - } + company: string | null; + context_notes: string | null; + created_at: string | null; + email: string; + id: string; + job_title: string | null; + last_contact_date: string | null; + meeting_notes: string | null; + observations: string | null; + opportunities: string | null; + pain_points: string | null; + sentiment: string | null; + tags: string[] | null; + updated_at: string | null; + }; + Insert: { + company?: string | null; + context_notes?: string | null; + created_at?: string | null; + email: string; + id?: string; + job_title?: string | null; + last_contact_date?: string | null; + meeting_notes?: string | null; + observations?: string | null; + opportunities?: string | null; + pain_points?: string | null; + sentiment?: string | null; + tags?: string[] | null; + updated_at?: string | null; + }; + Update: { + company?: string | null; + context_notes?: string | null; + created_at?: string | null; + email?: string; + id?: string; + job_title?: string | null; + last_contact_date?: string | null; + meeting_notes?: string | null; + observations?: string | null; + opportunities?: string | null; + pain_points?: string | null; + sentiment?: string | null; + tags?: string[] | null; + updated_at?: string | null; + }; + Relationships: []; + }; agent_status: { Row: { - agent_id: string - id: string - progress: number | null - social_id: string - status: number | null - updated_at: string - } - Insert: { - agent_id?: string - id?: string - progress?: number | null - social_id: string - status?: number | null - updated_at?: string - } - Update: { - agent_id?: string - id?: string - progress?: number | null - social_id?: string - status?: number | null - updated_at?: string - } + agent_id: string; + id: string; + progress: number | null; + social_id: string; + status: number | null; + updated_at: string; + }; + Insert: { + agent_id?: string; + id?: string; + progress?: number | null; + social_id: string; + status?: number | null; + updated_at?: string; + }; + Update: { + agent_id?: string; + id?: string; + progress?: number | null; + social_id?: string; + status?: number | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "agent_status_agent_id_fkey" - columns: ["agent_id"] - isOneToOne: false - referencedRelation: "agents" - referencedColumns: ["id"] + foreignKeyName: "agent_status_agent_id_fkey"; + columns: ["agent_id"]; + isOneToOne: false; + referencedRelation: "agents"; + referencedColumns: ["id"]; }, { - foreignKeyName: "agent_status_social_id_fkey" - columns: ["social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "agent_status_social_id_fkey"; + columns: ["social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; agent_template_favorites: { Row: { - created_at: string | null - template_id: string - user_id: string - } + created_at: string | null; + template_id: string; + user_id: string; + }; Insert: { - created_at?: string | null - template_id: string - user_id: string - } + created_at?: string | null; + template_id: string; + user_id: string; + }; Update: { - created_at?: string | null - template_id?: string - user_id?: string - } + created_at?: string | null; + template_id?: string; + user_id?: string; + }; Relationships: [ { - foreignKeyName: "agent_template_favorites_template_id_fkey" - columns: ["template_id"] - isOneToOne: false - referencedRelation: "agent_templates" - referencedColumns: ["id"] + foreignKeyName: "agent_template_favorites_template_id_fkey"; + columns: ["template_id"]; + isOneToOne: false; + referencedRelation: "agent_templates"; + referencedColumns: ["id"]; }, { - foreignKeyName: "agent_template_favorites_user_id_fkey" - columns: ["user_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "agent_template_favorites_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; agent_template_shares: { Row: { - created_at: string | null - template_id: string - user_id: string - } + created_at: string | null; + template_id: string; + user_id: string; + }; Insert: { - created_at?: string | null - template_id: string - user_id: string - } + created_at?: string | null; + template_id: string; + user_id: string; + }; Update: { - created_at?: string | null - template_id?: string - user_id?: string - } + created_at?: string | null; + template_id?: string; + user_id?: string; + }; Relationships: [ { - foreignKeyName: "agent_template_shares_template_id_fkey" - columns: ["template_id"] - isOneToOne: false - referencedRelation: "agent_templates" - referencedColumns: ["id"] + foreignKeyName: "agent_template_shares_template_id_fkey"; + columns: ["template_id"]; + isOneToOne: false; + referencedRelation: "agent_templates"; + referencedColumns: ["id"]; }, { - foreignKeyName: "agent_template_shares_user_id_fkey" - columns: ["user_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "agent_template_shares_user_id_fkey"; + columns: ["user_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; agent_templates: { Row: { - created_at: string - creator: string | null - description: string - favorites_count: number - id: string - is_private: boolean - prompt: string - tags: string[] - title: string - updated_at: string | null - } - Insert: { - created_at?: string - creator?: string | null - description: string - favorites_count?: number - id?: string - is_private?: boolean - prompt: string - tags?: string[] - title: string - updated_at?: string | null - } - Update: { - created_at?: string - creator?: string | null - description?: string - favorites_count?: number - id?: string - is_private?: boolean - prompt?: string - tags?: string[] - title?: string - updated_at?: string | null - } + created_at: string; + creator: string | null; + description: string; + favorites_count: number; + id: string; + is_private: boolean; + prompt: string; + tags: string[]; + title: string; + updated_at: string | null; + }; + Insert: { + created_at?: string; + creator?: string | null; + description: string; + favorites_count?: number; + id?: string; + is_private?: boolean; + prompt: string; + tags?: string[]; + title: string; + updated_at?: string | null; + }; + Update: { + created_at?: string; + creator?: string | null; + description?: string; + favorites_count?: number; + id?: string; + is_private?: boolean; + prompt?: string; + tags?: string[]; + title?: string; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "agent_templates_creator_fkey" - columns: ["creator"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "agent_templates_creator_fkey"; + columns: ["creator"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; agents: { Row: { - id: string - updated_at: string - } + id: string; + updated_at: string; + }; Insert: { - id?: string - updated_at?: string - } + id?: string; + updated_at?: string; + }; Update: { - id?: string - updated_at?: string - } - Relationships: [] - } + id?: string; + updated_at?: string; + }; + Relationships: []; + }; app_store_link_clicked: { Row: { - clientId: string | null - id: string | null - timestamp: number | null - } - Insert: { - clientId?: string | null - id?: string | null - timestamp?: number | null - } - Update: { - clientId?: string | null - id?: string | null - timestamp?: number | null - } - Relationships: [] - } + clientId: string | null; + id: string | null; + timestamp: number | null; + }; + Insert: { + clientId?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Update: { + clientId?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; apple_login_button_clicked: { Row: { - campaignId: string | null - clientId: string | null - fanId: string | null - game: string | null - id: string | null - timestamp: number | null - } - Insert: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string | null - timestamp?: number | null - } - Update: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string | null - timestamp?: number | null - } + campaignId: string | null; + clientId: string | null; + fanId: string | null; + game: string | null; + id: string | null; + timestamp: number | null; + }; + Insert: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Update: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; Relationships: [ { - foreignKeyName: "apple_login_button_clicked_campaignId_fkey" - columns: ["campaignId"] - isOneToOne: false - referencedRelation: "campaigns" - referencedColumns: ["id"] + foreignKeyName: "apple_login_button_clicked_campaignId_fkey"; + columns: ["campaignId"]; + isOneToOne: false; + referencedRelation: "campaigns"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; apple_music: { Row: { - fanId: string | null - game: string | null - id: string | null - syncid: string | null - syncId: string | null - timestamp: number | null - } - Insert: { - fanId?: string | null - game?: string | null - id?: string | null - syncid?: string | null - syncId?: string | null - timestamp?: number | null - } - Update: { - fanId?: string | null - game?: string | null - id?: string | null - syncid?: string | null - syncId?: string | null - timestamp?: number | null - } - Relationships: [] - } + fanId: string | null; + game: string | null; + id: string | null; + syncid: string | null; + syncId: string | null; + timestamp: number | null; + }; + Insert: { + fanId?: string | null; + game?: string | null; + id?: string | null; + syncid?: string | null; + syncId?: string | null; + timestamp?: number | null; + }; + Update: { + fanId?: string | null; + game?: string | null; + id?: string | null; + syncid?: string | null; + syncId?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; apple_play_button_clicked: { Row: { - appleId: string | null - campaignId: string | null - clientId: string | null - fanId: string | null - game: string | null - id: string - timestamp: number | null - } - Insert: { - appleId?: string | null - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string - timestamp?: number | null - } - Update: { - appleId?: string | null - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string - timestamp?: number | null - } + appleId: string | null; + campaignId: string | null; + clientId: string | null; + fanId: string | null; + game: string | null; + id: string; + timestamp: number | null; + }; + Insert: { + appleId?: string | null; + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string; + timestamp?: number | null; + }; + Update: { + appleId?: string | null; + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string; + timestamp?: number | null; + }; Relationships: [ { - foreignKeyName: "apple_play_button_clicked_campaignId_fkey" - columns: ["campaignId"] - isOneToOne: false - referencedRelation: "campaigns" - referencedColumns: ["id"] + foreignKeyName: "apple_play_button_clicked_campaignId_fkey"; + columns: ["campaignId"]; + isOneToOne: false; + referencedRelation: "campaigns"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; artist_fan_segment: { Row: { - artist_social_id: string | null - fan_social_id: string | null - id: string - segment_name: string | null - updated_at: string - } - Insert: { - artist_social_id?: string | null - fan_social_id?: string | null - id?: string - segment_name?: string | null - updated_at?: string - } - Update: { - artist_social_id?: string | null - fan_social_id?: string | null - id?: string - segment_name?: string | null - updated_at?: string - } + artist_social_id: string | null; + fan_social_id: string | null; + id: string; + segment_name: string | null; + updated_at: string; + }; + Insert: { + artist_social_id?: string | null; + fan_social_id?: string | null; + id?: string; + segment_name?: string | null; + updated_at?: string; + }; + Update: { + artist_social_id?: string | null; + fan_social_id?: string | null; + id?: string; + segment_name?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "artist_fan_segment_artist_social_id_fkey" - columns: ["artist_social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "artist_fan_segment_artist_social_id_fkey"; + columns: ["artist_social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, { - foreignKeyName: "artist_fan_segment_fan_social_id_fkey" - columns: ["fan_social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "artist_fan_segment_fan_social_id_fkey"; + columns: ["fan_social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; artist_organization_ids: { Row: { - artist_id: string - created_at: string | null - id: string - organization_id: string - updated_at: string | null - } - Insert: { - artist_id: string - created_at?: string | null - id?: string - organization_id: string - updated_at?: string | null - } - Update: { - artist_id?: string - created_at?: string | null - id?: string - organization_id?: string - updated_at?: string | null - } + artist_id: string; + created_at: string | null; + id: string; + organization_id: string; + updated_at: string | null; + }; + Insert: { + artist_id: string; + created_at?: string | null; + id?: string; + organization_id: string; + updated_at?: string | null; + }; + Update: { + artist_id?: string; + created_at?: string | null; + id?: string; + organization_id?: string; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "artist_organization_ids_artist_id_fkey" - columns: ["artist_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "artist_organization_ids_artist_id_fkey"; + columns: ["artist_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "artist_organization_ids_organization_id_fkey" - columns: ["organization_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "artist_organization_ids_organization_id_fkey"; + columns: ["organization_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; artist_segments: { Row: { - artist_account_id: string - id: string - segment_id: string - updated_at: string | null - } - Insert: { - artist_account_id: string - id?: string - segment_id: string - updated_at?: string | null - } - Update: { - artist_account_id?: string - id?: string - segment_id?: string - updated_at?: string | null - } + artist_account_id: string; + id: string; + segment_id: string; + updated_at: string | null; + }; + Insert: { + artist_account_id: string; + id?: string; + segment_id: string; + updated_at?: string | null; + }; + Update: { + artist_account_id?: string; + id?: string; + segment_id?: string; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "artist_segments_artist_account_id_fkey" - columns: ["artist_account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "artist_segments_artist_account_id_fkey"; + columns: ["artist_account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "artist_segments_segment_id_fkey" - columns: ["segment_id"] - isOneToOne: false - referencedRelation: "segments" - referencedColumns: ["id"] + foreignKeyName: "artist_segments_segment_id_fkey"; + columns: ["segment_id"]; + isOneToOne: false; + referencedRelation: "segments"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; billing_customers: { Row: { - account_id: string - customer_id: string - email: string | null - id: number - provider: Database["public"]["Enums"]["billing_provider"] - } - Insert: { - account_id: string - customer_id: string - email?: string | null - id?: number - provider: Database["public"]["Enums"]["billing_provider"] - } - Update: { - account_id?: string - customer_id?: string - email?: string | null - id?: number - provider?: Database["public"]["Enums"]["billing_provider"] - } - Relationships: [] - } + account_id: string; + customer_id: string; + email: string | null; + id: number; + provider: Database["public"]["Enums"]["billing_provider"]; + }; + Insert: { + account_id: string; + customer_id: string; + email?: string | null; + id?: number; + provider: Database["public"]["Enums"]["billing_provider"]; + }; + Update: { + account_id?: string; + customer_id?: string; + email?: string | null; + id?: number; + provider?: Database["public"]["Enums"]["billing_provider"]; + }; + Relationships: []; + }; campaigns: { Row: { - artist_id: string | null - clientId: string | null - id: string - timestamp: number | null - } - Insert: { - artist_id?: string | null - clientId?: string | null - id?: string - timestamp?: number | null - } - Update: { - artist_id?: string | null - clientId?: string | null - id?: string - timestamp?: number | null - } + artist_id: string | null; + clientId: string | null; + id: string; + timestamp: number | null; + }; + Insert: { + artist_id?: string | null; + clientId?: string | null; + id?: string; + timestamp?: number | null; + }; + Update: { + artist_id?: string | null; + clientId?: string | null; + id?: string; + timestamp?: number | null; + }; Relationships: [ { - foreignKeyName: "campaigns_artist_id_fkey" - columns: ["artist_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "campaigns_artist_id_fkey"; + columns: ["artist_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; catalog_songs: { Row: { - catalog: string - created_at: string - id: string - song: string - updated_at: string - } - Insert: { - catalog: string - created_at?: string - id?: string - song: string - updated_at?: string - } - Update: { - catalog?: string - created_at?: string - id?: string - song?: string - updated_at?: string - } + catalog: string; + created_at: string; + id: string; + song: string; + updated_at: string; + }; + Insert: { + catalog: string; + created_at?: string; + id?: string; + song: string; + updated_at?: string; + }; + Update: { + catalog?: string; + created_at?: string; + id?: string; + song?: string; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "catalog_songs_catalog_fkey" - columns: ["catalog"] - isOneToOne: false - referencedRelation: "catalogs" - referencedColumns: ["id"] + foreignKeyName: "catalog_songs_catalog_fkey"; + columns: ["catalog"]; + isOneToOne: false; + referencedRelation: "catalogs"; + referencedColumns: ["id"]; }, { - foreignKeyName: "catalog_songs_song_fkey" - columns: ["song"] - isOneToOne: false - referencedRelation: "songs" - referencedColumns: ["isrc"] + foreignKeyName: "catalog_songs_song_fkey"; + columns: ["song"]; + isOneToOne: false; + referencedRelation: "songs"; + referencedColumns: ["isrc"]; }, - ] - } + ]; + }; catalogs: { Row: { - created_at: string - id: string - name: string - updated_at: string - } - Insert: { - created_at?: string - id?: string - name: string - updated_at?: string - } - Update: { - created_at?: string - id?: string - name?: string - updated_at?: string - } - Relationships: [] - } + created_at: string; + id: string; + name: string; + updated_at: string; + }; + Insert: { + created_at?: string; + id?: string; + name: string; + updated_at?: string; + }; + Update: { + created_at?: string; + id?: string; + name?: string; + updated_at?: string; + }; + Relationships: []; + }; config: { Row: { - billing_provider: Database["public"]["Enums"]["billing_provider"] - enable_account_billing: boolean - enable_team_account_billing: boolean - enable_team_accounts: boolean - } - Insert: { - billing_provider?: Database["public"]["Enums"]["billing_provider"] - enable_account_billing?: boolean - enable_team_account_billing?: boolean - enable_team_accounts?: boolean - } - Update: { - billing_provider?: Database["public"]["Enums"]["billing_provider"] - enable_account_billing?: boolean - enable_team_account_billing?: boolean - enable_team_accounts?: boolean - } - Relationships: [] - } + billing_provider: Database["public"]["Enums"]["billing_provider"]; + enable_account_billing: boolean; + enable_team_account_billing: boolean; + enable_team_accounts: boolean; + }; + Insert: { + billing_provider?: Database["public"]["Enums"]["billing_provider"]; + enable_account_billing?: boolean; + enable_team_account_billing?: boolean; + enable_team_accounts?: boolean; + }; + Update: { + billing_provider?: Database["public"]["Enums"]["billing_provider"]; + enable_account_billing?: boolean; + enable_team_account_billing?: boolean; + enable_team_accounts?: boolean; + }; + Relationships: []; + }; cookie_players: { Row: { - game: string | null - id: string | null - timestamp: number | null - uniquePlayerID: string | null - } - Insert: { - game?: string | null - id?: string | null - timestamp?: number | null - uniquePlayerID?: string | null - } - Update: { - game?: string | null - id?: string | null - timestamp?: number | null - uniquePlayerID?: string | null - } - Relationships: [] - } + game: string | null; + id: string | null; + timestamp: number | null; + uniquePlayerID: string | null; + }; + Insert: { + game?: string | null; + id?: string | null; + timestamp?: number | null; + uniquePlayerID?: string | null; + }; + Update: { + game?: string | null; + id?: string | null; + timestamp?: number | null; + uniquePlayerID?: string | null; + }; + Relationships: []; + }; credits_usage: { Row: { - account_id: string - id: number - remaining_credits: number - timestamp: string | null - } - Insert: { - account_id: string - id?: number - remaining_credits?: number - timestamp?: string | null - } - Update: { - account_id?: string - id?: number - remaining_credits?: number - timestamp?: string | null - } + account_id: string; + id: number; + remaining_credits: number; + timestamp: string | null; + }; + Insert: { + account_id: string; + id?: number; + remaining_credits?: number; + timestamp?: string | null; + }; + Update: { + account_id?: string; + id?: number; + remaining_credits?: number; + timestamp?: string | null; + }; Relationships: [ { - foreignKeyName: "credits_usage_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "credits_usage_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; cta_redirect: { Row: { - clientId: string - id: number - timestamp: string | null - url: string | null - } - Insert: { - clientId: string - id?: number - timestamp?: string | null - url?: string | null - } - Update: { - clientId?: string - id?: number - timestamp?: string | null - url?: string | null - } - Relationships: [] - } + clientId: string; + id: number; + timestamp: string | null; + url: string | null; + }; + Insert: { + clientId: string; + id?: number; + timestamp?: string | null; + url?: string | null; + }; + Update: { + clientId?: string; + id?: number; + timestamp?: string | null; + url?: string | null; + }; + Relationships: []; + }; error_logs: { Row: { - account_id: string | null - created_at: string - error_message: string | null - error_timestamp: string | null - error_type: string | null - id: string - last_message: string | null - raw_message: string - room_id: string | null - stack_trace: string | null - telegram_message_id: number | null - tool_name: string | null - } - Insert: { - account_id?: string | null - created_at?: string - error_message?: string | null - error_timestamp?: string | null - error_type?: string | null - id?: string - last_message?: string | null - raw_message: string - room_id?: string | null - stack_trace?: string | null - telegram_message_id?: number | null - tool_name?: string | null - } - Update: { - account_id?: string | null - created_at?: string - error_message?: string | null - error_timestamp?: string | null - error_type?: string | null - id?: string - last_message?: string | null - raw_message?: string - room_id?: string | null - stack_trace?: string | null - telegram_message_id?: number | null - tool_name?: string | null - } + account_id: string | null; + created_at: string; + error_message: string | null; + error_timestamp: string | null; + error_type: string | null; + id: string; + last_message: string | null; + raw_message: string; + room_id: string | null; + stack_trace: string | null; + telegram_message_id: number | null; + tool_name: string | null; + }; + Insert: { + account_id?: string | null; + created_at?: string; + error_message?: string | null; + error_timestamp?: string | null; + error_type?: string | null; + id?: string; + last_message?: string | null; + raw_message: string; + room_id?: string | null; + stack_trace?: string | null; + telegram_message_id?: number | null; + tool_name?: string | null; + }; + Update: { + account_id?: string | null; + created_at?: string; + error_message?: string | null; + error_timestamp?: string | null; + error_type?: string | null; + id?: string; + last_message?: string | null; + raw_message?: string; + room_id?: string | null; + stack_trace?: string | null; + telegram_message_id?: number | null; + tool_name?: string | null; + }; Relationships: [ { - foreignKeyName: "error_logs_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "error_logs_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "error_logs_room_id_fkey" - columns: ["room_id"] - isOneToOne: false - referencedRelation: "rooms" - referencedColumns: ["id"] + foreignKeyName: "error_logs_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; fan_segments: { Row: { - fan_social_id: string - id: string - segment_id: string - updated_at: string | null - } - Insert: { - fan_social_id: string - id?: string - segment_id: string - updated_at?: string | null - } - Update: { - fan_social_id?: string - id?: string - segment_id?: string - updated_at?: string | null - } + fan_social_id: string; + id: string; + segment_id: string; + updated_at: string | null; + }; + Insert: { + fan_social_id: string; + id?: string; + segment_id: string; + updated_at?: string | null; + }; + Update: { + fan_social_id?: string; + id?: string; + segment_id?: string; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "fan_segments_fan_social_id_fkey" - columns: ["fan_social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "fan_segments_fan_social_id_fkey"; + columns: ["fan_social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, { - foreignKeyName: "fan_segments_segment_id_fkey" - columns: ["segment_id"] - isOneToOne: false - referencedRelation: "segments" - referencedColumns: ["id"] + foreignKeyName: "fan_segments_segment_id_fkey"; + columns: ["segment_id"]; + isOneToOne: false; + referencedRelation: "segments"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; fans: { Row: { - account_status: string | null - apple_token: string | null - campaign_id: string | null - campaign_interaction_count: number | null - campaignId: string | null - city: string | null - click_through_rate: number | null - clientId: string | null - consent_given: boolean | null - country: string | null - custom_tags: Json | null - discord_username: string | null - display_name: string | null - email: string | null - email_open_rate: number | null - engagement_level: string | null - episodes: Json | null - explicit_content_filter_enabled: boolean | null - explicit_content_filter_locked: boolean | null - "explicit_content.filter_enabled": boolean | null - "explicit_content.filter_locked": boolean | null - external_urls_spotify: string | null - "external_urls.spotify": string | null - facebook_profile_url: string | null - first_stream_date: string | null - followedArtists: Json | null - followers_total: number | null - "followers.href": string | null - "followers.total": number | null - gamification_points: number | null - genres: Json | null - heavyRotations: Json | null - href: string | null - id: string - images: Json | null - instagram_handle: string | null - last_campaign_interaction: string | null - last_login: string | null - last_purchase_date: string | null - last_stream_date: string | null - linkedin_profile_url: string | null - os_type: string | null - playlist: Json | null - preferences: Json | null - preferred_artists: Json | null - preferred_device: string | null - product: string | null - recentlyPlayed: Json | null - recommendations: Json | null - recommended_events: Json | null - reddit_username: string | null - saved_podcasts: Json | null - savedAlbums: Json | null - savedAudioBooks: Json | null - savedShows: Json | null - savedTracks: Json | null - social_shares: number | null - spotify_token: string | null - subscription_tier: string | null - testField: string | null - tiktok_handle: string | null - time_zone: string | null - timestamp: string | null - top_artists_long_term: Json | null - top_artists_medium_term: Json | null - top_tracks_long_term: Json | null - top_tracks_medium_term: Json | null - top_tracks_short_term: Json | null - topArtists: Json | null - topTracks: Json | null - total_spent: number | null - total_streams: number | null - twitter_handle: string | null - type: string | null - uri: string | null - youtube_channel_url: string | null - } - Insert: { - account_status?: string | null - apple_token?: string | null - campaign_id?: string | null - campaign_interaction_count?: number | null - campaignId?: string | null - city?: string | null - click_through_rate?: number | null - clientId?: string | null - consent_given?: boolean | null - country?: string | null - custom_tags?: Json | null - discord_username?: string | null - display_name?: string | null - email?: string | null - email_open_rate?: number | null - engagement_level?: string | null - episodes?: Json | null - explicit_content_filter_enabled?: boolean | null - explicit_content_filter_locked?: boolean | null - "explicit_content.filter_enabled"?: boolean | null - "explicit_content.filter_locked"?: boolean | null - external_urls_spotify?: string | null - "external_urls.spotify"?: string | null - facebook_profile_url?: string | null - first_stream_date?: string | null - followedArtists?: Json | null - followers_total?: number | null - "followers.href"?: string | null - "followers.total"?: number | null - gamification_points?: number | null - genres?: Json | null - heavyRotations?: Json | null - href?: string | null - id?: string - images?: Json | null - instagram_handle?: string | null - last_campaign_interaction?: string | null - last_login?: string | null - last_purchase_date?: string | null - last_stream_date?: string | null - linkedin_profile_url?: string | null - os_type?: string | null - playlist?: Json | null - preferences?: Json | null - preferred_artists?: Json | null - preferred_device?: string | null - product?: string | null - recentlyPlayed?: Json | null - recommendations?: Json | null - recommended_events?: Json | null - reddit_username?: string | null - saved_podcasts?: Json | null - savedAlbums?: Json | null - savedAudioBooks?: Json | null - savedShows?: Json | null - savedTracks?: Json | null - social_shares?: number | null - spotify_token?: string | null - subscription_tier?: string | null - testField?: string | null - tiktok_handle?: string | null - time_zone?: string | null - timestamp?: string | null - top_artists_long_term?: Json | null - top_artists_medium_term?: Json | null - top_tracks_long_term?: Json | null - top_tracks_medium_term?: Json | null - top_tracks_short_term?: Json | null - topArtists?: Json | null - topTracks?: Json | null - total_spent?: number | null - total_streams?: number | null - twitter_handle?: string | null - type?: string | null - uri?: string | null - youtube_channel_url?: string | null - } - Update: { - account_status?: string | null - apple_token?: string | null - campaign_id?: string | null - campaign_interaction_count?: number | null - campaignId?: string | null - city?: string | null - click_through_rate?: number | null - clientId?: string | null - consent_given?: boolean | null - country?: string | null - custom_tags?: Json | null - discord_username?: string | null - display_name?: string | null - email?: string | null - email_open_rate?: number | null - engagement_level?: string | null - episodes?: Json | null - explicit_content_filter_enabled?: boolean | null - explicit_content_filter_locked?: boolean | null - "explicit_content.filter_enabled"?: boolean | null - "explicit_content.filter_locked"?: boolean | null - external_urls_spotify?: string | null - "external_urls.spotify"?: string | null - facebook_profile_url?: string | null - first_stream_date?: string | null - followedArtists?: Json | null - followers_total?: number | null - "followers.href"?: string | null - "followers.total"?: number | null - gamification_points?: number | null - genres?: Json | null - heavyRotations?: Json | null - href?: string | null - id?: string - images?: Json | null - instagram_handle?: string | null - last_campaign_interaction?: string | null - last_login?: string | null - last_purchase_date?: string | null - last_stream_date?: string | null - linkedin_profile_url?: string | null - os_type?: string | null - playlist?: Json | null - preferences?: Json | null - preferred_artists?: Json | null - preferred_device?: string | null - product?: string | null - recentlyPlayed?: Json | null - recommendations?: Json | null - recommended_events?: Json | null - reddit_username?: string | null - saved_podcasts?: Json | null - savedAlbums?: Json | null - savedAudioBooks?: Json | null - savedShows?: Json | null - savedTracks?: Json | null - social_shares?: number | null - spotify_token?: string | null - subscription_tier?: string | null - testField?: string | null - tiktok_handle?: string | null - time_zone?: string | null - timestamp?: string | null - top_artists_long_term?: Json | null - top_artists_medium_term?: Json | null - top_tracks_long_term?: Json | null - top_tracks_medium_term?: Json | null - top_tracks_short_term?: Json | null - topArtists?: Json | null - topTracks?: Json | null - total_spent?: number | null - total_streams?: number | null - twitter_handle?: string | null - type?: string | null - uri?: string | null - youtube_channel_url?: string | null - } + account_status: string | null; + apple_token: string | null; + campaign_id: string | null; + campaign_interaction_count: number | null; + campaignId: string | null; + city: string | null; + click_through_rate: number | null; + clientId: string | null; + consent_given: boolean | null; + country: string | null; + custom_tags: Json | null; + discord_username: string | null; + display_name: string | null; + email: string | null; + email_open_rate: number | null; + engagement_level: string | null; + episodes: Json | null; + explicit_content_filter_enabled: boolean | null; + explicit_content_filter_locked: boolean | null; + "explicit_content.filter_enabled": boolean | null; + "explicit_content.filter_locked": boolean | null; + external_urls_spotify: string | null; + "external_urls.spotify": string | null; + facebook_profile_url: string | null; + first_stream_date: string | null; + followedArtists: Json | null; + followers_total: number | null; + "followers.href": string | null; + "followers.total": number | null; + gamification_points: number | null; + genres: Json | null; + heavyRotations: Json | null; + href: string | null; + id: string; + images: Json | null; + instagram_handle: string | null; + last_campaign_interaction: string | null; + last_login: string | null; + last_purchase_date: string | null; + last_stream_date: string | null; + linkedin_profile_url: string | null; + os_type: string | null; + playlist: Json | null; + preferences: Json | null; + preferred_artists: Json | null; + preferred_device: string | null; + product: string | null; + recentlyPlayed: Json | null; + recommendations: Json | null; + recommended_events: Json | null; + reddit_username: string | null; + saved_podcasts: Json | null; + savedAlbums: Json | null; + savedAudioBooks: Json | null; + savedShows: Json | null; + savedTracks: Json | null; + social_shares: number | null; + spotify_token: string | null; + subscription_tier: string | null; + testField: string | null; + tiktok_handle: string | null; + time_zone: string | null; + timestamp: string | null; + top_artists_long_term: Json | null; + top_artists_medium_term: Json | null; + top_tracks_long_term: Json | null; + top_tracks_medium_term: Json | null; + top_tracks_short_term: Json | null; + topArtists: Json | null; + topTracks: Json | null; + total_spent: number | null; + total_streams: number | null; + twitter_handle: string | null; + type: string | null; + uri: string | null; + youtube_channel_url: string | null; + }; + Insert: { + account_status?: string | null; + apple_token?: string | null; + campaign_id?: string | null; + campaign_interaction_count?: number | null; + campaignId?: string | null; + city?: string | null; + click_through_rate?: number | null; + clientId?: string | null; + consent_given?: boolean | null; + country?: string | null; + custom_tags?: Json | null; + discord_username?: string | null; + display_name?: string | null; + email?: string | null; + email_open_rate?: number | null; + engagement_level?: string | null; + episodes?: Json | null; + explicit_content_filter_enabled?: boolean | null; + explicit_content_filter_locked?: boolean | null; + "explicit_content.filter_enabled"?: boolean | null; + "explicit_content.filter_locked"?: boolean | null; + external_urls_spotify?: string | null; + "external_urls.spotify"?: string | null; + facebook_profile_url?: string | null; + first_stream_date?: string | null; + followedArtists?: Json | null; + followers_total?: number | null; + "followers.href"?: string | null; + "followers.total"?: number | null; + gamification_points?: number | null; + genres?: Json | null; + heavyRotations?: Json | null; + href?: string | null; + id?: string; + images?: Json | null; + instagram_handle?: string | null; + last_campaign_interaction?: string | null; + last_login?: string | null; + last_purchase_date?: string | null; + last_stream_date?: string | null; + linkedin_profile_url?: string | null; + os_type?: string | null; + playlist?: Json | null; + preferences?: Json | null; + preferred_artists?: Json | null; + preferred_device?: string | null; + product?: string | null; + recentlyPlayed?: Json | null; + recommendations?: Json | null; + recommended_events?: Json | null; + reddit_username?: string | null; + saved_podcasts?: Json | null; + savedAlbums?: Json | null; + savedAudioBooks?: Json | null; + savedShows?: Json | null; + savedTracks?: Json | null; + social_shares?: number | null; + spotify_token?: string | null; + subscription_tier?: string | null; + testField?: string | null; + tiktok_handle?: string | null; + time_zone?: string | null; + timestamp?: string | null; + top_artists_long_term?: Json | null; + top_artists_medium_term?: Json | null; + top_tracks_long_term?: Json | null; + top_tracks_medium_term?: Json | null; + top_tracks_short_term?: Json | null; + topArtists?: Json | null; + topTracks?: Json | null; + total_spent?: number | null; + total_streams?: number | null; + twitter_handle?: string | null; + type?: string | null; + uri?: string | null; + youtube_channel_url?: string | null; + }; + Update: { + account_status?: string | null; + apple_token?: string | null; + campaign_id?: string | null; + campaign_interaction_count?: number | null; + campaignId?: string | null; + city?: string | null; + click_through_rate?: number | null; + clientId?: string | null; + consent_given?: boolean | null; + country?: string | null; + custom_tags?: Json | null; + discord_username?: string | null; + display_name?: string | null; + email?: string | null; + email_open_rate?: number | null; + engagement_level?: string | null; + episodes?: Json | null; + explicit_content_filter_enabled?: boolean | null; + explicit_content_filter_locked?: boolean | null; + "explicit_content.filter_enabled"?: boolean | null; + "explicit_content.filter_locked"?: boolean | null; + external_urls_spotify?: string | null; + "external_urls.spotify"?: string | null; + facebook_profile_url?: string | null; + first_stream_date?: string | null; + followedArtists?: Json | null; + followers_total?: number | null; + "followers.href"?: string | null; + "followers.total"?: number | null; + gamification_points?: number | null; + genres?: Json | null; + heavyRotations?: Json | null; + href?: string | null; + id?: string; + images?: Json | null; + instagram_handle?: string | null; + last_campaign_interaction?: string | null; + last_login?: string | null; + last_purchase_date?: string | null; + last_stream_date?: string | null; + linkedin_profile_url?: string | null; + os_type?: string | null; + playlist?: Json | null; + preferences?: Json | null; + preferred_artists?: Json | null; + preferred_device?: string | null; + product?: string | null; + recentlyPlayed?: Json | null; + recommendations?: Json | null; + recommended_events?: Json | null; + reddit_username?: string | null; + saved_podcasts?: Json | null; + savedAlbums?: Json | null; + savedAudioBooks?: Json | null; + savedShows?: Json | null; + savedTracks?: Json | null; + social_shares?: number | null; + spotify_token?: string | null; + subscription_tier?: string | null; + testField?: string | null; + tiktok_handle?: string | null; + time_zone?: string | null; + timestamp?: string | null; + top_artists_long_term?: Json | null; + top_artists_medium_term?: Json | null; + top_tracks_long_term?: Json | null; + top_tracks_medium_term?: Json | null; + top_tracks_short_term?: Json | null; + topArtists?: Json | null; + topTracks?: Json | null; + total_spent?: number | null; + total_streams?: number | null; + twitter_handle?: string | null; + type?: string | null; + uri?: string | null; + youtube_channel_url?: string | null; + }; Relationships: [ { - foreignKeyName: "fans_campaignId_fkey" - columns: ["campaignId"] - isOneToOne: false - referencedRelation: "campaigns" - referencedColumns: ["id"] + foreignKeyName: "fans_campaignId_fkey"; + columns: ["campaignId"]; + isOneToOne: false; + referencedRelation: "campaigns"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; files: { Row: { - artist_account_id: string - created_at: string - description: string | null - file_name: string - id: string - is_directory: boolean - mime_type: string | null - owner_account_id: string - size_bytes: number | null - storage_key: string - tags: string[] | null - updated_at: string - } - Insert: { - artist_account_id: string - created_at?: string - description?: string | null - file_name: string - id?: string - is_directory?: boolean - mime_type?: string | null - owner_account_id: string - size_bytes?: number | null - storage_key: string - tags?: string[] | null - updated_at?: string - } - Update: { - artist_account_id?: string - created_at?: string - description?: string | null - file_name?: string - id?: string - is_directory?: boolean - mime_type?: string | null - owner_account_id?: string - size_bytes?: number | null - storage_key?: string - tags?: string[] | null - updated_at?: string - } + artist_account_id: string; + created_at: string; + description: string | null; + file_name: string; + id: string; + is_directory: boolean; + mime_type: string | null; + owner_account_id: string; + size_bytes: number | null; + storage_key: string; + tags: string[] | null; + updated_at: string; + }; + Insert: { + artist_account_id: string; + created_at?: string; + description?: string | null; + file_name: string; + id?: string; + is_directory?: boolean; + mime_type?: string | null; + owner_account_id: string; + size_bytes?: number | null; + storage_key: string; + tags?: string[] | null; + updated_at?: string; + }; + Update: { + artist_account_id?: string; + created_at?: string; + description?: string | null; + file_name?: string; + id?: string; + is_directory?: boolean; + mime_type?: string | null; + owner_account_id?: string; + size_bytes?: number | null; + storage_key?: string; + tags?: string[] | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "files_artist_account_id_fkey" - columns: ["artist_account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "files_artist_account_id_fkey"; + columns: ["artist_account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "files_owner_account_id_fkey" - columns: ["owner_account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "files_owner_account_id_fkey"; + columns: ["owner_account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; follows: { Row: { - game: string | null - id: string | null - timestamp: number | null - } - Insert: { - game?: string | null - id?: string | null - timestamp?: number | null - } - Update: { - game?: string | null - id?: string | null - timestamp?: number | null - } - Relationships: [] - } + game: string | null; + id: string | null; + timestamp: number | null; + }; + Insert: { + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Update: { + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; founder_dashboard_chart_annotations: { Row: { - chart_type: string | null - created_at: string | null - event_date: string - event_description: string | null - id: string - } - Insert: { - chart_type?: string | null - created_at?: string | null - event_date: string - event_description?: string | null - id?: string - } - Update: { - chart_type?: string | null - created_at?: string | null - event_date?: string - event_description?: string | null - id?: string - } - Relationships: [] - } + chart_type: string | null; + created_at: string | null; + event_date: string; + event_description: string | null; + id: string; + }; + Insert: { + chart_type?: string | null; + created_at?: string | null; + event_date: string; + event_description?: string | null; + id?: string; + }; + Update: { + chart_type?: string | null; + created_at?: string | null; + event_date?: string; + event_description?: string | null; + id?: string; + }; + Relationships: []; + }; funnel_analytics: { Row: { - artist_id: string | null - handle: string | null - id: string - pilot_id: string | null - status: number | null - type: Database["public"]["Enums"]["social_type"] | null - updated_at: string - } - Insert: { - artist_id?: string | null - handle?: string | null - id?: string - pilot_id?: string | null - status?: number | null - type?: Database["public"]["Enums"]["social_type"] | null - updated_at?: string - } - Update: { - artist_id?: string | null - handle?: string | null - id?: string - pilot_id?: string | null - status?: number | null - type?: Database["public"]["Enums"]["social_type"] | null - updated_at?: string - } + artist_id: string | null; + handle: string | null; + id: string; + pilot_id: string | null; + status: number | null; + type: Database["public"]["Enums"]["social_type"] | null; + updated_at: string; + }; + Insert: { + artist_id?: string | null; + handle?: string | null; + id?: string; + pilot_id?: string | null; + status?: number | null; + type?: Database["public"]["Enums"]["social_type"] | null; + updated_at?: string; + }; + Update: { + artist_id?: string | null; + handle?: string | null; + id?: string; + pilot_id?: string | null; + status?: number | null; + type?: Database["public"]["Enums"]["social_type"] | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "funnel_analytics_artist_id_fkey" - columns: ["artist_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "funnel_analytics_artist_id_fkey"; + columns: ["artist_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; funnel_analytics_accounts: { Row: { - account_id: string | null - analysis_id: string | null - created_at: string - id: string - updated_at: string | null - } - Insert: { - account_id?: string | null - analysis_id?: string | null - created_at?: string - id?: string - updated_at?: string | null - } - Update: { - account_id?: string | null - analysis_id?: string | null - created_at?: string - id?: string - updated_at?: string | null - } + account_id: string | null; + analysis_id: string | null; + created_at: string; + id: string; + updated_at: string | null; + }; + Insert: { + account_id?: string | null; + analysis_id?: string | null; + created_at?: string; + id?: string; + updated_at?: string | null; + }; + Update: { + account_id?: string | null; + analysis_id?: string | null; + created_at?: string; + id?: string; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "account_funnel_analytics_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "account_funnel_analytics_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "account_funnel_analytics_analysis_id_fkey" - columns: ["analysis_id"] - isOneToOne: false - referencedRelation: "funnel_analytics" - referencedColumns: ["id"] + foreignKeyName: "account_funnel_analytics_analysis_id_fkey"; + columns: ["analysis_id"]; + isOneToOne: false; + referencedRelation: "funnel_analytics"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; funnel_analytics_segments: { Row: { - analysis_id: string | null - created_at: string - icon: string | null - id: string - name: string | null - size: number | null - } - Insert: { - analysis_id?: string | null - created_at?: string - icon?: string | null - id?: string - name?: string | null - size?: number | null - } - Update: { - analysis_id?: string | null - created_at?: string - icon?: string | null - id?: string - name?: string | null - size?: number | null - } + analysis_id: string | null; + created_at: string; + icon: string | null; + id: string; + name: string | null; + size: number | null; + }; + Insert: { + analysis_id?: string | null; + created_at?: string; + icon?: string | null; + id?: string; + name?: string | null; + size?: number | null; + }; + Update: { + analysis_id?: string | null; + created_at?: string; + icon?: string | null; + id?: string; + name?: string | null; + size?: number | null; + }; Relationships: [ { - foreignKeyName: "funnel_analytics_segments_analysis_id_fkey" - columns: ["analysis_id"] - isOneToOne: false - referencedRelation: "funnel_analytics" - referencedColumns: ["id"] + foreignKeyName: "funnel_analytics_segments_analysis_id_fkey"; + columns: ["analysis_id"]; + isOneToOne: false; + referencedRelation: "funnel_analytics"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; funnel_reports: { Row: { - id: string - next_steps: string | null - report: string | null - stack_unique_id: string | null - timestamp: string - type: Database["public"]["Enums"]["social_type"] | null - } - Insert: { - id?: string - next_steps?: string | null - report?: string | null - stack_unique_id?: string | null - timestamp?: string - type?: Database["public"]["Enums"]["social_type"] | null - } - Update: { - id?: string - next_steps?: string | null - report?: string | null - stack_unique_id?: string | null - timestamp?: string - type?: Database["public"]["Enums"]["social_type"] | null - } - Relationships: [] - } + id: string; + next_steps: string | null; + report: string | null; + stack_unique_id: string | null; + timestamp: string; + type: Database["public"]["Enums"]["social_type"] | null; + }; + Insert: { + id?: string; + next_steps?: string | null; + report?: string | null; + stack_unique_id?: string | null; + timestamp?: string; + type?: Database["public"]["Enums"]["social_type"] | null; + }; + Update: { + id?: string; + next_steps?: string | null; + report?: string | null; + stack_unique_id?: string | null; + timestamp?: string; + type?: Database["public"]["Enums"]["social_type"] | null; + }; + Relationships: []; + }; game_start: { Row: { - clientId: string | null - fanId: Json | null - game: string | null - id: string | null - timestamp: number | null - } - Insert: { - clientId?: string | null - fanId?: Json | null - game?: string | null - id?: string | null - timestamp?: number | null - } - Update: { - clientId?: string | null - fanId?: Json | null - game?: string | null - id?: string | null - timestamp?: number | null - } - Relationships: [] - } + clientId: string | null; + fanId: Json | null; + game: string | null; + id: string | null; + timestamp: number | null; + }; + Insert: { + clientId?: string | null; + fanId?: Json | null; + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Update: { + clientId?: string | null; + fanId?: Json | null; + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; invitations: { Row: { - account_id: string - created_at: string - email: string - expires_at: string - id: number - invite_token: string - invited_by: string - role: string - updated_at: string - } - Insert: { - account_id: string - created_at?: string - email: string - expires_at?: string - id?: number - invite_token: string - invited_by: string - role: string - updated_at?: string - } - Update: { - account_id?: string - created_at?: string - email?: string - expires_at?: string - id?: number - invite_token?: string - invited_by?: string - role?: string - updated_at?: string - } + account_id: string; + created_at: string; + email: string; + expires_at: string; + id: number; + invite_token: string; + invited_by: string; + role: string; + updated_at: string; + }; + Insert: { + account_id: string; + created_at?: string; + email: string; + expires_at?: string; + id?: number; + invite_token: string; + invited_by: string; + role: string; + updated_at?: string; + }; + Update: { + account_id?: string; + created_at?: string; + email?: string; + expires_at?: string; + id?: number; + invite_token?: string; + invited_by?: string; + role?: string; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "invitations_role_fkey" - columns: ["role"] - isOneToOne: false - referencedRelation: "roles" - referencedColumns: ["name"] + foreignKeyName: "invitations_role_fkey"; + columns: ["role"]; + isOneToOne: false; + referencedRelation: "roles"; + referencedColumns: ["name"]; }, - ] - } + ]; + }; ios_redirect: { Row: { - clientId: string | null - fanId: string | null - id: string | null - timestamp: number | null - } - Insert: { - clientId?: string | null - fanId?: string | null - id?: string | null - timestamp?: number | null - } - Update: { - clientId?: string | null - fanId?: string | null - id?: string | null - timestamp?: number | null - } - Relationships: [] - } + clientId: string | null; + fanId: string | null; + id: string | null; + timestamp: number | null; + }; + Insert: { + clientId?: string | null; + fanId?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Update: { + clientId?: string | null; + fanId?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; leaderboard: { Row: { - id: string | null - Name: string | null - Number: string | null - Score: string | null - Spotify: string | null - "Time._nanoseconds": string | null - "Time._seconds": string | null - } - Insert: { - id?: string | null - Name?: string | null - Number?: string | null - Score?: string | null - Spotify?: string | null - "Time._nanoseconds"?: string | null - "Time._seconds"?: string | null - } - Update: { - id?: string | null - Name?: string | null - Number?: string | null - Score?: string | null - Spotify?: string | null - "Time._nanoseconds"?: string | null - "Time._seconds"?: string | null - } - Relationships: [] - } + id: string | null; + Name: string | null; + Number: string | null; + Score: string | null; + Spotify: string | null; + "Time._nanoseconds": string | null; + "Time._seconds": string | null; + }; + Insert: { + id?: string | null; + Name?: string | null; + Number?: string | null; + Score?: string | null; + Spotify?: string | null; + "Time._nanoseconds"?: string | null; + "Time._seconds"?: string | null; + }; + Update: { + id?: string | null; + Name?: string | null; + Number?: string | null; + Score?: string | null; + Spotify?: string | null; + "Time._nanoseconds"?: string | null; + "Time._seconds"?: string | null; + }; + Relationships: []; + }; leaderboard_boogie: { Row: { - clientId: string | null - displayName: string | null - fanId: string | null - gameType: string | null - id: string | null - score: number | null - timestamp: string | null - } - Insert: { - clientId?: string | null - displayName?: string | null - fanId?: string | null - gameType?: string | null - id?: string | null - score?: number | null - timestamp?: string | null - } - Update: { - clientId?: string | null - displayName?: string | null - fanId?: string | null - gameType?: string | null - id?: string | null - score?: number | null - timestamp?: string | null - } - Relationships: [] - } + clientId: string | null; + displayName: string | null; + fanId: string | null; + gameType: string | null; + id: string | null; + score: number | null; + timestamp: string | null; + }; + Insert: { + clientId?: string | null; + displayName?: string | null; + fanId?: string | null; + gameType?: string | null; + id?: string | null; + score?: number | null; + timestamp?: string | null; + }; + Update: { + clientId?: string | null; + displayName?: string | null; + fanId?: string | null; + gameType?: string | null; + id?: string | null; + score?: number | null; + timestamp?: string | null; + }; + Relationships: []; + }; leaderboard_luh_tyler_3d: { Row: { - FanId: string | null - id: string | null - Score: string | null - ScorePerTime: string | null - Time: string | null - timestamp: string | null - UserName: string | null - } - Insert: { - FanId?: string | null - id?: string | null - Score?: string | null - ScorePerTime?: string | null - Time?: string | null - timestamp?: string | null - UserName?: string | null - } - Update: { - FanId?: string | null - id?: string | null - Score?: string | null - ScorePerTime?: string | null - Time?: string | null - timestamp?: string | null - UserName?: string | null - } - Relationships: [] - } + FanId: string | null; + id: string | null; + Score: string | null; + ScorePerTime: string | null; + Time: string | null; + timestamp: string | null; + UserName: string | null; + }; + Insert: { + FanId?: string | null; + id?: string | null; + Score?: string | null; + ScorePerTime?: string | null; + Time?: string | null; + timestamp?: string | null; + UserName?: string | null; + }; + Update: { + FanId?: string | null; + id?: string | null; + Score?: string | null; + ScorePerTime?: string | null; + Time?: string | null; + timestamp?: string | null; + UserName?: string | null; + }; + Relationships: []; + }; leaderboard_luv: { Row: { - f: string | null - id: string | null - } + f: string | null; + id: string | null; + }; Insert: { - f?: string | null - id?: string | null - } + f?: string | null; + id?: string | null; + }; Update: { - f?: string | null - id?: string | null - } - Relationships: [] - } + f?: string | null; + id?: string | null; + }; + Relationships: []; + }; memories: { Row: { - content: Json - id: string - room_id: string | null - updated_at: string - } - Insert: { - content: Json - id?: string - room_id?: string | null - updated_at?: string - } - Update: { - content?: Json - id?: string - room_id?: string | null - updated_at?: string - } + content: Json; + id: string; + room_id: string | null; + updated_at: string; + }; + Insert: { + content: Json; + id?: string; + room_id?: string | null; + updated_at?: string; + }; + Update: { + content?: Json; + id?: string; + room_id?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "memories_room_id_fkey" - columns: ["room_id"] - isOneToOne: false - referencedRelation: "rooms" - referencedColumns: ["id"] + foreignKeyName: "memories_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; memory_emails: { Row: { - created_at: string - email_id: string - id: string - memory: string - message_id: string - } - Insert: { - created_at?: string - email_id: string - id?: string - memory: string - message_id: string - } - Update: { - created_at?: string - email_id?: string - id?: string - memory?: string - message_id?: string - } + created_at: string; + email_id: string; + id: string; + memory: string; + message_id: string; + }; + Insert: { + created_at?: string; + email_id: string; + id?: string; + memory: string; + message_id: string; + }; + Update: { + created_at?: string; + email_id?: string; + id?: string; + memory?: string; + message_id?: string; + }; Relationships: [ { - foreignKeyName: "memory_emails_memory_fkey" - columns: ["memory"] - isOneToOne: false - referencedRelation: "memories" - referencedColumns: ["id"] + foreignKeyName: "memory_emails_memory_fkey"; + columns: ["memory"]; + isOneToOne: false; + referencedRelation: "memories"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; notifications: { Row: { - account_id: string - body: string - channel: Database["public"]["Enums"]["notification_channel"] - created_at: string - dismissed: boolean - expires_at: string | null - id: number - link: string | null - type: Database["public"]["Enums"]["notification_type"] - } - Insert: { - account_id: string - body: string - channel?: Database["public"]["Enums"]["notification_channel"] - created_at?: string - dismissed?: boolean - expires_at?: string | null - id?: never - link?: string | null - type?: Database["public"]["Enums"]["notification_type"] - } - Update: { - account_id?: string - body?: string - channel?: Database["public"]["Enums"]["notification_channel"] - created_at?: string - dismissed?: boolean - expires_at?: string | null - id?: never - link?: string | null - type?: Database["public"]["Enums"]["notification_type"] - } - Relationships: [] - } + account_id: string; + body: string; + channel: Database["public"]["Enums"]["notification_channel"]; + created_at: string; + dismissed: boolean; + expires_at: string | null; + id: number; + link: string | null; + type: Database["public"]["Enums"]["notification_type"]; + }; + Insert: { + account_id: string; + body: string; + channel?: Database["public"]["Enums"]["notification_channel"]; + created_at?: string; + dismissed?: boolean; + expires_at?: string | null; + id?: never; + link?: string | null; + type?: Database["public"]["Enums"]["notification_type"]; + }; + Update: { + account_id?: string; + body?: string; + channel?: Database["public"]["Enums"]["notification_channel"]; + created_at?: string; + dismissed?: boolean; + expires_at?: string | null; + id?: never; + link?: string | null; + type?: Database["public"]["Enums"]["notification_type"]; + }; + Relationships: []; + }; order_items: { Row: { - created_at: string - id: string - order_id: string - price_amount: number | null - product_id: string - quantity: number - updated_at: string - variant_id: string - } - Insert: { - created_at?: string - id: string - order_id: string - price_amount?: number | null - product_id: string - quantity?: number - updated_at?: string - variant_id: string - } - Update: { - created_at?: string - id?: string - order_id?: string - price_amount?: number | null - product_id?: string - quantity?: number - updated_at?: string - variant_id?: string - } + created_at: string; + id: string; + order_id: string; + price_amount: number | null; + product_id: string; + quantity: number; + updated_at: string; + variant_id: string; + }; + Insert: { + created_at?: string; + id: string; + order_id: string; + price_amount?: number | null; + product_id: string; + quantity?: number; + updated_at?: string; + variant_id: string; + }; + Update: { + created_at?: string; + id?: string; + order_id?: string; + price_amount?: number | null; + product_id?: string; + quantity?: number; + updated_at?: string; + variant_id?: string; + }; Relationships: [ { - foreignKeyName: "order_items_order_id_fkey" - columns: ["order_id"] - isOneToOne: false - referencedRelation: "orders" - referencedColumns: ["id"] + foreignKeyName: "order_items_order_id_fkey"; + columns: ["order_id"]; + isOneToOne: false; + referencedRelation: "orders"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; orders: { Row: { - account_id: string - billing_customer_id: number - billing_provider: Database["public"]["Enums"]["billing_provider"] - created_at: string - currency: string - id: string - status: Database["public"]["Enums"]["payment_status"] - total_amount: number - updated_at: string - } - Insert: { - account_id: string - billing_customer_id: number - billing_provider: Database["public"]["Enums"]["billing_provider"] - created_at?: string - currency: string - id: string - status: Database["public"]["Enums"]["payment_status"] - total_amount: number - updated_at?: string - } - Update: { - account_id?: string - billing_customer_id?: number - billing_provider?: Database["public"]["Enums"]["billing_provider"] - created_at?: string - currency?: string - id?: string - status?: Database["public"]["Enums"]["payment_status"] - total_amount?: number - updated_at?: string - } + account_id: string; + billing_customer_id: number; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + created_at: string; + currency: string; + id: string; + status: Database["public"]["Enums"]["payment_status"]; + total_amount: number; + updated_at: string; + }; + Insert: { + account_id: string; + billing_customer_id: number; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + created_at?: string; + currency: string; + id: string; + status: Database["public"]["Enums"]["payment_status"]; + total_amount: number; + updated_at?: string; + }; + Update: { + account_id?: string; + billing_customer_id?: number; + billing_provider?: Database["public"]["Enums"]["billing_provider"]; + created_at?: string; + currency?: string; + id?: string; + status?: Database["public"]["Enums"]["payment_status"]; + total_amount?: number; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "orders_billing_customer_id_fkey" - columns: ["billing_customer_id"] - isOneToOne: false - referencedRelation: "billing_customers" - referencedColumns: ["id"] + foreignKeyName: "orders_billing_customer_id_fkey"; + columns: ["billing_customer_id"]; + isOneToOne: false; + referencedRelation: "billing_customers"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; organization_domains: { Row: { - created_at: string | null - domain: string - id: string - organization_id: string - } - Insert: { - created_at?: string | null - domain: string - id?: string - organization_id: string - } - Update: { - created_at?: string | null - domain?: string - id?: string - organization_id?: string - } + created_at: string | null; + domain: string; + id: string; + organization_id: string; + }; + Insert: { + created_at?: string | null; + domain: string; + id?: string; + organization_id: string; + }; + Update: { + created_at?: string | null; + domain?: string; + id?: string; + organization_id?: string; + }; Relationships: [ { - foreignKeyName: "organization_domains_organization_id_fkey" - columns: ["organization_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "organization_domains_organization_id_fkey"; + columns: ["organization_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; plans: { Row: { - name: string - tokens_quota: number - variant_id: string - } - Insert: { - name: string - tokens_quota: number - variant_id: string - } - Update: { - name?: string - tokens_quota?: number - variant_id?: string - } - Relationships: [] - } + name: string; + tokens_quota: number; + variant_id: string; + }; + Insert: { + name: string; + tokens_quota: number; + variant_id: string; + }; + Update: { + name?: string; + tokens_quota?: number; + variant_id?: string; + }; + Relationships: []; + }; popup_open: { Row: { - campaignId: string | null - clientId: string | null - fanId: string | null - game: string | null - id: string | null - timestamp: string | null - } - Insert: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string | null - timestamp?: string | null - } - Update: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string | null - timestamp?: string | null - } - Relationships: [] - } + campaignId: string | null; + clientId: string | null; + fanId: string | null; + game: string | null; + id: string | null; + timestamp: string | null; + }; + Insert: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string | null; + timestamp?: string | null; + }; + Update: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string | null; + timestamp?: string | null; + }; + Relationships: []; + }; post_comments: { Row: { - comment: string | null - commented_at: string - id: string - post_id: string | null - social_id: string | null - } - Insert: { - comment?: string | null - commented_at: string - id?: string - post_id?: string | null - social_id?: string | null - } - Update: { - comment?: string | null - commented_at?: string - id?: string - post_id?: string | null - social_id?: string | null - } + comment: string | null; + commented_at: string; + id: string; + post_id: string | null; + social_id: string | null; + }; + Insert: { + comment?: string | null; + commented_at: string; + id?: string; + post_id?: string | null; + social_id?: string | null; + }; + Update: { + comment?: string | null; + commented_at?: string; + id?: string; + post_id?: string | null; + social_id?: string | null; + }; Relationships: [ { - foreignKeyName: "post_comments_post_id_fkey" - columns: ["post_id"] - isOneToOne: false - referencedRelation: "posts" - referencedColumns: ["id"] + foreignKeyName: "post_comments_post_id_fkey"; + columns: ["post_id"]; + isOneToOne: false; + referencedRelation: "posts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "post_comments_social_id_fkey" - columns: ["social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "post_comments_social_id_fkey"; + columns: ["social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; posts: { Row: { - id: string - post_url: string - updated_at: string - } - Insert: { - id?: string - post_url: string - updated_at?: string - } - Update: { - id?: string - post_url?: string - updated_at?: string - } - Relationships: [] - } + id: string; + post_url: string; + updated_at: string; + }; + Insert: { + id?: string; + post_url: string; + updated_at?: string; + }; + Update: { + id?: string; + post_url?: string; + updated_at?: string; + }; + Relationships: []; + }; presave: { Row: { - accessToken: string | null - fanId: string | null - "fanId.error.code": string | null - "fanId.error.name": string | null - id: string | null - presaveId: string | null - presaveReleaseDate: string | null - refreshToken: string | null - timestamp: number | null - } - Insert: { - accessToken?: string | null - fanId?: string | null - "fanId.error.code"?: string | null - "fanId.error.name"?: string | null - id?: string | null - presaveId?: string | null - presaveReleaseDate?: string | null - refreshToken?: string | null - timestamp?: number | null - } - Update: { - accessToken?: string | null - fanId?: string | null - "fanId.error.code"?: string | null - "fanId.error.name"?: string | null - id?: string | null - presaveId?: string | null - presaveReleaseDate?: string | null - refreshToken?: string | null - timestamp?: number | null - } - Relationships: [] - } + accessToken: string | null; + fanId: string | null; + "fanId.error.code": string | null; + "fanId.error.name": string | null; + id: string | null; + presaveId: string | null; + presaveReleaseDate: string | null; + refreshToken: string | null; + timestamp: number | null; + }; + Insert: { + accessToken?: string | null; + fanId?: string | null; + "fanId.error.code"?: string | null; + "fanId.error.name"?: string | null; + id?: string | null; + presaveId?: string | null; + presaveReleaseDate?: string | null; + refreshToken?: string | null; + timestamp?: number | null; + }; + Update: { + accessToken?: string | null; + fanId?: string | null; + "fanId.error.code"?: string | null; + "fanId.error.name"?: string | null; + id?: string | null; + presaveId?: string | null; + presaveReleaseDate?: string | null; + refreshToken?: string | null; + timestamp?: number | null; + }; + Relationships: []; + }; pulse_accounts: { Row: { - account_id: string - active: boolean - id: string - } + account_id: string; + active: boolean; + id: string; + }; Insert: { - account_id: string - active?: boolean - id?: string - } + account_id: string; + active?: boolean; + id?: string; + }; Update: { - account_id?: string - active?: boolean - id?: string - } + account_id?: string; + active?: boolean; + id?: string; + }; Relationships: [ { - foreignKeyName: "pulse_accounts_account_id_fkey" - columns: ["account_id"] - isOneToOne: true - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "pulse_accounts_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: true; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; role_permissions: { Row: { - id: number - permission: Database["public"]["Enums"]["app_permissions"] - role: string - } + id: number; + permission: Database["public"]["Enums"]["app_permissions"]; + role: string; + }; Insert: { - id?: number - permission: Database["public"]["Enums"]["app_permissions"] - role: string - } + id?: number; + permission: Database["public"]["Enums"]["app_permissions"]; + role: string; + }; Update: { - id?: number - permission?: Database["public"]["Enums"]["app_permissions"] - role?: string - } + id?: number; + permission?: Database["public"]["Enums"]["app_permissions"]; + role?: string; + }; Relationships: [ { - foreignKeyName: "role_permissions_role_fkey" - columns: ["role"] - isOneToOne: false - referencedRelation: "roles" - referencedColumns: ["name"] + foreignKeyName: "role_permissions_role_fkey"; + columns: ["role"]; + isOneToOne: false; + referencedRelation: "roles"; + referencedColumns: ["name"]; }, - ] - } + ]; + }; roles: { Row: { - hierarchy_level: number - name: string - } + hierarchy_level: number; + name: string; + }; Insert: { - hierarchy_level: number - name: string - } + hierarchy_level: number; + name: string; + }; Update: { - hierarchy_level?: number - name?: string - } - Relationships: [] - } + hierarchy_level?: number; + name?: string; + }; + Relationships: []; + }; room_reports: { Row: { - id: string - report_id: string - room_id: string | null - } + id: string; + report_id: string; + room_id: string | null; + }; Insert: { - id?: string - report_id?: string - room_id?: string | null - } + id?: string; + report_id?: string; + room_id?: string | null; + }; Update: { - id?: string - report_id?: string - room_id?: string | null - } + id?: string; + report_id?: string; + room_id?: string | null; + }; Relationships: [ { - foreignKeyName: "room_reports_report_id_fkey" - columns: ["report_id"] - isOneToOne: false - referencedRelation: "segment_reports" - referencedColumns: ["id"] + foreignKeyName: "room_reports_report_id_fkey"; + columns: ["report_id"]; + isOneToOne: false; + referencedRelation: "segment_reports"; + referencedColumns: ["id"]; }, { - foreignKeyName: "room_reports_room_id_fkey" - columns: ["room_id"] - isOneToOne: false - referencedRelation: "rooms" - referencedColumns: ["id"] + foreignKeyName: "room_reports_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; rooms: { Row: { - account_id: string | null - artist_id: string | null - id: string - topic: string | null - updated_at: string - } - Insert: { - account_id?: string | null - artist_id?: string | null - id?: string - topic?: string | null - updated_at?: string - } - Update: { - account_id?: string | null - artist_id?: string | null - id?: string - topic?: string | null - updated_at?: string - } + account_id: string | null; + artist_id: string | null; + id: string; + topic: string | null; + updated_at: string; + }; + Insert: { + account_id?: string | null; + artist_id?: string | null; + id?: string; + topic?: string | null; + updated_at?: string; + }; + Update: { + account_id?: string | null; + artist_id?: string | null; + id?: string; + topic?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "rooms_artist_id_fkey" - columns: ["artist_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "rooms_artist_id_fkey"; + columns: ["artist_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; sales_pipeline_customers: { Row: { - activity_count: number | null - assigned_to: string | null - company_size: string | null - competitors: string[] | null - contact_email: string | null - contact_name: string | null - contact_phone: string | null - contacts: Json | null - conversion_stage: string | null - conversion_target_date: string | null - created_at: string | null - current_artists: number - current_mrr: number - custom_fields: Json | null - days_in_stage: number | null - domain: string | null - email: string | null - engagement_health: string | null - expected_close_date: string | null - external_ids: Json | null - id: string - industry: string | null - internal_owner: string | null - last_activity_date: string | null - last_activity_type: string | null - last_contact_date: string - logo_url: string | null - lost_reason: string | null - name: string - next_action: string | null - next_activity_date: string | null - next_activity_type: string | null - notes: string | null - order_index: number | null - organization: string | null - potential_artists: number - potential_mrr: number - priority: string | null - probability: number | null - recoupable_user_id: string | null - source: string | null - stage: string - stage_entered_at: string | null - tags: string[] | null - todos: Json | null - trial_end_date: string | null - trial_start_date: string | null - type: string | null - updated_at: string | null - use_case_type: string | null - website: string | null - weighted_mrr: number | null - win_reason: string | null - } - Insert: { - activity_count?: number | null - assigned_to?: string | null - company_size?: string | null - competitors?: string[] | null - contact_email?: string | null - contact_name?: string | null - contact_phone?: string | null - contacts?: Json | null - conversion_stage?: string | null - conversion_target_date?: string | null - created_at?: string | null - current_artists?: number - current_mrr?: number - custom_fields?: Json | null - days_in_stage?: number | null - domain?: string | null - email?: string | null - engagement_health?: string | null - expected_close_date?: string | null - external_ids?: Json | null - id?: string - industry?: string | null - internal_owner?: string | null - last_activity_date?: string | null - last_activity_type?: string | null - last_contact_date?: string - logo_url?: string | null - lost_reason?: string | null - name: string - next_action?: string | null - next_activity_date?: string | null - next_activity_type?: string | null - notes?: string | null - order_index?: number | null - organization?: string | null - potential_artists?: number - potential_mrr?: number - priority?: string | null - probability?: number | null - recoupable_user_id?: string | null - source?: string | null - stage: string - stage_entered_at?: string | null - tags?: string[] | null - todos?: Json | null - trial_end_date?: string | null - trial_start_date?: string | null - type?: string | null - updated_at?: string | null - use_case_type?: string | null - website?: string | null - weighted_mrr?: number | null - win_reason?: string | null - } - Update: { - activity_count?: number | null - assigned_to?: string | null - company_size?: string | null - competitors?: string[] | null - contact_email?: string | null - contact_name?: string | null - contact_phone?: string | null - contacts?: Json | null - conversion_stage?: string | null - conversion_target_date?: string | null - created_at?: string | null - current_artists?: number - current_mrr?: number - custom_fields?: Json | null - days_in_stage?: number | null - domain?: string | null - email?: string | null - engagement_health?: string | null - expected_close_date?: string | null - external_ids?: Json | null - id?: string - industry?: string | null - internal_owner?: string | null - last_activity_date?: string | null - last_activity_type?: string | null - last_contact_date?: string - logo_url?: string | null - lost_reason?: string | null - name?: string - next_action?: string | null - next_activity_date?: string | null - next_activity_type?: string | null - notes?: string | null - order_index?: number | null - organization?: string | null - potential_artists?: number - potential_mrr?: number - priority?: string | null - probability?: number | null - recoupable_user_id?: string | null - source?: string | null - stage?: string - stage_entered_at?: string | null - tags?: string[] | null - todos?: Json | null - trial_end_date?: string | null - trial_start_date?: string | null - type?: string | null - updated_at?: string | null - use_case_type?: string | null - website?: string | null - weighted_mrr?: number | null - win_reason?: string | null - } - Relationships: [] - } + activity_count: number | null; + assigned_to: string | null; + company_size: string | null; + competitors: string[] | null; + contact_email: string | null; + contact_name: string | null; + contact_phone: string | null; + contacts: Json | null; + conversion_stage: string | null; + conversion_target_date: string | null; + created_at: string | null; + current_artists: number; + current_mrr: number; + custom_fields: Json | null; + days_in_stage: number | null; + domain: string | null; + email: string | null; + engagement_health: string | null; + expected_close_date: string | null; + external_ids: Json | null; + id: string; + industry: string | null; + internal_owner: string | null; + last_activity_date: string | null; + last_activity_type: string | null; + last_contact_date: string; + logo_url: string | null; + lost_reason: string | null; + name: string; + next_action: string | null; + next_activity_date: string | null; + next_activity_type: string | null; + notes: string | null; + order_index: number | null; + organization: string | null; + potential_artists: number; + potential_mrr: number; + priority: string | null; + probability: number | null; + recoupable_user_id: string | null; + source: string | null; + stage: string; + stage_entered_at: string | null; + tags: string[] | null; + todos: Json | null; + trial_end_date: string | null; + trial_start_date: string | null; + type: string | null; + updated_at: string | null; + use_case_type: string | null; + website: string | null; + weighted_mrr: number | null; + win_reason: string | null; + }; + Insert: { + activity_count?: number | null; + assigned_to?: string | null; + company_size?: string | null; + competitors?: string[] | null; + contact_email?: string | null; + contact_name?: string | null; + contact_phone?: string | null; + contacts?: Json | null; + conversion_stage?: string | null; + conversion_target_date?: string | null; + created_at?: string | null; + current_artists?: number; + current_mrr?: number; + custom_fields?: Json | null; + days_in_stage?: number | null; + domain?: string | null; + email?: string | null; + engagement_health?: string | null; + expected_close_date?: string | null; + external_ids?: Json | null; + id?: string; + industry?: string | null; + internal_owner?: string | null; + last_activity_date?: string | null; + last_activity_type?: string | null; + last_contact_date?: string; + logo_url?: string | null; + lost_reason?: string | null; + name: string; + next_action?: string | null; + next_activity_date?: string | null; + next_activity_type?: string | null; + notes?: string | null; + order_index?: number | null; + organization?: string | null; + potential_artists?: number; + potential_mrr?: number; + priority?: string | null; + probability?: number | null; + recoupable_user_id?: string | null; + source?: string | null; + stage: string; + stage_entered_at?: string | null; + tags?: string[] | null; + todos?: Json | null; + trial_end_date?: string | null; + trial_start_date?: string | null; + type?: string | null; + updated_at?: string | null; + use_case_type?: string | null; + website?: string | null; + weighted_mrr?: number | null; + win_reason?: string | null; + }; + Update: { + activity_count?: number | null; + assigned_to?: string | null; + company_size?: string | null; + competitors?: string[] | null; + contact_email?: string | null; + contact_name?: string | null; + contact_phone?: string | null; + contacts?: Json | null; + conversion_stage?: string | null; + conversion_target_date?: string | null; + created_at?: string | null; + current_artists?: number; + current_mrr?: number; + custom_fields?: Json | null; + days_in_stage?: number | null; + domain?: string | null; + email?: string | null; + engagement_health?: string | null; + expected_close_date?: string | null; + external_ids?: Json | null; + id?: string; + industry?: string | null; + internal_owner?: string | null; + last_activity_date?: string | null; + last_activity_type?: string | null; + last_contact_date?: string; + logo_url?: string | null; + lost_reason?: string | null; + name?: string; + next_action?: string | null; + next_activity_date?: string | null; + next_activity_type?: string | null; + notes?: string | null; + order_index?: number | null; + organization?: string | null; + potential_artists?: number; + potential_mrr?: number; + priority?: string | null; + probability?: number | null; + recoupable_user_id?: string | null; + source?: string | null; + stage?: string; + stage_entered_at?: string | null; + tags?: string[] | null; + todos?: Json | null; + trial_end_date?: string | null; + trial_start_date?: string | null; + type?: string | null; + updated_at?: string | null; + use_case_type?: string | null; + website?: string | null; + weighted_mrr?: number | null; + win_reason?: string | null; + }; + Relationships: []; + }; save_track: { Row: { - game: string | null - id: string | null - timestamp: string | null - } - Insert: { - game?: string | null - id?: string | null - timestamp?: string | null - } - Update: { - game?: string | null - id?: string | null - timestamp?: string | null - } - Relationships: [] - } + game: string | null; + id: string | null; + timestamp: string | null; + }; + Insert: { + game?: string | null; + id?: string | null; + timestamp?: string | null; + }; + Update: { + game?: string | null; + id?: string | null; + timestamp?: string | null; + }; + Relationships: []; + }; scheduled_actions: { Row: { - account_id: string - artist_account_id: string - created_at: string | null - enabled: boolean | null - id: string - last_run: string | null - model: string | null - next_run: string | null - prompt: string - schedule: string - title: string - trigger_schedule_id: string | null - updated_at: string | null - } - Insert: { - account_id: string - artist_account_id: string - created_at?: string | null - enabled?: boolean | null - id?: string - last_run?: string | null - model?: string | null - next_run?: string | null - prompt: string - schedule: string - title: string - trigger_schedule_id?: string | null - updated_at?: string | null - } - Update: { - account_id?: string - artist_account_id?: string - created_at?: string | null - enabled?: boolean | null - id?: string - last_run?: string | null - model?: string | null - next_run?: string | null - prompt?: string - schedule?: string - title?: string - trigger_schedule_id?: string | null - updated_at?: string | null - } + account_id: string; + artist_account_id: string; + created_at: string | null; + enabled: boolean | null; + id: string; + last_run: string | null; + model: string | null; + next_run: string | null; + prompt: string; + schedule: string; + title: string; + trigger_schedule_id: string | null; + updated_at: string | null; + }; + Insert: { + account_id: string; + artist_account_id: string; + created_at?: string | null; + enabled?: boolean | null; + id?: string; + last_run?: string | null; + model?: string | null; + next_run?: string | null; + prompt: string; + schedule: string; + title: string; + trigger_schedule_id?: string | null; + updated_at?: string | null; + }; + Update: { + account_id?: string; + artist_account_id?: string; + created_at?: string | null; + enabled?: boolean | null; + id?: string; + last_run?: string | null; + model?: string | null; + next_run?: string | null; + prompt?: string; + schedule?: string; + title?: string; + trigger_schedule_id?: string | null; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "scheduled_actions_account_id_fkey" - columns: ["account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "scheduled_actions_account_id_fkey"; + columns: ["account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "scheduled_actions_artist_account_id_fkey" - columns: ["artist_account_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "scheduled_actions_artist_account_id_fkey"; + columns: ["artist_account_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; segment_reports: { Row: { - artist_id: string | null - id: string - next_steps: string | null - report: string | null - updated_at: string | null - } - Insert: { - artist_id?: string | null - id?: string - next_steps?: string | null - report?: string | null - updated_at?: string | null - } - Update: { - artist_id?: string | null - id?: string - next_steps?: string | null - report?: string | null - updated_at?: string | null - } + artist_id: string | null; + id: string; + next_steps: string | null; + report: string | null; + updated_at: string | null; + }; + Insert: { + artist_id?: string | null; + id?: string; + next_steps?: string | null; + report?: string | null; + updated_at?: string | null; + }; + Update: { + artist_id?: string | null; + id?: string; + next_steps?: string | null; + report?: string | null; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "segment_reports_artist_id_fkey" - columns: ["artist_id"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "segment_reports_artist_id_fkey"; + columns: ["artist_id"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; segment_rooms: { Row: { - id: string - room_id: string - segment_id: string - updated_at: string - } - Insert: { - id?: string - room_id: string - segment_id: string - updated_at?: string - } - Update: { - id?: string - room_id?: string - segment_id?: string - updated_at?: string - } + id: string; + room_id: string; + segment_id: string; + updated_at: string; + }; + Insert: { + id?: string; + room_id: string; + segment_id: string; + updated_at?: string; + }; + Update: { + id?: string; + room_id?: string; + segment_id?: string; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "segment_rooms_room_id_fkey" - columns: ["room_id"] - isOneToOne: false - referencedRelation: "rooms" - referencedColumns: ["id"] + foreignKeyName: "segment_rooms_room_id_fkey"; + columns: ["room_id"]; + isOneToOne: false; + referencedRelation: "rooms"; + referencedColumns: ["id"]; }, { - foreignKeyName: "segment_rooms_segment_id_fkey" - columns: ["segment_id"] - isOneToOne: false - referencedRelation: "segments" - referencedColumns: ["id"] + foreignKeyName: "segment_rooms_segment_id_fkey"; + columns: ["segment_id"]; + isOneToOne: false; + referencedRelation: "segments"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; segments: { Row: { - id: string - name: string - updated_at: string | null - } - Insert: { - id?: string - name: string - updated_at?: string | null - } - Update: { - id?: string - name?: string - updated_at?: string | null - } - Relationships: [] - } + id: string; + name: string; + updated_at: string | null; + }; + Insert: { + id?: string; + name: string; + updated_at?: string | null; + }; + Update: { + id?: string; + name?: string; + updated_at?: string | null; + }; + Relationships: []; + }; social_fans: { Row: { - artist_social_id: string - created_at: string - fan_social_id: string - id: string - latest_engagement: string | null - latest_engagement_id: string | null - updated_at: string - } - Insert: { - artist_social_id: string - created_at?: string - fan_social_id: string - id?: string - latest_engagement?: string | null - latest_engagement_id?: string | null - updated_at?: string - } - Update: { - artist_social_id?: string - created_at?: string - fan_social_id?: string - id?: string - latest_engagement?: string | null - latest_engagement_id?: string | null - updated_at?: string - } + artist_social_id: string; + created_at: string; + fan_social_id: string; + id: string; + latest_engagement: string | null; + latest_engagement_id: string | null; + updated_at: string; + }; + Insert: { + artist_social_id: string; + created_at?: string; + fan_social_id: string; + id?: string; + latest_engagement?: string | null; + latest_engagement_id?: string | null; + updated_at?: string; + }; + Update: { + artist_social_id?: string; + created_at?: string; + fan_social_id?: string; + id?: string; + latest_engagement?: string | null; + latest_engagement_id?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "social_fans_artist_social_id_fkey" - columns: ["artist_social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "social_fans_artist_social_id_fkey"; + columns: ["artist_social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, { - foreignKeyName: "social_fans_fan_social_id_fkey" - columns: ["fan_social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "social_fans_fan_social_id_fkey"; + columns: ["fan_social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, { - foreignKeyName: "social_fans_latest_engagement_id_fkey" - columns: ["latest_engagement_id"] - isOneToOne: false - referencedRelation: "post_comments" - referencedColumns: ["id"] + foreignKeyName: "social_fans_latest_engagement_id_fkey"; + columns: ["latest_engagement_id"]; + isOneToOne: false; + referencedRelation: "post_comments"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; social_posts: { Row: { - id: string - post_id: string | null - social_id: string | null - updated_at: string | null - } - Insert: { - id?: string - post_id?: string | null - social_id?: string | null - updated_at?: string | null - } - Update: { - id?: string - post_id?: string | null - social_id?: string | null - updated_at?: string | null - } + id: string; + post_id: string | null; + social_id: string | null; + updated_at: string | null; + }; + Insert: { + id?: string; + post_id?: string | null; + social_id?: string | null; + updated_at?: string | null; + }; + Update: { + id?: string; + post_id?: string | null; + social_id?: string | null; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "social_posts_post_id_fkey" - columns: ["post_id"] - isOneToOne: false - referencedRelation: "posts" - referencedColumns: ["id"] + foreignKeyName: "social_posts_post_id_fkey"; + columns: ["post_id"]; + isOneToOne: false; + referencedRelation: "posts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "social_posts_social_id_fkey" - columns: ["social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "social_posts_social_id_fkey"; + columns: ["social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; social_spotify_albums: { Row: { - album_id: string | null - id: string - social_id: string | null - updated_at: string - } - Insert: { - album_id?: string | null - id?: string - social_id?: string | null - updated_at?: string - } - Update: { - album_id?: string | null - id?: string - social_id?: string | null - updated_at?: string - } + album_id: string | null; + id: string; + social_id: string | null; + updated_at: string; + }; + Insert: { + album_id?: string | null; + id?: string; + social_id?: string | null; + updated_at?: string; + }; + Update: { + album_id?: string | null; + id?: string; + social_id?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "social_spotify_albums_album_id_fkey" - columns: ["album_id"] - isOneToOne: false - referencedRelation: "spotify_albums" - referencedColumns: ["id"] + foreignKeyName: "social_spotify_albums_album_id_fkey"; + columns: ["album_id"]; + isOneToOne: false; + referencedRelation: "spotify_albums"; + referencedColumns: ["id"]; }, { - foreignKeyName: "social_spotify_albums_social_id_fkey" - columns: ["social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "social_spotify_albums_social_id_fkey"; + columns: ["social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; social_spotify_tracks: { Row: { - id: string - social_id: string - track_id: string | null - updated_at: string | null - } - Insert: { - id?: string - social_id?: string - track_id?: string | null - updated_at?: string | null - } - Update: { - id?: string - social_id?: string - track_id?: string | null - updated_at?: string | null - } + id: string; + social_id: string; + track_id: string | null; + updated_at: string | null; + }; + Insert: { + id?: string; + social_id?: string; + track_id?: string | null; + updated_at?: string | null; + }; + Update: { + id?: string; + social_id?: string; + track_id?: string | null; + updated_at?: string | null; + }; Relationships: [ { - foreignKeyName: "social_spotify_tracks_social_id_fkey" - columns: ["social_id"] - isOneToOne: false - referencedRelation: "socials" - referencedColumns: ["id"] + foreignKeyName: "social_spotify_tracks_social_id_fkey"; + columns: ["social_id"]; + isOneToOne: false; + referencedRelation: "socials"; + referencedColumns: ["id"]; }, { - foreignKeyName: "social_spotify_tracks_track_id_fkey" - columns: ["track_id"] - isOneToOne: false - referencedRelation: "spotify_tracks" - referencedColumns: ["id"] + foreignKeyName: "social_spotify_tracks_track_id_fkey"; + columns: ["track_id"]; + isOneToOne: false; + referencedRelation: "spotify_tracks"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; socials: { Row: { - avatar: string | null - bio: string | null - followerCount: number | null - followingCount: number | null - id: string - profile_url: string - region: string | null - updated_at: string - username: string - } - Insert: { - avatar?: string | null - bio?: string | null - followerCount?: number | null - followingCount?: number | null - id?: string - profile_url: string - region?: string | null - updated_at?: string - username: string - } - Update: { - avatar?: string | null - bio?: string | null - followerCount?: number | null - followingCount?: number | null - id?: string - profile_url?: string - region?: string | null - updated_at?: string - username?: string - } - Relationships: [] - } + avatar: string | null; + bio: string | null; + followerCount: number | null; + followingCount: number | null; + id: string; + profile_url: string; + region: string | null; + updated_at: string; + username: string; + }; + Insert: { + avatar?: string | null; + bio?: string | null; + followerCount?: number | null; + followingCount?: number | null; + id?: string; + profile_url: string; + region?: string | null; + updated_at?: string; + username: string; + }; + Update: { + avatar?: string | null; + bio?: string | null; + followerCount?: number | null; + followingCount?: number | null; + id?: string; + profile_url?: string; + region?: string | null; + updated_at?: string; + username?: string; + }; + Relationships: []; + }; song_artists: { Row: { - artist: string - created_at: string - id: string - song: string - updated_at: string - } - Insert: { - artist: string - created_at?: string - id?: string - song: string - updated_at?: string - } - Update: { - artist?: string - created_at?: string - id?: string - song?: string - updated_at?: string - } + artist: string; + created_at: string; + id: string; + song: string; + updated_at: string; + }; + Insert: { + artist: string; + created_at?: string; + id?: string; + song: string; + updated_at?: string; + }; + Update: { + artist?: string; + created_at?: string; + id?: string; + song?: string; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "song_artists_artist_fkey" - columns: ["artist"] - isOneToOne: false - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "song_artists_artist_fkey"; + columns: ["artist"]; + isOneToOne: false; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, { - foreignKeyName: "song_artists_song_fkey" - columns: ["song"] - isOneToOne: false - referencedRelation: "songs" - referencedColumns: ["isrc"] + foreignKeyName: "song_artists_song_fkey"; + columns: ["song"]; + isOneToOne: false; + referencedRelation: "songs"; + referencedColumns: ["isrc"]; }, - ] - } + ]; + }; songs: { Row: { - album: string | null - isrc: string - name: string | null - notes: string | null - updated_at: string - } - Insert: { - album?: string | null - isrc: string - name?: string | null - notes?: string | null - updated_at?: string - } - Update: { - album?: string | null - isrc?: string - name?: string | null - notes?: string | null - updated_at?: string - } - Relationships: [] - } + album: string | null; + isrc: string; + name: string | null; + notes: string | null; + updated_at: string; + }; + Insert: { + album?: string | null; + isrc: string; + name?: string | null; + notes?: string | null; + updated_at?: string; + }; + Update: { + album?: string | null; + isrc?: string; + name?: string | null; + notes?: string | null; + updated_at?: string; + }; + Relationships: []; + }; spotify: { Row: { - clientId: string | null - country: string | null - display_name: string | null - email: string | null - "explicit_content.filter_enabled": string | null - "explicit_content.filter_locked": string | null - "external_urls.spotify": Json | null - fanId: string | null - "fanId.country": string | null - "fanId.display_name": string | null - "fanId.email": string | null - "fanId.explicit_content.filter_enabled": string | null - "fanId.explicit_content.filter_locked": string | null - "fanId.external_urls.spotify": string | null - "fanId.followers.total": string | null - "fanId.href": string | null - "fanId.id": string | null - "fanId.images": string | null - "fanId.isNewFan": string | null - "fanId.playlist": string | null - "fanId.presavedData.clientId": string | null - "fanId.presavedData.country": string | null - "fanId.presavedData.display_name": string | null - "fanId.presavedData.email": string | null - "fanId.presavedData.explicit_content.filter_enabled": string | null - "fanId.presavedData.explicit_content.filter_locked": string | null - "fanId.presavedData.external_urls.spotify": string | null - "fanId.presavedData.followers.total": string | null - "fanId.presavedData.href": string | null - "fanId.presavedData.id": string | null - "fanId.presavedData.images": string | null - "fanId.presavedData.playlist": string | null - "fanId.presavedData.product": string | null - "fanId.presavedData.recentlyPlayed": string | null - "fanId.presavedData.timestamp": string | null - "fanId.presavedData.type": string | null - "fanId.presavedData.uri": string | null - "fanId.product": string | null - "fanId.timestamp": string | null - "fanId.type": string | null - "fanId.uri": string | null - "followers.total": Json | null - game: string | null - href: string | null - id: string | null - images: Json | null - playlist: Json | null - product: string | null - syncId: string | null - timestamp: string | null - type: string | null - uri: string | null - } - Insert: { - clientId?: string | null - country?: string | null - display_name?: string | null - email?: string | null - "explicit_content.filter_enabled"?: string | null - "explicit_content.filter_locked"?: string | null - "external_urls.spotify"?: Json | null - fanId?: string | null - "fanId.country"?: string | null - "fanId.display_name"?: string | null - "fanId.email"?: string | null - "fanId.explicit_content.filter_enabled"?: string | null - "fanId.explicit_content.filter_locked"?: string | null - "fanId.external_urls.spotify"?: string | null - "fanId.followers.total"?: string | null - "fanId.href"?: string | null - "fanId.id"?: string | null - "fanId.images"?: string | null - "fanId.isNewFan"?: string | null - "fanId.playlist"?: string | null - "fanId.presavedData.clientId"?: string | null - "fanId.presavedData.country"?: string | null - "fanId.presavedData.display_name"?: string | null - "fanId.presavedData.email"?: string | null - "fanId.presavedData.explicit_content.filter_enabled"?: string | null - "fanId.presavedData.explicit_content.filter_locked"?: string | null - "fanId.presavedData.external_urls.spotify"?: string | null - "fanId.presavedData.followers.total"?: string | null - "fanId.presavedData.href"?: string | null - "fanId.presavedData.id"?: string | null - "fanId.presavedData.images"?: string | null - "fanId.presavedData.playlist"?: string | null - "fanId.presavedData.product"?: string | null - "fanId.presavedData.recentlyPlayed"?: string | null - "fanId.presavedData.timestamp"?: string | null - "fanId.presavedData.type"?: string | null - "fanId.presavedData.uri"?: string | null - "fanId.product"?: string | null - "fanId.timestamp"?: string | null - "fanId.type"?: string | null - "fanId.uri"?: string | null - "followers.total"?: Json | null - game?: string | null - href?: string | null - id?: string | null - images?: Json | null - playlist?: Json | null - product?: string | null - syncId?: string | null - timestamp?: string | null - type?: string | null - uri?: string | null - } - Update: { - clientId?: string | null - country?: string | null - display_name?: string | null - email?: string | null - "explicit_content.filter_enabled"?: string | null - "explicit_content.filter_locked"?: string | null - "external_urls.spotify"?: Json | null - fanId?: string | null - "fanId.country"?: string | null - "fanId.display_name"?: string | null - "fanId.email"?: string | null - "fanId.explicit_content.filter_enabled"?: string | null - "fanId.explicit_content.filter_locked"?: string | null - "fanId.external_urls.spotify"?: string | null - "fanId.followers.total"?: string | null - "fanId.href"?: string | null - "fanId.id"?: string | null - "fanId.images"?: string | null - "fanId.isNewFan"?: string | null - "fanId.playlist"?: string | null - "fanId.presavedData.clientId"?: string | null - "fanId.presavedData.country"?: string | null - "fanId.presavedData.display_name"?: string | null - "fanId.presavedData.email"?: string | null - "fanId.presavedData.explicit_content.filter_enabled"?: string | null - "fanId.presavedData.explicit_content.filter_locked"?: string | null - "fanId.presavedData.external_urls.spotify"?: string | null - "fanId.presavedData.followers.total"?: string | null - "fanId.presavedData.href"?: string | null - "fanId.presavedData.id"?: string | null - "fanId.presavedData.images"?: string | null - "fanId.presavedData.playlist"?: string | null - "fanId.presavedData.product"?: string | null - "fanId.presavedData.recentlyPlayed"?: string | null - "fanId.presavedData.timestamp"?: string | null - "fanId.presavedData.type"?: string | null - "fanId.presavedData.uri"?: string | null - "fanId.product"?: string | null - "fanId.timestamp"?: string | null - "fanId.type"?: string | null - "fanId.uri"?: string | null - "followers.total"?: Json | null - game?: string | null - href?: string | null - id?: string | null - images?: Json | null - playlist?: Json | null - product?: string | null - syncId?: string | null - timestamp?: string | null - type?: string | null - uri?: string | null - } - Relationships: [] - } + clientId: string | null; + country: string | null; + display_name: string | null; + email: string | null; + "explicit_content.filter_enabled": string | null; + "explicit_content.filter_locked": string | null; + "external_urls.spotify": Json | null; + fanId: string | null; + "fanId.country": string | null; + "fanId.display_name": string | null; + "fanId.email": string | null; + "fanId.explicit_content.filter_enabled": string | null; + "fanId.explicit_content.filter_locked": string | null; + "fanId.external_urls.spotify": string | null; + "fanId.followers.total": string | null; + "fanId.href": string | null; + "fanId.id": string | null; + "fanId.images": string | null; + "fanId.isNewFan": string | null; + "fanId.playlist": string | null; + "fanId.presavedData.clientId": string | null; + "fanId.presavedData.country": string | null; + "fanId.presavedData.display_name": string | null; + "fanId.presavedData.email": string | null; + "fanId.presavedData.explicit_content.filter_enabled": string | null; + "fanId.presavedData.explicit_content.filter_locked": string | null; + "fanId.presavedData.external_urls.spotify": string | null; + "fanId.presavedData.followers.total": string | null; + "fanId.presavedData.href": string | null; + "fanId.presavedData.id": string | null; + "fanId.presavedData.images": string | null; + "fanId.presavedData.playlist": string | null; + "fanId.presavedData.product": string | null; + "fanId.presavedData.recentlyPlayed": string | null; + "fanId.presavedData.timestamp": string | null; + "fanId.presavedData.type": string | null; + "fanId.presavedData.uri": string | null; + "fanId.product": string | null; + "fanId.timestamp": string | null; + "fanId.type": string | null; + "fanId.uri": string | null; + "followers.total": Json | null; + game: string | null; + href: string | null; + id: string | null; + images: Json | null; + playlist: Json | null; + product: string | null; + syncId: string | null; + timestamp: string | null; + type: string | null; + uri: string | null; + }; + Insert: { + clientId?: string | null; + country?: string | null; + display_name?: string | null; + email?: string | null; + "explicit_content.filter_enabled"?: string | null; + "explicit_content.filter_locked"?: string | null; + "external_urls.spotify"?: Json | null; + fanId?: string | null; + "fanId.country"?: string | null; + "fanId.display_name"?: string | null; + "fanId.email"?: string | null; + "fanId.explicit_content.filter_enabled"?: string | null; + "fanId.explicit_content.filter_locked"?: string | null; + "fanId.external_urls.spotify"?: string | null; + "fanId.followers.total"?: string | null; + "fanId.href"?: string | null; + "fanId.id"?: string | null; + "fanId.images"?: string | null; + "fanId.isNewFan"?: string | null; + "fanId.playlist"?: string | null; + "fanId.presavedData.clientId"?: string | null; + "fanId.presavedData.country"?: string | null; + "fanId.presavedData.display_name"?: string | null; + "fanId.presavedData.email"?: string | null; + "fanId.presavedData.explicit_content.filter_enabled"?: string | null; + "fanId.presavedData.explicit_content.filter_locked"?: string | null; + "fanId.presavedData.external_urls.spotify"?: string | null; + "fanId.presavedData.followers.total"?: string | null; + "fanId.presavedData.href"?: string | null; + "fanId.presavedData.id"?: string | null; + "fanId.presavedData.images"?: string | null; + "fanId.presavedData.playlist"?: string | null; + "fanId.presavedData.product"?: string | null; + "fanId.presavedData.recentlyPlayed"?: string | null; + "fanId.presavedData.timestamp"?: string | null; + "fanId.presavedData.type"?: string | null; + "fanId.presavedData.uri"?: string | null; + "fanId.product"?: string | null; + "fanId.timestamp"?: string | null; + "fanId.type"?: string | null; + "fanId.uri"?: string | null; + "followers.total"?: Json | null; + game?: string | null; + href?: string | null; + id?: string | null; + images?: Json | null; + playlist?: Json | null; + product?: string | null; + syncId?: string | null; + timestamp?: string | null; + type?: string | null; + uri?: string | null; + }; + Update: { + clientId?: string | null; + country?: string | null; + display_name?: string | null; + email?: string | null; + "explicit_content.filter_enabled"?: string | null; + "explicit_content.filter_locked"?: string | null; + "external_urls.spotify"?: Json | null; + fanId?: string | null; + "fanId.country"?: string | null; + "fanId.display_name"?: string | null; + "fanId.email"?: string | null; + "fanId.explicit_content.filter_enabled"?: string | null; + "fanId.explicit_content.filter_locked"?: string | null; + "fanId.external_urls.spotify"?: string | null; + "fanId.followers.total"?: string | null; + "fanId.href"?: string | null; + "fanId.id"?: string | null; + "fanId.images"?: string | null; + "fanId.isNewFan"?: string | null; + "fanId.playlist"?: string | null; + "fanId.presavedData.clientId"?: string | null; + "fanId.presavedData.country"?: string | null; + "fanId.presavedData.display_name"?: string | null; + "fanId.presavedData.email"?: string | null; + "fanId.presavedData.explicit_content.filter_enabled"?: string | null; + "fanId.presavedData.explicit_content.filter_locked"?: string | null; + "fanId.presavedData.external_urls.spotify"?: string | null; + "fanId.presavedData.followers.total"?: string | null; + "fanId.presavedData.href"?: string | null; + "fanId.presavedData.id"?: string | null; + "fanId.presavedData.images"?: string | null; + "fanId.presavedData.playlist"?: string | null; + "fanId.presavedData.product"?: string | null; + "fanId.presavedData.recentlyPlayed"?: string | null; + "fanId.presavedData.timestamp"?: string | null; + "fanId.presavedData.type"?: string | null; + "fanId.presavedData.uri"?: string | null; + "fanId.product"?: string | null; + "fanId.timestamp"?: string | null; + "fanId.type"?: string | null; + "fanId.uri"?: string | null; + "followers.total"?: Json | null; + game?: string | null; + href?: string | null; + id?: string | null; + images?: Json | null; + playlist?: Json | null; + product?: string | null; + syncId?: string | null; + timestamp?: string | null; + type?: string | null; + uri?: string | null; + }; + Relationships: []; + }; spotify_albums: { Row: { - id: string - name: string | null - release_date: string | null - updated_at: string - uri: string - } - Insert: { - id?: string - name?: string | null - release_date?: string | null - updated_at?: string - uri: string - } - Update: { - id?: string - name?: string | null - release_date?: string | null - updated_at?: string - uri?: string - } - Relationships: [] - } + id: string; + name: string | null; + release_date: string | null; + updated_at: string; + uri: string; + }; + Insert: { + id?: string; + name?: string | null; + release_date?: string | null; + updated_at?: string; + uri: string; + }; + Update: { + id?: string; + name?: string | null; + release_date?: string | null; + updated_at?: string; + uri?: string; + }; + Relationships: []; + }; spotify_analytics_albums: { Row: { - analysis_id: string | null - artist_name: string | null - created_at: string - id: string - name: string | null - release_date: number | null - uri: string | null - } - Insert: { - analysis_id?: string | null - artist_name?: string | null - created_at?: string - id?: string - name?: string | null - release_date?: number | null - uri?: string | null - } - Update: { - analysis_id?: string | null - artist_name?: string | null - created_at?: string - id?: string - name?: string | null - release_date?: number | null - uri?: string | null - } + analysis_id: string | null; + artist_name: string | null; + created_at: string; + id: string; + name: string | null; + release_date: number | null; + uri: string | null; + }; + Insert: { + analysis_id?: string | null; + artist_name?: string | null; + created_at?: string; + id?: string; + name?: string | null; + release_date?: number | null; + uri?: string | null; + }; + Update: { + analysis_id?: string | null; + artist_name?: string | null; + created_at?: string; + id?: string; + name?: string | null; + release_date?: number | null; + uri?: string | null; + }; Relationships: [ { - foreignKeyName: "spotify_analytics_albums_analysis_id_fkey" - columns: ["analysis_id"] - isOneToOne: false - referencedRelation: "funnel_analytics" - referencedColumns: ["id"] + foreignKeyName: "spotify_analytics_albums_analysis_id_fkey"; + columns: ["analysis_id"]; + isOneToOne: false; + referencedRelation: "funnel_analytics"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; spotify_analytics_tracks: { Row: { - analysis_id: string | null - artist_name: string | null - created_at: string - id: string - name: string | null - popularity: number | null - uri: string | null - } - Insert: { - analysis_id?: string | null - artist_name?: string | null - created_at?: string - id?: string - name?: string | null - popularity?: number | null - uri?: string | null - } - Update: { - analysis_id?: string | null - artist_name?: string | null - created_at?: string - id?: string - name?: string | null - popularity?: number | null - uri?: string | null - } + analysis_id: string | null; + artist_name: string | null; + created_at: string; + id: string; + name: string | null; + popularity: number | null; + uri: string | null; + }; + Insert: { + analysis_id?: string | null; + artist_name?: string | null; + created_at?: string; + id?: string; + name?: string | null; + popularity?: number | null; + uri?: string | null; + }; + Update: { + analysis_id?: string | null; + artist_name?: string | null; + created_at?: string; + id?: string; + name?: string | null; + popularity?: number | null; + uri?: string | null; + }; Relationships: [ { - foreignKeyName: "spotify_analytics_tracks_analysis_id_fkey" - columns: ["analysis_id"] - isOneToOne: false - referencedRelation: "funnel_analytics" - referencedColumns: ["id"] + foreignKeyName: "spotify_analytics_tracks_analysis_id_fkey"; + columns: ["analysis_id"]; + isOneToOne: false; + referencedRelation: "funnel_analytics"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; spotify_login_button_clicked: { Row: { - campaignId: string | null - clientId: string | null - fanId: string | null - game: string | null - id: string | null - timestamp: number | null - } - Insert: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string | null - timestamp?: number | null - } - Update: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string | null - timestamp?: number | null - } + campaignId: string | null; + clientId: string | null; + fanId: string | null; + game: string | null; + id: string | null; + timestamp: number | null; + }; + Insert: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; + Update: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string | null; + timestamp?: number | null; + }; Relationships: [ { - foreignKeyName: "spotify_login_button_clicked_campaignId_fkey" - columns: ["campaignId"] - isOneToOne: false - referencedRelation: "campaigns" - referencedColumns: ["id"] + foreignKeyName: "spotify_login_button_clicked_campaignId_fkey"; + columns: ["campaignId"]; + isOneToOne: false; + referencedRelation: "campaigns"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; spotify_play_button_clicked: { Row: { - campaignId: string | null - clientId: string | null - fanId: string | null - game: string | null - id: string - isPremium: boolean | null - timestamp: number | null - } - Insert: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string - isPremium?: boolean | null - timestamp?: number | null - } - Update: { - campaignId?: string | null - clientId?: string | null - fanId?: string | null - game?: string | null - id?: string - isPremium?: boolean | null - timestamp?: number | null - } + campaignId: string | null; + clientId: string | null; + fanId: string | null; + game: string | null; + id: string; + isPremium: boolean | null; + timestamp: number | null; + }; + Insert: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string; + isPremium?: boolean | null; + timestamp?: number | null; + }; + Update: { + campaignId?: string | null; + clientId?: string | null; + fanId?: string | null; + game?: string | null; + id?: string; + isPremium?: boolean | null; + timestamp?: number | null; + }; Relationships: [ { - foreignKeyName: "spotify_play_button_clicked_campaignId_fkey" - columns: ["campaignId"] - isOneToOne: false - referencedRelation: "campaigns" - referencedColumns: ["id"] + foreignKeyName: "spotify_play_button_clicked_campaignId_fkey"; + columns: ["campaignId"]; + isOneToOne: false; + referencedRelation: "campaigns"; + referencedColumns: ["id"]; }, { - foreignKeyName: "spotify_play_button_clicked_fanId_fkey" - columns: ["fanId"] - isOneToOne: false - referencedRelation: "fans" - referencedColumns: ["id"] + foreignKeyName: "spotify_play_button_clicked_fanId_fkey"; + columns: ["fanId"]; + isOneToOne: false; + referencedRelation: "fans"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; spotify_tracks: { Row: { - id: string - name: string | null - popularity: number | null - updated_at: string - uri: string - } - Insert: { - id?: string - name?: string | null - popularity?: number | null - updated_at?: string - uri: string - } - Update: { - id?: string - name?: string | null - popularity?: number | null - updated_at?: string - uri?: string - } - Relationships: [] - } + id: string; + name: string | null; + popularity: number | null; + updated_at: string; + uri: string; + }; + Insert: { + id?: string; + name?: string | null; + popularity?: number | null; + updated_at?: string; + uri: string; + }; + Update: { + id?: string; + name?: string | null; + popularity?: number | null; + updated_at?: string; + uri?: string; + }; + Relationships: []; + }; subscription_items: { Row: { - created_at: string - id: string - interval: string - interval_count: number - price_amount: number | null - product_id: string - quantity: number - subscription_id: string - type: Database["public"]["Enums"]["subscription_item_type"] - updated_at: string - variant_id: string - } - Insert: { - created_at?: string - id: string - interval: string - interval_count: number - price_amount?: number | null - product_id: string - quantity?: number - subscription_id: string - type: Database["public"]["Enums"]["subscription_item_type"] - updated_at?: string - variant_id: string - } - Update: { - created_at?: string - id?: string - interval?: string - interval_count?: number - price_amount?: number | null - product_id?: string - quantity?: number - subscription_id?: string - type?: Database["public"]["Enums"]["subscription_item_type"] - updated_at?: string - variant_id?: string - } + created_at: string; + id: string; + interval: string; + interval_count: number; + price_amount: number | null; + product_id: string; + quantity: number; + subscription_id: string; + type: Database["public"]["Enums"]["subscription_item_type"]; + updated_at: string; + variant_id: string; + }; + Insert: { + created_at?: string; + id: string; + interval: string; + interval_count: number; + price_amount?: number | null; + product_id: string; + quantity?: number; + subscription_id: string; + type: Database["public"]["Enums"]["subscription_item_type"]; + updated_at?: string; + variant_id: string; + }; + Update: { + created_at?: string; + id?: string; + interval?: string; + interval_count?: number; + price_amount?: number | null; + product_id?: string; + quantity?: number; + subscription_id?: string; + type?: Database["public"]["Enums"]["subscription_item_type"]; + updated_at?: string; + variant_id?: string; + }; Relationships: [ { - foreignKeyName: "subscription_items_subscription_id_fkey" - columns: ["subscription_id"] - isOneToOne: false - referencedRelation: "subscriptions" - referencedColumns: ["id"] + foreignKeyName: "subscription_items_subscription_id_fkey"; + columns: ["subscription_id"]; + isOneToOne: false; + referencedRelation: "subscriptions"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; subscriptions: { Row: { - account_id: string - active: boolean - billing_customer_id: number - billing_provider: Database["public"]["Enums"]["billing_provider"] - cancel_at_period_end: boolean - created_at: string - currency: string - id: string - period_ends_at: string - period_starts_at: string - status: Database["public"]["Enums"]["subscription_status"] - trial_ends_at: string | null - trial_starts_at: string | null - updated_at: string - } - Insert: { - account_id: string - active: boolean - billing_customer_id: number - billing_provider: Database["public"]["Enums"]["billing_provider"] - cancel_at_period_end: boolean - created_at?: string - currency: string - id: string - period_ends_at: string - period_starts_at: string - status: Database["public"]["Enums"]["subscription_status"] - trial_ends_at?: string | null - trial_starts_at?: string | null - updated_at?: string - } - Update: { - account_id?: string - active?: boolean - billing_customer_id?: number - billing_provider?: Database["public"]["Enums"]["billing_provider"] - cancel_at_period_end?: boolean - created_at?: string - currency?: string - id?: string - period_ends_at?: string - period_starts_at?: string - status?: Database["public"]["Enums"]["subscription_status"] - trial_ends_at?: string | null - trial_starts_at?: string | null - updated_at?: string - } + account_id: string; + active: boolean; + billing_customer_id: number; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + cancel_at_period_end: boolean; + created_at: string; + currency: string; + id: string; + period_ends_at: string; + period_starts_at: string; + status: Database["public"]["Enums"]["subscription_status"]; + trial_ends_at: string | null; + trial_starts_at: string | null; + updated_at: string; + }; + Insert: { + account_id: string; + active: boolean; + billing_customer_id: number; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + cancel_at_period_end: boolean; + created_at?: string; + currency: string; + id: string; + period_ends_at: string; + period_starts_at: string; + status: Database["public"]["Enums"]["subscription_status"]; + trial_ends_at?: string | null; + trial_starts_at?: string | null; + updated_at?: string; + }; + Update: { + account_id?: string; + active?: boolean; + billing_customer_id?: number; + billing_provider?: Database["public"]["Enums"]["billing_provider"]; + cancel_at_period_end?: boolean; + created_at?: string; + currency?: string; + id?: string; + period_ends_at?: string; + period_starts_at?: string; + status?: Database["public"]["Enums"]["subscription_status"]; + trial_ends_at?: string | null; + trial_starts_at?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "subscriptions_billing_customer_id_fkey" - columns: ["billing_customer_id"] - isOneToOne: false - referencedRelation: "billing_customers" - referencedColumns: ["id"] + foreignKeyName: "subscriptions_billing_customer_id_fkey"; + columns: ["billing_customer_id"]; + isOneToOne: false; + referencedRelation: "billing_customers"; + referencedColumns: ["id"]; }, - ] - } + ]; + }; tasks: { Row: { - account_id: string - created_at: string - description: string | null - done: boolean - id: string - title: string - updated_at: string - } - Insert: { - account_id: string - created_at?: string - description?: string | null - done?: boolean - id?: string - title: string - updated_at?: string - } - Update: { - account_id?: string - created_at?: string - description?: string | null - done?: boolean - id?: string - title?: string - updated_at?: string - } - Relationships: [] - } + account_id: string; + created_at: string; + description: string | null; + done: boolean; + id: string; + title: string; + updated_at: string; + }; + Insert: { + account_id: string; + created_at?: string; + description?: string | null; + done?: boolean; + id?: string; + title: string; + updated_at?: string; + }; + Update: { + account_id?: string; + created_at?: string; + description?: string | null; + done?: boolean; + id?: string; + title?: string; + updated_at?: string; + }; + Relationships: []; + }; test_emails: { Row: { - created_at: string - email: string | null - id: number - } - Insert: { - created_at?: string - email?: string | null - id?: number - } - Update: { - created_at?: string - email?: string | null - id?: number - } - Relationships: [] - } + created_at: string; + email: string | null; + id: number; + }; + Insert: { + created_at?: string; + email?: string | null; + id?: number; + }; + Update: { + created_at?: string; + email?: string | null; + id?: number; + }; + Relationships: []; + }; youtube_tokens: { Row: { - access_token: string - artist_account_id: string - created_at: string - expires_at: string - id: string - refresh_token: string | null - updated_at: string - } - Insert: { - access_token: string - artist_account_id: string - created_at?: string - expires_at: string - id?: string - refresh_token?: string | null - updated_at?: string - } - Update: { - access_token?: string - artist_account_id?: string - created_at?: string - expires_at?: string - id?: string - refresh_token?: string | null - updated_at?: string - } + access_token: string; + artist_account_id: string; + created_at: string; + expires_at: string; + id: string; + refresh_token: string | null; + updated_at: string; + }; + Insert: { + access_token: string; + artist_account_id: string; + created_at?: string; + expires_at: string; + id?: string; + refresh_token?: string | null; + updated_at?: string; + }; + Update: { + access_token?: string; + artist_account_id?: string; + created_at?: string; + expires_at?: string; + id?: string; + refresh_token?: string | null; + updated_at?: string; + }; Relationships: [ { - foreignKeyName: "youtube_tokens_artist_account_id_fkey" - columns: ["artist_account_id"] - isOneToOne: true - referencedRelation: "accounts" - referencedColumns: ["id"] + foreignKeyName: "youtube_tokens_artist_account_id_fkey"; + columns: ["artist_account_id"]; + isOneToOne: true; + referencedRelation: "accounts"; + referencedColumns: ["id"]; }, - ] - } - } + ]; + }; + }; Views: { - [_ in never]: never - } + [_ in never]: never; + }; Functions: { accept_invitation: { - Args: { token: string; user_id: string } - Returns: string - } + Args: { token: string; user_id: string }; + Returns: string; + }; add_invitations_to_account: { Args: { - account_slug: string - invitations: Database["public"]["CompositeTypes"]["invitation"][] - } - Returns: Database["public"]["Tables"]["invitations"]["Row"][] - } + account_slug: string; + invitations: Database["public"]["CompositeTypes"]["invitation"][]; + }; + Returns: Database["public"]["Tables"]["invitations"]["Row"][]; + }; can_action_account_member: { - Args: { target_team_account_id: string; target_user_id: string } - Returns: boolean - } + Args: { target_team_account_id: string; target_user_id: string }; + Returns: boolean; + }; count_reports_by_day: { - Args: { end_date: string; start_date: string } + Args: { end_date: string; start_date: string }; Returns: { - count: number - date_key: string - }[] - } + count: number; + date_key: string; + }[]; + }; count_reports_by_month: { - Args: { end_date: string; start_date: string } + Args: { end_date: string; start_date: string }; Returns: { - count: number - date_key: string - }[] - } + count: number; + date_key: string; + }[]; + }; count_reports_by_week: { - Args: { end_date: string; start_date: string } + Args: { end_date: string; start_date: string }; Returns: { - count: number - date_key: string - }[] - } + count: number; + date_key: string; + }[]; + }; create_invitation: { - Args: { account_id: string; email: string; role: string } + Args: { account_id: string; email: string; role: string }; Returns: { - account_id: string - created_at: string - email: string - expires_at: string - id: number - invite_token: string - invited_by: string - role: string - updated_at: string - } + account_id: string; + created_at: string; + email: string; + expires_at: string; + id: number; + invite_token: string; + invited_by: string; + role: string; + updated_at: string; + }; SetofOptions: { - from: "*" - to: "invitations" - isOneToOne: true - isSetofReturn: false - } - } + from: "*"; + to: "invitations"; + isOneToOne: true; + isSetofReturn: false; + }; + }; deduct_credits: { - Args: { account_id: string; amount: number } - Returns: undefined - } - extract_domain: { Args: { email: string }; Returns: string } + Args: { account_id: string; amount: number }; + Returns: undefined; + }; + extract_domain: { Args: { email: string }; Returns: string }; get_account_invitations: { - Args: { account_slug: string } + Args: { account_slug: string }; Returns: { - account_id: string - created_at: string - email: string - expires_at: string - id: number - invited_by: string - inviter_email: string - inviter_name: string - role: string - updated_at: string - }[] - } + account_id: string; + created_at: string; + email: string; + expires_at: string; + id: number; + invited_by: string; + inviter_email: string; + inviter_name: string; + role: string; + updated_at: string; + }[]; + }; get_account_members: { - Args: { account_slug: string } + Args: { account_slug: string }; Returns: { - account_id: string - created_at: string - email: string - id: string - name: string - picture_url: string - primary_owner_user_id: string - role: string - role_hierarchy_level: number - updated_at: string - user_id: string - }[] - } + account_id: string; + created_at: string; + email: string; + id: string; + name: string; + picture_url: string; + primary_owner_user_id: string; + role: string; + role_hierarchy_level: number; + updated_at: string; + user_id: string; + }[]; + }; get_campaign: | { Args: { clientid: string }; Returns: Json } | { - Args: { artistid: string; campaignid: string; email: string } - Returns: Json - } + Args: { artistid: string; campaignid: string; email: string }; + Returns: Json; + }; get_campaign_fans: { - Args: { artistid: string; email: string } - Returns: Json - } - get_config: { Args: never; Returns: Json } + Args: { artistid: string; email: string }; + Returns: Json; + }; + get_config: { Args: never; Returns: Json }; get_fans_listening_top_songs: { - Args: { artistid: string; email: string } - Returns: Json - } + Args: { artistid: string; email: string }; + Returns: Json; + }; get_message_counts_by_user: | { - Args: { start_date: string } + Args: { start_date: string }; Returns: { - account_email: string - message_count: number - }[] + account_email: string; + message_count: number; + }[]; } | { - Args: { end_date: string; start_date: string } + Args: { end_date: string; start_date: string }; Returns: { - account_email: string - message_count: number - }[] - } + account_email: string; + message_count: number; + }[]; + }; get_rooms_created_by_user: { - Args: { start_date: string } + Args: { start_date: string }; Returns: { - account_email: string - rooms_created: number - }[] - } + account_email: string; + rooms_created: number; + }[]; + }; get_segment_reports_by_user: { - Args: { start_date: string } + Args: { start_date: string }; Returns: { - email: string - segment_report_count: number - }[] - } - get_upper_system_role: { Args: never; Returns: string } + email: string; + segment_report_count: number; + }[]; + }; + get_upper_system_role: { Args: never; Returns: string }; has_active_subscription: { - Args: { target_account_id: string } - Returns: boolean - } - has_credits: { Args: { account_id: string }; Returns: boolean } + Args: { target_account_id: string }; + Returns: boolean; + }; + has_credits: { Args: { account_id: string }; Returns: boolean }; has_more_elevated_role: { Args: { - role_name: string - target_account_id: string - target_user_id: string - } - Returns: boolean - } + role_name: string; + target_account_id: string; + target_user_id: string; + }; + Returns: boolean; + }; has_permission: { Args: { - account_id: string - permission_name: Database["public"]["Enums"]["app_permissions"] - user_id: string - } - Returns: boolean - } + account_id: string; + permission_name: Database["public"]["Enums"]["app_permissions"]; + user_id: string; + }; + Returns: boolean; + }; has_role_on_account: { - Args: { account_id: string; account_role?: string } - Returns: boolean - } + Args: { account_id: string; account_role?: string }; + Returns: boolean; + }; has_same_role_hierarchy_level: { Args: { - role_name: string - target_account_id: string - target_user_id: string - } - Returns: boolean - } - is_account_owner: { Args: { account_id: string }; Returns: boolean } + role_name: string; + target_account_id: string; + target_user_id: string; + }; + Returns: boolean; + }; + is_account_owner: { Args: { account_id: string }; Returns: boolean }; is_account_team_member: { - Args: { target_account_id: string } - Returns: boolean - } - is_set: { Args: { field_name: string }; Returns: boolean } + Args: { target_account_id: string }; + Returns: boolean; + }; + is_set: { Args: { field_name: string }; Returns: boolean }; is_team_member: { - Args: { account_id: string; user_id: string } - Returns: boolean - } + Args: { account_id: string; user_id: string }; + Returns: boolean; + }; team_account_workspace: { - Args: { account_slug: string } + Args: { account_slug: string }; Returns: { - id: string - name: string - permissions: Database["public"]["Enums"]["app_permissions"][] - picture_url: string - primary_owner_user_id: string - role: string - role_hierarchy_level: number - slug: string - subscription_status: Database["public"]["Enums"]["subscription_status"] - }[] - } + id: string; + name: string; + permissions: Database["public"]["Enums"]["app_permissions"][]; + picture_url: string; + primary_owner_user_id: string; + role: string; + role_hierarchy_level: number; + slug: string; + subscription_status: Database["public"]["Enums"]["subscription_status"]; + }[]; + }; transfer_team_account_ownership: { - Args: { new_owner_id: string; target_account_id: string } - Returns: undefined - } + Args: { new_owner_id: string; target_account_id: string }; + Returns: undefined; + }; upsert_order: { Args: { - billing_provider: Database["public"]["Enums"]["billing_provider"] - currency: string - line_items: Json - status: Database["public"]["Enums"]["payment_status"] - target_account_id: string - target_customer_id: string - target_order_id: string - total_amount: number - } + billing_provider: Database["public"]["Enums"]["billing_provider"]; + currency: string; + line_items: Json; + status: Database["public"]["Enums"]["payment_status"]; + target_account_id: string; + target_customer_id: string; + target_order_id: string; + total_amount: number; + }; Returns: { - account_id: string - billing_customer_id: number - billing_provider: Database["public"]["Enums"]["billing_provider"] - created_at: string - currency: string - id: string - status: Database["public"]["Enums"]["payment_status"] - total_amount: number - updated_at: string - } + account_id: string; + billing_customer_id: number; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + created_at: string; + currency: string; + id: string; + status: Database["public"]["Enums"]["payment_status"]; + total_amount: number; + updated_at: string; + }; SetofOptions: { - from: "*" - to: "orders" - isOneToOne: true - isSetofReturn: false - } - } + from: "*"; + to: "orders"; + isOneToOne: true; + isSetofReturn: false; + }; + }; upsert_subscription: { Args: { - active: boolean - billing_provider: Database["public"]["Enums"]["billing_provider"] - cancel_at_period_end: boolean - currency: string - line_items: Json - period_ends_at: string - period_starts_at: string - status: Database["public"]["Enums"]["subscription_status"] - target_account_id: string - target_customer_id: string - target_subscription_id: string - trial_ends_at?: string - trial_starts_at?: string - } + active: boolean; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + cancel_at_period_end: boolean; + currency: string; + line_items: Json; + period_ends_at: string; + period_starts_at: string; + status: Database["public"]["Enums"]["subscription_status"]; + target_account_id: string; + target_customer_id: string; + target_subscription_id: string; + trial_ends_at?: string; + trial_starts_at?: string; + }; Returns: { - account_id: string - active: boolean - billing_customer_id: number - billing_provider: Database["public"]["Enums"]["billing_provider"] - cancel_at_period_end: boolean - created_at: string - currency: string - id: string - period_ends_at: string - period_starts_at: string - status: Database["public"]["Enums"]["subscription_status"] - trial_ends_at: string | null - trial_starts_at: string | null - updated_at: string - } + account_id: string; + active: boolean; + billing_customer_id: number; + billing_provider: Database["public"]["Enums"]["billing_provider"]; + cancel_at_period_end: boolean; + created_at: string; + currency: string; + id: string; + period_ends_at: string; + period_starts_at: string; + status: Database["public"]["Enums"]["subscription_status"]; + trial_ends_at: string | null; + trial_starts_at: string | null; + updated_at: string; + }; SetofOptions: { - from: "*" - to: "subscriptions" - isOneToOne: true - isSetofReturn: false - } - } - } + from: "*"; + to: "subscriptions"; + isOneToOne: true; + isSetofReturn: false; + }; + }; + }; Enums: { app_permissions: | "roles.manage" @@ -3872,20 +3866,14 @@ export type Database = { | "members.manage" | "invites.manage" | "tasks.write" - | "tasks.delete" - billing_provider: "stripe" | "lemon-squeezy" | "paddle" - chat_role: "user" | "assistant" - notification_channel: "in_app" | "email" - notification_type: "info" | "warning" | "error" - payment_status: "pending" | "succeeded" | "failed" - social_type: - | "TIKTOK" - | "YOUTUBE" - | "INSTAGRAM" - | "TWITTER" - | "SPOTIFY" - | "APPLE" - subscription_item_type: "flat" | "per_seat" | "metered" + | "tasks.delete"; + billing_provider: "stripe" | "lemon-squeezy" | "paddle"; + chat_role: "user" | "assistant"; + notification_channel: "in_app" | "email"; + notification_type: "info" | "warning" | "error"; + payment_status: "pending" | "succeeded" | "failed"; + social_type: "TIKTOK" | "YOUTUBE" | "INSTAGRAM" | "TWITTER" | "SPOTIFY" | "APPLE"; + subscription_item_type: "flat" | "per_seat" | "metered"; subscription_status: | "active" | "trialing" @@ -3894,133 +3882,131 @@ export type Database = { | "unpaid" | "incomplete" | "incomplete_expired" - | "paused" - } + | "paused"; + }; CompositeTypes: { invitation: { - email: string | null - role: string | null - } - } - } -} + email: string | null; + role: string | null; + }; + }; + }; +}; -type DatabaseWithoutInternals = Omit +type DatabaseWithoutInternals = Omit; -type DefaultSchema = DatabaseWithoutInternals[Extract] +type DefaultSchema = DatabaseWithoutInternals[Extract]; export type Tables< DefaultSchemaTableNameOrOptions extends | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) : never = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { - Row: infer R + Row: infer R; } ? R : never - : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & - DefaultSchema["Views"]) - ? (DefaultSchema["Tables"] & - DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { - Row: infer R + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) + ? (DefaultSchema["Tables"] & DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R; } ? R : never - : never + : never; export type TablesInsert< DefaultSchemaTableNameOrOptions extends | keyof DefaultSchema["Tables"] | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Insert: infer I + Insert: infer I; } ? I : never : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { - Insert: infer I + Insert: infer I; } ? I : never - : never + : never; export type TablesUpdate< DefaultSchemaTableNameOrOptions extends | keyof DefaultSchema["Tables"] | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, > = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Update: infer U + Update: infer U; } ? U : never : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { - Update: infer U + Update: infer U; } ? U : never - : never + : never; export type Enums< DefaultSchemaEnumNameOrOptions extends | keyof DefaultSchema["Enums"] | { schema: keyof DatabaseWithoutInternals }, EnumName extends DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] : never = never, > = DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] - : never + : never; export type CompositeTypes< PublicCompositeTypeNameOrOptions extends | keyof DefaultSchema["CompositeTypes"] | { schema: keyof DatabaseWithoutInternals }, CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] : never = never, > = PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals + schema: keyof DatabaseWithoutInternals; } ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] - : never + : never; export const Constants = { public: { @@ -4039,14 +4025,7 @@ export const Constants = { notification_channel: ["in_app", "email"], notification_type: ["info", "warning", "error"], payment_status: ["pending", "succeeded", "failed"], - social_type: [ - "TIKTOK", - "YOUTUBE", - "INSTAGRAM", - "TWITTER", - "SPOTIFY", - "APPLE", - ], + social_type: ["TIKTOK", "YOUTUBE", "INSTAGRAM", "TWITTER", "SPOTIFY", "APPLE"], subscription_item_type: ["flat", "per_seat", "metered"], subscription_status: [ "active", @@ -4060,4 +4039,4 @@ export const Constants = { ], }, }, -} as const +} as const; From 23e7a6ec2863864a4feda05c4ee43b70a181f11e Mon Sep 17 00:00:00 2001 From: Recoup Agent Date: Fri, 13 Mar 2026 17:18:03 +0000 Subject: [PATCH 2/2] agent: address feedback --- app/api/content/create/route.ts | 32 --- app/api/content/estimate/route.ts | 32 --- app/api/content/templates/route.ts | 32 --- app/api/content/validate/route.ts | 32 --- .../whatsApp/isWhatsAppConfigured.ts | 13 -- .../__tests__/createContentHandler.test.ts | 131 ------------ .../getArtistContentReadiness.test.ts | 89 --------- .../getContentEstimateHandler.test.ts | 71 ------- .../getContentTemplatesHandler.test.ts | 46 ----- .../getContentValidateHandler.test.ts | 88 --------- .../persistCreateContentRunVideo.test.ts | 187 ------------------ .../validateCreateContentBody.test.ts | 122 ------------ lib/content/booleanFromString.ts | 10 - lib/content/contentTemplates.ts | 31 --- lib/content/createContentHandler.ts | 85 -------- lib/content/getArtistContentReadiness.ts | 135 ------------- lib/content/getArtistFileTree.ts | 34 ---- lib/content/getArtistRootPrefix.ts | 13 -- lib/content/getContentEstimateHandler.ts | 40 ---- lib/content/getContentTemplatesHandler.ts | 25 --- lib/content/getContentValidateHandler.ts | 45 ----- lib/content/isCompletedRun.ts | 10 - lib/content/persistCreateContentRunVideo.ts | 125 ------------ lib/content/resolveArtistSlug.ts | 20 -- lib/content/validateCreateContentBody.ts | 96 --------- .../validateGetContentEstimateQuery.ts | 41 ---- .../validateGetContentValidateQuery.ts | 46 ----- lib/github/getOrgRepoUrls.ts | 30 --- lib/supabase/files/selectFileByStorageKey.ts | 30 --- .../storage/createSignedFileUrlByKey.ts | 27 --- .../__tests__/triggerCreateContent.test.ts | 32 --- lib/trigger/triggerCreateContent.ts | 24 --- 32 files changed, 1774 deletions(-) delete mode 100644 app/api/content/create/route.ts delete mode 100644 app/api/content/estimate/route.ts delete mode 100644 app/api/content/templates/route.ts delete mode 100644 app/api/content/validate/route.ts delete mode 100644 lib/coding-agent/whatsApp/isWhatsAppConfigured.ts delete mode 100644 lib/content/__tests__/createContentHandler.test.ts delete mode 100644 lib/content/__tests__/getArtistContentReadiness.test.ts delete mode 100644 lib/content/__tests__/getContentEstimateHandler.test.ts delete mode 100644 lib/content/__tests__/getContentTemplatesHandler.test.ts delete mode 100644 lib/content/__tests__/getContentValidateHandler.test.ts delete mode 100644 lib/content/__tests__/persistCreateContentRunVideo.test.ts delete mode 100644 lib/content/__tests__/validateCreateContentBody.test.ts delete mode 100644 lib/content/booleanFromString.ts delete mode 100644 lib/content/contentTemplates.ts delete mode 100644 lib/content/createContentHandler.ts delete mode 100644 lib/content/getArtistContentReadiness.ts delete mode 100644 lib/content/getArtistFileTree.ts delete mode 100644 lib/content/getArtistRootPrefix.ts delete mode 100644 lib/content/getContentEstimateHandler.ts delete mode 100644 lib/content/getContentTemplatesHandler.ts delete mode 100644 lib/content/getContentValidateHandler.ts delete mode 100644 lib/content/isCompletedRun.ts delete mode 100644 lib/content/persistCreateContentRunVideo.ts delete mode 100644 lib/content/resolveArtistSlug.ts delete mode 100644 lib/content/validateCreateContentBody.ts delete mode 100644 lib/content/validateGetContentEstimateQuery.ts delete mode 100644 lib/content/validateGetContentValidateQuery.ts delete mode 100644 lib/github/getOrgRepoUrls.ts delete mode 100644 lib/supabase/files/selectFileByStorageKey.ts delete mode 100644 lib/supabase/storage/createSignedFileUrlByKey.ts delete mode 100644 lib/trigger/__tests__/triggerCreateContent.test.ts delete mode 100644 lib/trigger/triggerCreateContent.ts diff --git a/app/api/content/create/route.ts b/app/api/content/create/route.ts deleted file mode 100644 index 9d1a5fd9..00000000 --- a/app/api/content/create/route.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { createContentHandler } from "@/lib/content/createContentHandler"; - -/** - * OPTIONS handler for CORS preflight requests. - * - * @returns Empty 204 response with CORS headers. - */ -export async function OPTIONS() { - return new NextResponse(null, { - status: 204, - headers: getCorsHeaders(), - }); -} - -/** - * POST /api/content/create - * - * Triggers the background content-creation pipeline and returns a run ID. - * - * @param request - Incoming API request. - * @returns Trigger response for the created task run. - */ -export async function POST(request: NextRequest): Promise { - return createContentHandler(request); -} - -export const dynamic = "force-dynamic"; -export const fetchCache = "force-no-store"; -export const revalidate = 0; - diff --git a/app/api/content/estimate/route.ts b/app/api/content/estimate/route.ts deleted file mode 100644 index e6ad622f..00000000 --- a/app/api/content/estimate/route.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { getContentEstimateHandler } from "@/lib/content/getContentEstimateHandler"; - -/** - * OPTIONS handler for CORS preflight requests. - * - * @returns Empty 204 response with CORS headers. - */ -export async function OPTIONS() { - return new NextResponse(null, { - status: 204, - headers: getCorsHeaders(), - }); -} - -/** - * GET /api/content/estimate - * - * Returns estimated content-creation costs. - * - * @param request - Incoming API request. - * @returns Cost estimate response. - */ -export async function GET(request: NextRequest): Promise { - return getContentEstimateHandler(request); -} - -export const dynamic = "force-dynamic"; -export const fetchCache = "force-no-store"; -export const revalidate = 0; - diff --git a/app/api/content/templates/route.ts b/app/api/content/templates/route.ts deleted file mode 100644 index 11572d48..00000000 --- a/app/api/content/templates/route.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { getContentTemplatesHandler } from "@/lib/content/getContentTemplatesHandler"; - -/** - * OPTIONS handler for CORS preflight requests. - * - * @returns Empty 204 response with CORS headers. - */ -export async function OPTIONS() { - return new NextResponse(null, { - status: 204, - headers: getCorsHeaders(), - }); -} - -/** - * GET /api/content/templates - * - * Lists available templates for the content-creation pipeline. - * - * @param request - Incoming API request. - * @returns Template list response. - */ -export async function GET(request: NextRequest): Promise { - return getContentTemplatesHandler(request); -} - -export const dynamic = "force-dynamic"; -export const fetchCache = "force-no-store"; -export const revalidate = 0; - diff --git a/app/api/content/validate/route.ts b/app/api/content/validate/route.ts deleted file mode 100644 index 9a205cdc..00000000 --- a/app/api/content/validate/route.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { getContentValidateHandler } from "@/lib/content/getContentValidateHandler"; - -/** - * OPTIONS handler for CORS preflight requests. - * - * @returns Empty 204 response with CORS headers. - */ -export async function OPTIONS() { - return new NextResponse(null, { - status: 204, - headers: getCorsHeaders(), - }); -} - -/** - * GET /api/content/validate - * - * Validates whether an artist is ready for content creation. - * - * @param request - Incoming API request. - * @returns Artist readiness response. - */ -export async function GET(request: NextRequest): Promise { - return getContentValidateHandler(request); -} - -export const dynamic = "force-dynamic"; -export const fetchCache = "force-no-store"; -export const revalidate = 0; - diff --git a/lib/coding-agent/whatsApp/isWhatsAppConfigured.ts b/lib/coding-agent/whatsApp/isWhatsAppConfigured.ts deleted file mode 100644 index 8d970266..00000000 --- a/lib/coding-agent/whatsApp/isWhatsAppConfigured.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const WHATSAPP_ENV_VARS = [ - "WHATSAPP_ACCESS_TOKEN", - "WHATSAPP_APP_SECRET", - "WHATSAPP_PHONE_NUMBER_ID", - "WHATSAPP_VERIFY_TOKEN", -] as const; - -/** - * Returns true when all WhatsApp environment variables are configured. - */ -export function isWhatsAppConfigured(): boolean { - return WHATSAPP_ENV_VARS.every(name => !!process.env[name]); -} diff --git a/lib/content/__tests__/createContentHandler.test.ts b/lib/content/__tests__/createContentHandler.test.ts deleted file mode 100644 index 64589da6..00000000 --- a/lib/content/__tests__/createContentHandler.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { NextRequest, NextResponse } from "next/server"; -import { createContentHandler } from "@/lib/content/createContentHandler"; -import { validateCreateContentBody } from "@/lib/content/validateCreateContentBody"; -import { triggerCreateContent } from "@/lib/trigger/triggerCreateContent"; -import { getArtistContentReadiness } from "@/lib/content/getArtistContentReadiness"; - -vi.mock("@/lib/networking/getCorsHeaders", () => ({ - getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), -})); - -vi.mock("@/lib/content/validateCreateContentBody", () => ({ - validateCreateContentBody: vi.fn(), -})); - -vi.mock("@/lib/trigger/triggerCreateContent", () => ({ - triggerCreateContent: vi.fn(), -})); - -vi.mock("@/lib/content/getArtistContentReadiness", () => ({ - getArtistContentReadiness: vi.fn(), -})); - -vi.mock("@/lib/supabase/account_snapshots/selectAccountSnapshots", () => ({ - selectAccountSnapshots: vi.fn(), -})); - -describe("createContentHandler", () => { - beforeEach(() => { - vi.clearAllMocks(); - vi.mocked(getArtistContentReadiness).mockResolvedValue({ - artist_account_id: "art_456", - ready: true, - missing: [], - warnings: [], - githubRepo: "https://github.com/test/repo", - }); - }); - - it("returns validation/auth error when validation fails", async () => { - const errorResponse = NextResponse.json( - { status: "error", error: "Unauthorized" }, - { status: 401 }, - ); - vi.mocked(validateCreateContentBody).mockResolvedValue(errorResponse); - const request = new NextRequest("http://localhost/api/content/create", { method: "POST" }); - - const result = await createContentHandler(request); - - expect(result).toBe(errorResponse); - }); - - it("returns 202 with runIds when trigger succeeds", async () => { - vi.mocked(validateCreateContentBody).mockResolvedValue({ - accountId: "acc_123", - artistAccountId: "art_456", - artistSlug: "gatsby-grace", - template: "artist-caption-bedroom", - lipsync: false, - captionLength: "short", - upscale: false, - batch: 1, - }); - vi.mocked(triggerCreateContent).mockResolvedValue({ id: "run_abc123" } as never); - const request = new NextRequest("http://localhost/api/content/create", { method: "POST" }); - - const result = await createContentHandler(request); - const body = await result.json(); - - expect(result.status).toBe(202); - expect(body.runIds).toEqual(["run_abc123"]); - expect(body.status).toBe("triggered"); - expect(body.artist_account_id).toBe("art_456"); - }); - - it("returns 202 with empty runIds and failed count when trigger fails", async () => { - vi.mocked(validateCreateContentBody).mockResolvedValue({ - accountId: "acc_123", - artistAccountId: "art_456", - artistSlug: "gatsby-grace", - template: "artist-caption-bedroom", - lipsync: false, - captionLength: "short", - upscale: false, - batch: 1, - }); - vi.mocked(triggerCreateContent).mockRejectedValue(new Error("Trigger unavailable")); - const request = new NextRequest("http://localhost/api/content/create", { method: "POST" }); - - const result = await createContentHandler(request); - const body = await result.json(); - - expect(result.status).toBe(202); - expect(body.runIds).toEqual([]); - expect(body.failed).toBe(1); - }); - - it("still triggers when readiness check finds missing files (best-effort)", async () => { - vi.mocked(validateCreateContentBody).mockResolvedValue({ - accountId: "acc_123", - artistAccountId: "art_456", - artistSlug: "gatsby-grace", - template: "artist-caption-bedroom", - lipsync: false, - captionLength: "short", - upscale: false, - batch: 1, - }); - vi.mocked(getArtistContentReadiness).mockResolvedValue({ - artist_account_id: "art_456", - ready: false, - missing: [ - { - file: "context/images/face-guide.png", - severity: "required", - fix: "Generate a face guide image before creating content.", - }, - ], - warnings: [], - }); - const request = new NextRequest("http://localhost/api/content/create", { method: "POST" }); - - const result = await createContentHandler(request); - const body = await result.json(); - - // Best-effort: validation doesn't block, task handles its own file discovery - expect(result.status).toBe(202); - expect(body.runIds).toBeDefined(); - }); -}); - diff --git a/lib/content/__tests__/getArtistContentReadiness.test.ts b/lib/content/__tests__/getArtistContentReadiness.test.ts deleted file mode 100644 index e7cbf03e..00000000 --- a/lib/content/__tests__/getArtistContentReadiness.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { getArtistContentReadiness } from "@/lib/content/getArtistContentReadiness"; -import { selectAccountSnapshots } from "@/lib/supabase/account_snapshots/selectAccountSnapshots"; -import { getRepoFileTree } from "@/lib/github/getRepoFileTree"; - -vi.mock("@/lib/supabase/account_snapshots/selectAccountSnapshots", () => ({ - selectAccountSnapshots: vi.fn(), -})); - -vi.mock("@/lib/github/getRepoFileTree", () => ({ - getRepoFileTree: vi.fn(), -})); - -describe("getArtistContentReadiness", () => { - beforeEach(() => { - vi.clearAllMocks(); - vi.mocked(selectAccountSnapshots).mockResolvedValue([ - { - github_repo: "https://github.com/test-org/test-repo", - }, - ] as never); - }); - - it("returns ready=true when required files exist", async () => { - vi.mocked(getRepoFileTree).mockResolvedValue([ - { path: "artists/gatsby-grace/context/images/face-guide.png", type: "blob", sha: "1" }, - { path: "artists/gatsby-grace/config/content-creation/config.json", type: "blob", sha: "2" }, - { path: "artists/gatsby-grace/songs/song-a.mp3", type: "blob", sha: "3" }, - { path: "artists/gatsby-grace/context/artist.md", type: "blob", sha: "4" }, - { path: "artists/gatsby-grace/context/audience.md", type: "blob", sha: "5" }, - { path: "artists/gatsby-grace/context/era.json", type: "blob", sha: "6" }, - ]); - - const result = await getArtistContentReadiness({ - accountId: "acc_123", - artistAccountId: "art_456", - artistSlug: "gatsby-grace", - }); - - expect(result.ready).toBe(true); - expect(result.missing).toEqual([]); - }); - - it("returns ready=false with required issues when core files are missing", async () => { - vi.mocked(getRepoFileTree).mockResolvedValue([ - { path: "artists/gatsby-grace/context/artist.md", type: "blob", sha: "1" }, - ]); - - const result = await getArtistContentReadiness({ - accountId: "acc_123", - artistAccountId: "art_456", - artistSlug: "gatsby-grace", - }); - - expect(result.ready).toBe(false); - expect(result.missing.some(item => item.file === "context/images/face-guide.png")).toBe(true); - expect(result.missing.some(item => item.file === "songs/*.mp3")).toBe(true); - }); - - it("returns ready=true when only face-guide and mp3 exist (config.json is optional)", async () => { - vi.mocked(getRepoFileTree).mockResolvedValue([ - { path: "artists/gatsby-grace/context/images/face-guide.png", type: "blob", sha: "1" }, - { path: "artists/gatsby-grace/songs/track.mp3", type: "blob", sha: "2" }, - ]); - - const result = await getArtistContentReadiness({ - accountId: "acc_123", - artistAccountId: "art_456", - artistSlug: "gatsby-grace", - }); - - expect(result.ready).toBe(true); - expect(result.missing).toEqual([]); - // config.json appears as a warning, not a blocker - expect(result.warnings.some(item => item.file === "config/content-creation/config.json")).toBe(true); - }); - - it("throws when account has no github repo", async () => { - vi.mocked(selectAccountSnapshots).mockResolvedValue([] as never); - - await expect( - getArtistContentReadiness({ - accountId: "acc_123", - artistSlug: "gatsby-grace", - }), - ).rejects.toThrow("No GitHub repository configured for this account"); - }); -}); - diff --git a/lib/content/__tests__/getContentEstimateHandler.test.ts b/lib/content/__tests__/getContentEstimateHandler.test.ts deleted file mode 100644 index 926a299f..00000000 --- a/lib/content/__tests__/getContentEstimateHandler.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { NextRequest, NextResponse } from "next/server"; -import { getContentEstimateHandler } from "@/lib/content/getContentEstimateHandler"; -import { validateGetContentEstimateQuery } from "@/lib/content/validateGetContentEstimateQuery"; - -vi.mock("@/lib/networking/getCorsHeaders", () => ({ - getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), -})); - -vi.mock("@/lib/content/validateGetContentEstimateQuery", () => ({ - validateGetContentEstimateQuery: vi.fn(), -})); - -describe("getContentEstimateHandler", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("returns validation error when query validation fails", async () => { - const errorResponse = NextResponse.json( - { status: "error", error: "invalid query" }, - { status: 400 }, - ); - vi.mocked(validateGetContentEstimateQuery).mockResolvedValue(errorResponse); - const request = new NextRequest("http://localhost/api/content/estimate", { method: "GET" }); - - const result = await getContentEstimateHandler(request); - expect(result).toBe(errorResponse); - }); - - it("returns estimate payload", async () => { - vi.mocked(validateGetContentEstimateQuery).mockResolvedValue({ - lipsync: false, - batch: 2, - compare: false, - }); - const request = new NextRequest("http://localhost/api/content/estimate?batch=2", { - method: "GET", - }); - - const result = await getContentEstimateHandler(request); - const body = await result.json(); - - expect(result.status).toBe(200); - expect(body.status).toBe("success"); - expect(body.per_video_estimate_usd).toBe(0.82); - expect(body.total_estimate_usd).toBe(1.64); - expect(body.profiles).toBeUndefined(); - }); - - it("returns compare profiles when compare=true", async () => { - vi.mocked(validateGetContentEstimateQuery).mockResolvedValue({ - lipsync: true, - batch: 1, - compare: true, - }); - const request = new NextRequest( - "http://localhost/api/content/estimate?compare=true&lipsync=true", - { - method: "GET", - }, - ); - - const result = await getContentEstimateHandler(request); - const body = await result.json(); - - expect(result.status).toBe(200); - expect(body.per_video_estimate_usd).toBe(0.95); - expect(body.profiles.current).toBe(0.95); - }); -}); diff --git a/lib/content/__tests__/getContentTemplatesHandler.test.ts b/lib/content/__tests__/getContentTemplatesHandler.test.ts deleted file mode 100644 index b7d45a16..00000000 --- a/lib/content/__tests__/getContentTemplatesHandler.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { NextRequest, NextResponse } from "next/server"; -import { getContentTemplatesHandler } from "@/lib/content/getContentTemplatesHandler"; -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; - -vi.mock("@/lib/networking/getCorsHeaders", () => ({ - getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), -})); - -vi.mock("@/lib/auth/validateAuthContext", () => ({ - validateAuthContext: vi.fn(), -})); - -describe("getContentTemplatesHandler", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("returns auth error when auth validation fails", async () => { - vi.mocked(validateAuthContext).mockResolvedValue( - NextResponse.json({ status: "error", error: "Unauthorized" }, { status: 401 }), - ); - const request = new NextRequest("http://localhost/api/content/templates", { method: "GET" }); - - const result = await getContentTemplatesHandler(request); - - expect(result.status).toBe(401); - }); - - it("returns templates when authenticated", async () => { - vi.mocked(validateAuthContext).mockResolvedValue({ - accountId: "acc_123", - orgId: null, - authToken: "test-key", - }); - const request = new NextRequest("http://localhost/api/content/templates", { method: "GET" }); - - const result = await getContentTemplatesHandler(request); - const body = await result.json(); - - expect(result.status).toBe(200); - expect(body.status).toBe("success"); - expect(Array.isArray(body.templates)).toBe(true); - expect(body.templates.length).toBeGreaterThan(0); - }); -}); diff --git a/lib/content/__tests__/getContentValidateHandler.test.ts b/lib/content/__tests__/getContentValidateHandler.test.ts deleted file mode 100644 index 136d8333..00000000 --- a/lib/content/__tests__/getContentValidateHandler.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { NextRequest, NextResponse } from "next/server"; -import { getContentValidateHandler } from "@/lib/content/getContentValidateHandler"; -import { validateGetContentValidateQuery } from "@/lib/content/validateGetContentValidateQuery"; -import { getArtistContentReadiness } from "@/lib/content/getArtistContentReadiness"; - -vi.mock("@/lib/networking/getCorsHeaders", () => ({ - getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), -})); - -vi.mock("@/lib/content/validateGetContentValidateQuery", () => ({ - validateGetContentValidateQuery: vi.fn(), -})); - -vi.mock("@/lib/content/getArtistContentReadiness", () => ({ - getArtistContentReadiness: vi.fn(), -})); - -describe("getContentValidateHandler", () => { - beforeEach(() => { - vi.clearAllMocks(); - vi.mocked(getArtistContentReadiness).mockResolvedValue({ - artist_account_id: "550e8400-e29b-41d4-a716-446655440000", - ready: true, - missing: [], - warnings: [], - githubRepo: "https://github.com/test/repo", - }); - }); - - it("returns validation error when query validation fails", async () => { - const errorResponse = NextResponse.json( - { status: "error", error: "artist_account_id query parameter is required" }, - { status: 400 }, - ); - vi.mocked(validateGetContentValidateQuery).mockResolvedValue(errorResponse); - const request = new NextRequest("http://localhost/api/content/validate", { method: "GET" }); - - const result = await getContentValidateHandler(request); - expect(result).toBe(errorResponse); - }); - - it("returns readiness payload when validation succeeds", async () => { - vi.mocked(validateGetContentValidateQuery).mockResolvedValue({ - accountId: "acc_123", - artistAccountId: "550e8400-e29b-41d4-a716-446655440000", - artistSlug: "gatsby-grace", - }); - const request = new NextRequest( - "http://localhost/api/content/validate?artist_account_id=550e8400-e29b-41d4-a716-446655440000", - { - method: "GET", - }, - ); - - const result = await getContentValidateHandler(request); - const body = await result.json(); - - expect(result.status).toBe(200); - expect(body.status).toBe("success"); - expect(body.ready).toBe(true); - expect(body.artist_account_id).toBe("550e8400-e29b-41d4-a716-446655440000"); - expect(Array.isArray(body.missing)).toBe(true); - }); - - it("returns 500 when readiness check throws", async () => { - vi.mocked(validateGetContentValidateQuery).mockResolvedValue({ - accountId: "acc_123", - artistAccountId: "550e8400-e29b-41d4-a716-446655440000", - artistSlug: "gatsby-grace", - }); - vi.mocked(getArtistContentReadiness).mockRejectedValue( - new Error("Failed to retrieve repository file tree"), - ); - const request = new NextRequest( - "http://localhost/api/content/validate?artist_account_id=550e8400-e29b-41d4-a716-446655440000", - { - method: "GET", - }, - ); - - const result = await getContentValidateHandler(request); - const body = await result.json(); - - expect(result.status).toBe(500); - expect(body.error).toBe("Failed to validate content readiness"); - }); -}); diff --git a/lib/content/__tests__/persistCreateContentRunVideo.test.ts b/lib/content/__tests__/persistCreateContentRunVideo.test.ts deleted file mode 100644 index c1c16828..00000000 --- a/lib/content/__tests__/persistCreateContentRunVideo.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { persistCreateContentRunVideo } from "@/lib/content/persistCreateContentRunVideo"; -import { CREATE_CONTENT_TASK_ID } from "@/lib/const"; -import { selectFileByStorageKey } from "@/lib/supabase/files/selectFileByStorageKey"; -import { uploadFileByKey } from "@/lib/supabase/storage/uploadFileByKey"; -import { createFileRecord } from "@/lib/supabase/files/createFileRecord"; -import { createSignedFileUrlByKey } from "@/lib/supabase/storage/createSignedFileUrlByKey"; - -vi.mock("@/lib/supabase/files/selectFileByStorageKey", () => ({ - selectFileByStorageKey: vi.fn(), -})); - -vi.mock("@/lib/supabase/storage/uploadFileByKey", () => ({ - uploadFileByKey: vi.fn(), -})); - -vi.mock("@/lib/supabase/files/createFileRecord", () => ({ - createFileRecord: vi.fn(), -})); - -vi.mock("@/lib/supabase/storage/createSignedFileUrlByKey", () => ({ - createSignedFileUrlByKey: vi.fn(), -})); - -describe("persistCreateContentRunVideo", () => { - beforeEach(() => { - vi.clearAllMocks(); - vi.mocked(createSignedFileUrlByKey).mockResolvedValue("https://example.com/signed.mp4"); - }); - - it("returns run unchanged when task is not create-content", async () => { - const run = { id: "run_1", taskIdentifier: "other-task", status: "COMPLETED", output: {} }; - const result = await persistCreateContentRunVideo(run); - expect(result).toEqual(run); - }); - - it("hydrates from existing file without uploading", async () => { - vi.mocked(selectFileByStorageKey).mockResolvedValue({ - id: "file_1", - owner_account_id: "acc_1", - artist_account_id: "acc_1", - storage_key: "content/acc_1/artist/run_1.mp4", - file_name: "artist-run_1.mp4", - mime_type: "video/mp4", - size_bytes: 100, - description: null, - tags: [], - }); - - const run = { - id: "run_1", - taskIdentifier: CREATE_CONTENT_TASK_ID, - status: "COMPLETED", - output: { - accountId: "acc_1", - artistSlug: "artist", - template: "artist-caption-bedroom", - lipsync: false, - videoSourceUrl: "https://example.com/video.mp4", - }, - }; - const result = await persistCreateContentRunVideo(run); - - expect(uploadFileByKey).not.toHaveBeenCalled(); - expect(createFileRecord).not.toHaveBeenCalled(); - expect((result.output as { video?: { fileId: string; signedUrl: string } }).video?.fileId).toBe( - "file_1", - ); - expect( - (result.output as { video?: { fileId: string; signedUrl: string } }).video?.signedUrl, - ).toBe("https://example.com/signed.mp4"); - }); - - it("downloads and persists video when file does not exist", async () => { - vi.mocked(selectFileByStorageKey).mockResolvedValue(null); - vi.mocked(createFileRecord).mockResolvedValue({ - id: "file_2", - owner_account_id: "acc_1", - artist_account_id: "acc_1", - storage_key: "content/acc_1/artist/artist-run_2.mp4", - file_name: "artist-run_2.mp4", - mime_type: "video/mp4", - size_bytes: 100, - description: null, - tags: [], - }); - - vi.stubGlobal( - "fetch", - vi.fn().mockResolvedValue({ - ok: true, - status: 200, - statusText: "OK", - headers: new Headers({ "content-type": "video/mp4" }), - blob: async () => new Blob(["video-bytes"], { type: "video/mp4" }), - }), - ); - - const run = { - id: "run_2", - taskIdentifier: CREATE_CONTENT_TASK_ID, - status: "COMPLETED", - output: { - accountId: "acc_1", - artistSlug: "artist", - template: "artist-caption-bedroom", - lipsync: false, - videoSourceUrl: "https://example.com/video.mp4", - }, - }; - const result = await persistCreateContentRunVideo(run); - - expect(uploadFileByKey).toHaveBeenCalledOnce(); - expect(createFileRecord).toHaveBeenCalledOnce(); - expect((result.output as { video?: { fileId: string; signedUrl: string } }).video?.fileId).toBe( - "file_2", - ); - expect( - (result.output as { video?: { fileId: string; signedUrl: string } }).video?.signedUrl, - ).toBe("https://example.com/signed.mp4"); - }); - - it("throws when upload fails", async () => { - vi.mocked(selectFileByStorageKey).mockResolvedValue(null); - vi.mocked(uploadFileByKey).mockRejectedValue(new Error("upload failed")); - - vi.stubGlobal( - "fetch", - vi.fn().mockResolvedValue({ - ok: true, - status: 200, - statusText: "OK", - headers: new Headers({ "content-type": "video/mp4" }), - blob: async () => new Blob(["video-bytes"], { type: "video/mp4" }), - }), - ); - - const run = { - id: "run_3", - taskIdentifier: CREATE_CONTENT_TASK_ID, - status: "COMPLETED", - output: { - accountId: "acc_1", - artistSlug: "artist", - template: "artist-caption-bedroom", - lipsync: false, - videoSourceUrl: "https://example.com/video.mp4", - }, - }; - - await expect(persistCreateContentRunVideo(run)).rejects.toThrow("upload failed"); - expect(createFileRecord).not.toHaveBeenCalled(); - }); - - it("throws when file record creation fails", async () => { - vi.mocked(selectFileByStorageKey).mockResolvedValue(null); - vi.mocked(uploadFileByKey).mockResolvedValue(undefined); - vi.mocked(createFileRecord).mockRejectedValue(new Error("create file record failed")); - - vi.stubGlobal( - "fetch", - vi.fn().mockResolvedValue({ - ok: true, - status: 200, - statusText: "OK", - headers: new Headers({ "content-type": "video/mp4" }), - blob: async () => new Blob(["video-bytes"], { type: "video/mp4" }), - }), - ); - - const run = { - id: "run_4", - taskIdentifier: CREATE_CONTENT_TASK_ID, - status: "COMPLETED", - output: { - accountId: "acc_1", - artistSlug: "artist", - template: "artist-caption-bedroom", - lipsync: false, - videoSourceUrl: "https://example.com/video.mp4", - }, - }; - - await expect(persistCreateContentRunVideo(run)).rejects.toThrow("Failed to create or find file record"); - }); -}); - diff --git a/lib/content/__tests__/validateCreateContentBody.test.ts b/lib/content/__tests__/validateCreateContentBody.test.ts deleted file mode 100644 index 1bc66a43..00000000 --- a/lib/content/__tests__/validateCreateContentBody.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { NextRequest, NextResponse } from "next/server"; -import { validateCreateContentBody } from "@/lib/content/validateCreateContentBody"; - -const mockValidateAuthContext = vi.fn(); - -vi.mock("@/lib/auth/validateAuthContext", () => ({ - validateAuthContext: (...args: unknown[]) => mockValidateAuthContext(...args), -})); - -vi.mock("@/lib/networking/getCorsHeaders", () => ({ - getCorsHeaders: vi.fn(() => ({ "Access-Control-Allow-Origin": "*" })), -})); - -vi.mock("@/lib/networking/safeParseJson", () => ({ - safeParseJson: vi.fn(async (req: Request) => req.json()), -})); - -vi.mock("@/lib/content/resolveArtistSlug", () => ({ - resolveArtistSlug: vi.fn().mockResolvedValue("gatsby-grace"), -})); - -function createRequest(body: unknown): NextRequest { - return new NextRequest("http://localhost/api/content/create", { - method: "POST", - headers: { "Content-Type": "application/json", "x-api-key": "test-key" }, - body: JSON.stringify(body), - }); -} - -describe("validateCreateContentBody", () => { - beforeEach(() => { - vi.clearAllMocks(); - mockValidateAuthContext.mockResolvedValue({ - accountId: "acc_123", - orgId: null, - authToken: "test-key", - }); - }); - - it("returns validated payload for a valid request", async () => { - const request = createRequest({ - artist_account_id: "550e8400-e29b-41d4-a716-446655440000", - template: "artist-caption-bedroom", - lipsync: true, - }); - - const result = await validateCreateContentBody(request); - - expect(result).not.toBeInstanceOf(NextResponse); - if (!(result instanceof NextResponse)) { - expect(result).toEqual({ - accountId: "acc_123", - artistAccountId: "550e8400-e29b-41d4-a716-446655440000", - artistSlug: "gatsby-grace", - template: "artist-caption-bedroom", - lipsync: true, - captionLength: "short", - upscale: false, - batch: 1, - }); - } - }); - - it("applies defaults when optional fields are omitted", async () => { - const request = createRequest({ - artist_account_id: "550e8400-e29b-41d4-a716-446655440000", - }); - - const result = await validateCreateContentBody(request); - - expect(result).not.toBeInstanceOf(NextResponse); - if (!(result instanceof NextResponse)) { - expect(result.template).toBe("artist-caption-bedroom"); - expect(result.lipsync).toBe(false); - } - }); - - it("returns 400 when artist_account_id is missing", async () => { - const request = createRequest({ - template: "artist-caption-bedroom", - }); - - const result = await validateCreateContentBody(request); - - expect(result).toBeInstanceOf(NextResponse); - if (result instanceof NextResponse) { - expect(result.status).toBe(400); - } - }); - - it("returns 400 when template is unsupported", async () => { - const request = createRequest({ - artist_account_id: "550e8400-e29b-41d4-a716-446655440000", - template: "not-a-real-template", - }); - - const result = await validateCreateContentBody(request); - - expect(result).toBeInstanceOf(NextResponse); - if (result instanceof NextResponse) { - expect(result.status).toBe(400); - const body = await result.json(); - expect(body.error).toContain("Unsupported template"); - } - }); - - it("returns auth error response when auth fails", async () => { - mockValidateAuthContext.mockResolvedValue( - NextResponse.json({ status: "error", error: "Unauthorized" }, { status: 401 }), - ); - const request = createRequest({ artist_account_id: "550e8400-e29b-41d4-a716-446655440000" }); - - const result = await validateCreateContentBody(request); - - expect(result).toBeInstanceOf(NextResponse); - if (result instanceof NextResponse) { - expect(result.status).toBe(401); - } - }); -}); - diff --git a/lib/content/booleanFromString.ts b/lib/content/booleanFromString.ts deleted file mode 100644 index df2bc823..00000000 --- a/lib/content/booleanFromString.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { z } from "zod"; - -/** - * Parses a string query param as a boolean. Only "true" → true; everything else → false. - * z.coerce.boolean() would treat any non-empty string (including "false") as true. - */ -export const booleanFromString = z - .enum(["true", "false"]) - .default("false") - .transform(v => v === "true"); diff --git a/lib/content/contentTemplates.ts b/lib/content/contentTemplates.ts deleted file mode 100644 index 6896c195..00000000 --- a/lib/content/contentTemplates.ts +++ /dev/null @@ -1,31 +0,0 @@ -export interface ContentTemplate { - name: string; - description: string; - defaultLipsync: boolean; -} - -export const CONTENT_TEMPLATES: ContentTemplate[] = [ - { - name: "artist-caption-bedroom", - description: "Moody purple bedroom setting", - defaultLipsync: false, - }, - { - name: "artist-caption-outside", - description: "Night street scene", - defaultLipsync: false, - }, - { - name: "artist-caption-stage", - description: "Small venue concert", - defaultLipsync: false, - }, -]; - -/** Derived from the first entry in CONTENT_TEMPLATES to avoid string duplication. */ -export const DEFAULT_CONTENT_TEMPLATE = CONTENT_TEMPLATES[0].name; - -export function isSupportedContentTemplate(template: string): boolean { - return CONTENT_TEMPLATES.some(item => item.name === template); -} - diff --git a/lib/content/createContentHandler.ts b/lib/content/createContentHandler.ts deleted file mode 100644 index b0e845f3..00000000 --- a/lib/content/createContentHandler.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateCreateContentBody } from "@/lib/content/validateCreateContentBody"; -import { triggerCreateContent } from "@/lib/trigger/triggerCreateContent"; -import { getArtistContentReadiness } from "@/lib/content/getArtistContentReadiness"; -import { selectAccountSnapshots } from "@/lib/supabase/account_snapshots/selectAccountSnapshots"; - -/** - * Handler for POST /api/content/create. - * Always returns runIds array (KISS — one response shape for single and batch). - */ -export async function createContentHandler(request: NextRequest): Promise { - const validated = await validateCreateContentBody(request); - if (validated instanceof NextResponse) { - return validated; - } - - try { - // Best-effort readiness check. The task has its own submodule-aware file - // discovery that works even when the API-level check can't find files - // (e.g., artists in org submodule repos). - let githubRepo: string; - try { - const readiness = await getArtistContentReadiness({ - accountId: validated.accountId, - artistAccountId: validated.artistAccountId, - artistSlug: validated.artistSlug, - }); - githubRepo = readiness.githubRepo; - } catch { - // If readiness check fails, still try to resolve the repo - const snapshots = await selectAccountSnapshots(validated.accountId); - const repo = snapshots?.[0]?.github_repo; - if (!repo) { - return NextResponse.json( - { status: "error", error: "No GitHub repository found for this account" }, - { status: 400, headers: getCorsHeaders() }, - ); - } - githubRepo = repo; - } - - const payload = { - accountId: validated.accountId, - artistSlug: validated.artistSlug, - template: validated.template, - lipsync: validated.lipsync, - captionLength: validated.captionLength, - upscale: validated.upscale, - githubRepo, - }; - - // Always use allSettled — works for single and batch. - const count = validated.batch; - const results = await Promise.allSettled( - Array.from({ length: count }, () => triggerCreateContent(payload)), - ); - const runIds = results - .filter(r => r.status === "fulfilled") - .map(r => (r as PromiseFulfilledResult<{ id: string }>).value.id); - const failedCount = results.filter(r => r.status === "rejected").length; - - return NextResponse.json( - { - runIds, - status: "triggered", - artist_account_id: validated.artistAccountId, - template: validated.template, - lipsync: validated.lipsync, - ...(failedCount > 0 && { failed: failedCount }), - }, - { status: 202, headers: getCorsHeaders() }, - ); - } catch (error) { - console.error("Failed to trigger content creation:", error); - return NextResponse.json( - { - status: "error", - error: "Failed to trigger content creation", - }, - { status: 500, headers: getCorsHeaders() }, - ); - } -} diff --git a/lib/content/getArtistContentReadiness.ts b/lib/content/getArtistContentReadiness.ts deleted file mode 100644 index d199c087..00000000 --- a/lib/content/getArtistContentReadiness.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { selectAccountSnapshots } from "@/lib/supabase/account_snapshots/selectAccountSnapshots"; -import { getArtistFileTree } from "@/lib/content/getArtistFileTree"; -import { getArtistRootPrefix } from "@/lib/content/getArtistRootPrefix"; - -type MissingSeverity = "required" | "recommended"; - -export interface ContentReadinessIssue { - file: string; - severity: MissingSeverity; - fix: string; -} - -export interface ArtistContentReadiness { - artist_account_id: string; - ready: boolean; - missing: ContentReadinessIssue[]; - warnings: ContentReadinessIssue[]; - /** The GitHub repo URL for this account's sandbox. */ - githubRepo: string; -} - -/** - * Checks whether an artist has the expected files for content creation. - * Searches the main repo and org submodule repos. - */ -export async function getArtistContentReadiness({ - accountId, - artistAccountId, - artistSlug, -}: { - accountId: string; - artistAccountId: string; - artistSlug: string; -}): Promise { - const snapshots = await selectAccountSnapshots(accountId); - if (!snapshots) { - throw new Error("Failed to query account snapshots"); - } - const githubRepo = snapshots[0]?.github_repo ?? null; - if (!githubRepo) { - throw new Error("No GitHub repository configured for this account"); - } - - const tree = await getArtistFileTree(githubRepo, artistSlug); - if (!tree) { - // Empty repo or artist not found in any repo — return not-ready instead of crashing - return { - artist_account_id: artistAccountId, - ready: false, - missing: [ - { - file: "artists/", - severity: "required" as const, - fix: "No repository file tree found. The sandbox repo may be empty or the artist directory does not exist yet.", - }, - ], - warnings: [], - githubRepo, - }; - } - - const blobPaths = tree.filter(entry => entry.type === "blob").map(entry => entry.path); - const artistRootPrefix = getArtistRootPrefix(blobPaths, artistSlug); - - const hasFile = (relativePath: string): boolean => - blobPaths.some(path => path === `${artistRootPrefix}${relativePath}`); - const hasAnyMp3 = blobPaths.some( - path => - path.startsWith(artistRootPrefix) && - path.toLowerCase().endsWith(".mp3"), - ); - - const issues: ContentReadinessIssue[] = []; - - if (!hasFile("context/images/face-guide.png")) { - issues.push({ - file: "context/images/face-guide.png", - severity: "required", - fix: "Generate a face guide image before creating content.", - }); - } - - // config/content-creation/config.json is optional — the pipeline uses sensible - // defaults and only reads the artist config file to override specific fields. - if (!hasFile("config/content-creation/config.json")) { - issues.push({ - file: "config/content-creation/config.json", - severity: "recommended", - fix: "Add a pipeline config to override default model/resolution settings.", - }); - } - - if (!hasAnyMp3) { - issues.push({ - file: "songs/*.mp3", - severity: "required", - fix: "Add at least one .mp3 file for audio selection.", - }); - } - - if (!hasFile("context/artist.md")) { - issues.push({ - file: "context/artist.md", - severity: "recommended", - fix: "Add artist context to improve caption quality.", - }); - } - - if (!hasFile("context/audience.md")) { - issues.push({ - file: "context/audience.md", - severity: "recommended", - fix: "Add audience context to improve targeting.", - }); - } - - if (!hasFile("context/era.json")) { - issues.push({ - file: "context/era.json", - severity: "recommended", - fix: "Add era metadata to improve song selection relevance.", - }); - } - - const requiredMissing = issues.filter(item => item.severity === "required"); - const warnings = issues.filter(item => item.severity === "recommended"); - - return { - artist_account_id: artistAccountId, - ready: requiredMissing.length === 0, - missing: requiredMissing, - warnings, - githubRepo, - }; -} diff --git a/lib/content/getArtistFileTree.ts b/lib/content/getArtistFileTree.ts deleted file mode 100644 index 908855a0..00000000 --- a/lib/content/getArtistFileTree.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { getRepoFileTree, type FileTreeEntry } from "@/lib/github/getRepoFileTree"; -import { getOrgRepoUrls } from "@/lib/github/getOrgRepoUrls"; - -/** - * Gets the file tree that contains the artist, checking the main repo - * first, then falling back to org submodule repos. - */ -export async function getArtistFileTree( - githubRepo: string, - artistSlug: string, -): Promise { - const mainTree = await getRepoFileTree(githubRepo); - if (mainTree) { - const blobPaths = mainTree.filter(e => e.type === "blob").map(e => e.path); - const hasArtist = blobPaths.some( - p => p.startsWith(`artists/${artistSlug}/`) || p.startsWith(`${artistSlug}/`), - ); - if (hasArtist) return mainTree; - } - - const orgUrls = await getOrgRepoUrls(githubRepo); - for (const orgUrl of orgUrls) { - const orgTree = await getRepoFileTree(orgUrl); - if (orgTree) { - const blobPaths = orgTree.filter(e => e.type === "blob").map(e => e.path); - const hasArtist = blobPaths.some( - p => p.startsWith(`artists/${artistSlug}/`) || p.startsWith(`${artistSlug}/`), - ); - if (hasArtist) return orgTree; - } - } - - return mainTree; -} diff --git a/lib/content/getArtistRootPrefix.ts b/lib/content/getArtistRootPrefix.ts deleted file mode 100644 index 5a777abe..00000000 --- a/lib/content/getArtistRootPrefix.ts +++ /dev/null @@ -1,13 +0,0 @@ -export function getArtistRootPrefix(paths: string[], artistSlug: string): string { - const preferredPrefix = `artists/${artistSlug}/`; - if (paths.some(path => path.startsWith(preferredPrefix))) { - return preferredPrefix; - } - - const directPrefix = `${artistSlug}/`; - if (paths.some(path => path.startsWith(directPrefix))) { - return directPrefix; - } - - return preferredPrefix; -} diff --git a/lib/content/getContentEstimateHandler.ts b/lib/content/getContentEstimateHandler.ts deleted file mode 100644 index 3d4d3a38..00000000 --- a/lib/content/getContentEstimateHandler.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateGetContentEstimateQuery } from "@/lib/content/validateGetContentEstimateQuery"; - -const BASE_IMAGE_TO_VIDEO_COST = 0.82; -const BASE_AUDIO_TO_VIDEO_COST = 0.95; - -/** - * Handler for GET /api/content/estimate. - * - * @param request - */ -export async function getContentEstimateHandler(request: NextRequest): Promise { - const validated = await validateGetContentEstimateQuery(request); - if (validated instanceof NextResponse) { - return validated; - } - - const perVideoEstimate = validated.lipsync ? BASE_AUDIO_TO_VIDEO_COST : BASE_IMAGE_TO_VIDEO_COST; - const totalEstimate = Number((perVideoEstimate * validated.batch).toFixed(2)); - - const response: Record = { - status: "success", - lipsync: validated.lipsync, - batch: validated.batch, - per_video_estimate_usd: perVideoEstimate, - total_estimate_usd: totalEstimate, - }; - - if (validated.compare) { - response.profiles = { - budget: 0.18, - mid: 0.62, - current: validated.lipsync ? BASE_AUDIO_TO_VIDEO_COST : BASE_IMAGE_TO_VIDEO_COST, - }; - } - - return NextResponse.json(response, { status: 200, headers: getCorsHeaders() }); -} diff --git a/lib/content/getContentTemplatesHandler.ts b/lib/content/getContentTemplatesHandler.ts deleted file mode 100644 index d1a65d80..00000000 --- a/lib/content/getContentTemplatesHandler.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { CONTENT_TEMPLATES } from "@/lib/content/contentTemplates"; - -/** - * Handler for GET /api/content/templates. - * - * @param request - */ -export async function getContentTemplatesHandler(request: NextRequest): Promise { - const authResult = await validateAuthContext(request); - if (authResult instanceof NextResponse) { - return authResult; - } - - return NextResponse.json( - { - status: "success", - templates: CONTENT_TEMPLATES, - }, - { status: 200, headers: getCorsHeaders() }, - ); -} diff --git a/lib/content/getContentValidateHandler.ts b/lib/content/getContentValidateHandler.ts deleted file mode 100644 index 268741c1..00000000 --- a/lib/content/getContentValidateHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateGetContentValidateQuery } from "@/lib/content/validateGetContentValidateQuery"; -import { getArtistContentReadiness } from "@/lib/content/getArtistContentReadiness"; - -/** - * Handler for GET /api/content/validate. - * NOTE: Phase 1 returns structural readiness scaffolding. Deep filesystem checks - * are performed in the background task before spend-heavy steps. - */ -export async function getContentValidateHandler(request: NextRequest): Promise { - const validated = await validateGetContentValidateQuery(request); - if (validated instanceof NextResponse) { - return validated; - } - - try { - const readiness = await getArtistContentReadiness({ - accountId: validated.accountId, - artistAccountId: validated.artistAccountId, - artistSlug: validated.artistSlug, - }); - - const { githubRepo: _, ...publicReadiness } = readiness; - - return NextResponse.json( - { - status: "success", - ...publicReadiness, - }, - { status: 200, headers: getCorsHeaders() }, - ); - } catch (error) { - console.error("Failed to validate content readiness:", error); - return NextResponse.json( - { - status: "error", - error: "Failed to validate content readiness", - }, - { status: 500, headers: getCorsHeaders() }, - ); - } -} - diff --git a/lib/content/isCompletedRun.ts b/lib/content/isCompletedRun.ts deleted file mode 100644 index 855ea068..00000000 --- a/lib/content/isCompletedRun.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type TriggerRunLike = { - id: string; - status?: string | null; - taskIdentifier?: string | null; - output?: unknown; -}; - -export function isCompletedRun(run: TriggerRunLike): boolean { - return run.status === "COMPLETED"; -} diff --git a/lib/content/persistCreateContentRunVideo.ts b/lib/content/persistCreateContentRunVideo.ts deleted file mode 100644 index 25a77eed..00000000 --- a/lib/content/persistCreateContentRunVideo.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { CREATE_CONTENT_TASK_ID } from "@/lib/const"; -import { uploadFileByKey } from "@/lib/supabase/storage/uploadFileByKey"; -import { createFileRecord } from "@/lib/supabase/files/createFileRecord"; -import { selectFileByStorageKey } from "@/lib/supabase/files/selectFileByStorageKey"; -import { createSignedFileUrlByKey } from "@/lib/supabase/storage/createSignedFileUrlByKey"; -import { isCompletedRun, type TriggerRunLike } from "@/lib/content/isCompletedRun"; - -type CreateContentOutput = { - status?: string; - accountId?: string; - artistSlug?: string; - template?: string; - lipsync?: boolean; - videoSourceUrl?: string; - video?: { - fileId: string; - storageKey: string; - fileName: string; - mimeType: string | null; - sizeBytes: number | null; - signedUrl: string; - } | null; -}; - -/** - * Persists create-content task video output to Supabase storage + files table - * and returns the run with normalized output. - * - * This keeps Supabase writes in API only. - */ -export async function persistCreateContentRunVideo(run: T): Promise { - if (run.taskIdentifier !== CREATE_CONTENT_TASK_ID || !isCompletedRun(run)) { - return run; - } - - const output = (run.output ?? {}) as CreateContentOutput; - if (!output.accountId || !output.artistSlug || !output.videoSourceUrl) { - return run; - } - - if (output.video?.storageKey) { - return run; - } - - const fileName = `${output.artistSlug}-${run.id}.mp4`; - const storageKey = `content/${output.accountId}/${output.artistSlug}/${fileName}`; - - const existingFile = await selectFileByStorageKey({ - ownerAccountId: output.accountId, - storageKey, - }); - - if (existingFile) { - const signedUrl = await createSignedFileUrlByKey({ - key: existingFile.storage_key, - }); - - return { - ...run, - output: { - ...output, - video: { - fileId: existingFile.id, - storageKey: existingFile.storage_key, - fileName: existingFile.file_name, - mimeType: existingFile.mime_type, - sizeBytes: existingFile.size_bytes, - signedUrl, - }, - }, - }; - } - - const response = await fetch(output.videoSourceUrl); - if (!response.ok) { - throw new Error(`Failed to download rendered video: ${response.status} ${response.statusText}`); - } - - const videoBlob = await response.blob(); - const mimeType = response.headers.get("content-type") || "video/mp4"; - - await uploadFileByKey(storageKey, videoBlob, { - contentType: mimeType, - upsert: true, - }); - - let createdFile; - try { - createdFile = await createFileRecord({ - ownerAccountId: output.accountId, - // Phase 1: artist account mapping is not wired yet, so we scope to owner account. - artistAccountId: output.accountId, - storageKey, - fileName, - mimeType, - sizeBytes: videoBlob.size, - description: `Content pipeline output for ${output.artistSlug}`, - tags: ["content", "video", output.template ?? "unknown-template"], - }); - } catch { - // Race condition: another request may have created the record. Re-select. - const raceFile = await selectFileByStorageKey({ ownerAccountId: output.accountId, storageKey }); - if (!raceFile) throw new Error("Failed to create or find file record"); - createdFile = raceFile; - } - - const signedUrl = await createSignedFileUrlByKey({ - key: createdFile.storage_key, - }); - - return { - ...run, - output: { - ...output, - video: { - fileId: createdFile.id, - storageKey: createdFile.storage_key, - fileName: createdFile.file_name, - mimeType: createdFile.mime_type, - sizeBytes: createdFile.size_bytes, - signedUrl, - }, - }, - }; -} diff --git a/lib/content/resolveArtistSlug.ts b/lib/content/resolveArtistSlug.ts deleted file mode 100644 index c74a8379..00000000 --- a/lib/content/resolveArtistSlug.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { selectAccounts } from "@/lib/supabase/accounts/selectAccounts"; - -/** - * Resolves an artist_account_id to an artist slug (directory name). - * - * The slug is derived from the artist's name, lowercased with spaces - * replaced by hyphens — matching how sandboxes generate folder names. - * - * @param artistAccountId - The artist's account UUID - * @returns The artist slug, or null if not found - */ -export async function resolveArtistSlug( - artistAccountId: string, -): Promise { - const accounts = await selectAccounts(artistAccountId); - const name = accounts[0]?.name; - if (!name) return null; - - return name.toLowerCase().replace(/\s+/g, "-"); -} diff --git a/lib/content/validateCreateContentBody.ts b/lib/content/validateCreateContentBody.ts deleted file mode 100644 index 6d7cdc56..00000000 --- a/lib/content/validateCreateContentBody.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { z } from "zod"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { safeParseJson } from "@/lib/networking/safeParseJson"; -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { - DEFAULT_CONTENT_TEMPLATE, - isSupportedContentTemplate, -} from "@/lib/content/contentTemplates"; -import { resolveArtistSlug } from "@/lib/content/resolveArtistSlug"; - -export const CAPTION_LENGTHS = ["short", "medium", "long"] as const; - -export const createContentBodySchema = z.object({ - artist_account_id: z.string({ message: "artist_account_id is required" }).uuid("artist_account_id must be a valid UUID"), - template: z - .string() - .min(1, "template cannot be empty") - .optional() - .default(DEFAULT_CONTENT_TEMPLATE), - lipsync: z.boolean().optional().default(false), - caption_length: z.enum(CAPTION_LENGTHS).optional().default("short"), - upscale: z.boolean().optional().default(false), - batch: z.number().int().min(1).max(30).optional().default(1), -}); - -export type ValidatedCreateContentBody = { - accountId: string; - artistAccountId: string; - artistSlug: string; - template: string; - lipsync: boolean; - captionLength: "short" | "medium" | "long"; - upscale: boolean; - batch: number; -}; - -/** - * Validates auth and request body for POST /api/content/create. - */ -export async function validateCreateContentBody( - request: NextRequest, -): Promise { - const body = await safeParseJson(request); - const result = createContentBodySchema.safeParse(body); - - if (!result.success) { - const firstError = result.error.issues[0]; - return NextResponse.json( - { - status: "error", - field: firstError.path, - error: firstError.message, - }, - { status: 400, headers: getCorsHeaders() }, - ); - } - - const authResult = await validateAuthContext(request); - - if (authResult instanceof NextResponse) { - return authResult; - } - - const template = result.data.template ?? DEFAULT_CONTENT_TEMPLATE; - if (!isSupportedContentTemplate(template)) { - return NextResponse.json( - { - status: "error", - error: `Unsupported template: ${template}`, - }, - { status: 400, headers: getCorsHeaders() }, - ); - } - - // Resolve artist_account_id → slug (directory name) - const artistSlug = await resolveArtistSlug(result.data.artist_account_id); - if (!artistSlug) { - return NextResponse.json( - { status: "error", error: "Artist not found for the provided artist_account_id" }, - { status: 404, headers: getCorsHeaders() }, - ); - } - - return { - accountId: authResult.accountId, - artistAccountId: result.data.artist_account_id, - artistSlug, - template, - lipsync: result.data.lipsync ?? false, - captionLength: result.data.caption_length ?? "short", - upscale: result.data.upscale ?? false, - batch: result.data.batch ?? 1, - }; -} diff --git a/lib/content/validateGetContentEstimateQuery.ts b/lib/content/validateGetContentEstimateQuery.ts deleted file mode 100644 index 5828e7cc..00000000 --- a/lib/content/validateGetContentEstimateQuery.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { z } from "zod"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { booleanFromString } from "@/lib/content/booleanFromString"; - -export const getContentEstimateQuerySchema = z.object({ - lipsync: booleanFromString, - batch: z.coerce.number().int().min(1).max(100).default(1), - compare: booleanFromString, -}); - -export type ValidatedGetContentEstimateQuery = z.infer; - -/** - * Validates auth and query params for GET /api/content/estimate. - */ -export async function validateGetContentEstimateQuery( - request: NextRequest, -): Promise { - const authResult = await validateAuthContext(request); - if (authResult instanceof NextResponse) { - return authResult; - } - - const lipsync = request.nextUrl.searchParams.get("lipsync") ?? undefined; - const batch = request.nextUrl.searchParams.get("batch") ?? undefined; - const compare = request.nextUrl.searchParams.get("compare") ?? undefined; - const result = getContentEstimateQuerySchema.safeParse({ lipsync, batch, compare }); - - if (!result.success) { - const firstError = result.error.issues[0]; - return NextResponse.json( - { status: "error", error: firstError.message }, - { status: 400, headers: getCorsHeaders() }, - ); - } - - return result.data; -} diff --git a/lib/content/validateGetContentValidateQuery.ts b/lib/content/validateGetContentValidateQuery.ts deleted file mode 100644 index c8084fa6..00000000 --- a/lib/content/validateGetContentValidateQuery.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { validateAuthContext } from "@/lib/auth/validateAuthContext"; -import { resolveArtistSlug } from "@/lib/content/resolveArtistSlug"; - -export type ValidatedGetContentValidateQuery = { - accountId: string; - artistAccountId: string; - artistSlug: string; -}; - -/** - * Validates auth and query params for GET /api/content/validate. - * Requires artist_account_id query parameter. - */ -export async function validateGetContentValidateQuery( - request: NextRequest, -): Promise { - const authResult = await validateAuthContext(request); - if (authResult instanceof NextResponse) { - return authResult; - } - - const artistAccountId = request.nextUrl.searchParams.get("artist_account_id"); - if (!artistAccountId) { - return NextResponse.json( - { status: "error", error: "artist_account_id query parameter is required" }, - { status: 400, headers: getCorsHeaders() }, - ); - } - - const artistSlug = await resolveArtistSlug(artistAccountId); - if (!artistSlug) { - return NextResponse.json( - { status: "error", error: "Artist not found for the provided artist_account_id" }, - { status: 404, headers: getCorsHeaders() }, - ); - } - - return { - accountId: authResult.accountId, - artistAccountId: artistAccountId, - artistSlug, - }; -} diff --git a/lib/github/getOrgRepoUrls.ts b/lib/github/getOrgRepoUrls.ts deleted file mode 100644 index 1aca55c1..00000000 --- a/lib/github/getOrgRepoUrls.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getRepoGitModules } from "./getRepoGitModules"; -import { parseGitHubRepoUrl } from "./parseGitHubRepoUrl"; - -/** - * Gets the GitHub URLs of all org submodule repos for a sandbox repository. - * - * Reads .gitmodules from the main repo and extracts the submodule URLs. - * Used by content readiness checks and artist file fetching. - * - * @param githubRepoUrl - Full GitHub repo URL (e.g. https://github.com/org/repo) - * @param branch - Branch to read .gitmodules from (defaults to "main") - * @returns Array of org repo URLs, or empty array if none found - */ -export async function getOrgRepoUrls( - githubRepoUrl: string, - branch = "main", -): Promise { - const repoInfo = parseGitHubRepoUrl(githubRepoUrl); - if (!repoInfo) return []; - - const submodules = await getRepoGitModules({ - owner: repoInfo.owner, - repo: repoInfo.repo, - branch, - }); - - if (!submodules) return []; - - return submodules.map(s => s.url); -} diff --git a/lib/supabase/files/selectFileByStorageKey.ts b/lib/supabase/files/selectFileByStorageKey.ts deleted file mode 100644 index 78598c3c..00000000 --- a/lib/supabase/files/selectFileByStorageKey.ts +++ /dev/null @@ -1,30 +0,0 @@ -import supabase from "@/lib/supabase/serverClient"; -import type { FileRecord } from "@/lib/supabase/files/createFileRecord"; - -/** - * Select a file record by storage key for an owner account. - * - * @param root0 - * @param root0.ownerAccountId - * @param root0.storageKey - */ -export async function selectFileByStorageKey({ - ownerAccountId, - storageKey, -}: { - ownerAccountId: string; - storageKey: string; -}): Promise { - const { data, error } = await supabase - .from("files") - .select("*") - .eq("owner_account_id", ownerAccountId) - .eq("storage_key", storageKey) - .maybeSingle(); - - if (error) { - throw new Error(`Failed to select file by storage key: ${error.message}`); - } - - return data; -} diff --git a/lib/supabase/storage/createSignedFileUrlByKey.ts b/lib/supabase/storage/createSignedFileUrlByKey.ts deleted file mode 100644 index 30625b7a..00000000 --- a/lib/supabase/storage/createSignedFileUrlByKey.ts +++ /dev/null @@ -1,27 +0,0 @@ -import supabase from "@/lib/supabase/serverClient"; -import { SUPABASE_STORAGE_BUCKET } from "@/lib/const"; - -/** - * Creates a signed URL for a file in Supabase storage. - * - * @param root0 - * @param root0.key - * @param root0.expiresInSeconds - */ -export async function createSignedFileUrlByKey({ - key, - expiresInSeconds = 60 * 60 * 24 * 7, -}: { - key: string; - expiresInSeconds?: number; -}): Promise { - const { data, error } = await supabase.storage - .from(SUPABASE_STORAGE_BUCKET) - .createSignedUrl(key, expiresInSeconds); - - if (error || !data?.signedUrl) { - throw new Error(`Failed to create signed URL: ${error?.message ?? "unknown error"}`); - } - - return data.signedUrl; -} diff --git a/lib/trigger/__tests__/triggerCreateContent.test.ts b/lib/trigger/__tests__/triggerCreateContent.test.ts deleted file mode 100644 index aafc108d..00000000 --- a/lib/trigger/__tests__/triggerCreateContent.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; -import { tasks } from "@trigger.dev/sdk"; -import { triggerCreateContent } from "@/lib/trigger/triggerCreateContent"; -import { CREATE_CONTENT_TASK_ID } from "@/lib/const"; - -vi.mock("@trigger.dev/sdk", () => ({ - tasks: { - trigger: vi.fn(), - }, -})); - -describe("triggerCreateContent", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("triggers create-content task with the expected payload", async () => { - const mockHandle = { id: "run_abc123" }; - vi.mocked(tasks.trigger).mockResolvedValue(mockHandle as never); - - const payload = { - accountId: "acc_123", - artistSlug: "gatsby-grace", - template: "artist-caption-bedroom", - lipsync: true, - }; - const result = await triggerCreateContent(payload); - - expect(tasks.trigger).toHaveBeenCalledWith(CREATE_CONTENT_TASK_ID, payload); - expect(result).toEqual(mockHandle); - }); -}); diff --git a/lib/trigger/triggerCreateContent.ts b/lib/trigger/triggerCreateContent.ts deleted file mode 100644 index 86dab6c8..00000000 --- a/lib/trigger/triggerCreateContent.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { tasks } from "@trigger.dev/sdk"; -import { CREATE_CONTENT_TASK_ID } from "@/lib/const"; - -export interface TriggerCreateContentPayload { - accountId: string; - artistSlug: string; - template: string; - lipsync: boolean; - /** Controls caption length: "short", "medium", or "long". */ - captionLength: "short" | "medium" | "long"; - /** Whether to upscale image and video for higher quality. */ - upscale: boolean; - /** GitHub repo URL so the task can fetch artist files. */ - githubRepo: string; -} - -/** - * Triggers the create-content task in Trigger.dev. - */ -export async function triggerCreateContent(payload: TriggerCreateContentPayload) { - const handle = await tasks.trigger(CREATE_CONTENT_TASK_ID, payload); - return handle; -} -