diff --git a/AGENTS.md b/AGENTS.md index e7190b8a..ade49945 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -261,12 +261,12 @@ const { accountId, orgId, authToken } = authResult; `validateAuthContext` handles: - Both `x-api-key` and `Authorization: Bearer` authentication -- Account ID override validation (org keys can access member accounts) +- Account ID override validation (accounts with shared org membership can access member accounts) - Organization access validation ### MCP Tools -**CRITICAL: Never manually extract `accountId` from `extra.authInfo` (e.g. `authInfo?.extra?.accountId`).** Always use `resolveAccountId()` — it handles validation, org-key overrides, and access control in one place. +**CRITICAL: Never manually extract `accountId` from `extra.authInfo` (e.g. `authInfo?.extra?.accountId`).** Always use `resolveAccountId()` — it handles validation, org-membership overrides, and access control in one place. ```typescript import { resolveAccountId } from "@/lib/mcp/resolveAccountId"; diff --git a/app/api/accounts/[id]/route.ts b/app/api/accounts/[id]/route.ts index 3ed40db1..42b388bf 100644 --- a/app/api/accounts/[id]/route.ts +++ b/app/api/accounts/[id]/route.ts @@ -23,6 +23,7 @@ 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 */ diff --git a/app/api/admins/privy/route.ts b/app/api/admins/privy/route.ts index 073bac60..d22ec616 100644 --- a/app/api/admins/privy/route.ts +++ b/app/api/admins/privy/route.ts @@ -8,11 +8,16 @@ import { getPrivyLoginsHandler } from "@/lib/admins/privy/getPrivyLoginsHandler" * Returns Privy login statistics for the requested time period. * Supports daily (last 24h), weekly (last 7 days), and monthly (last 30 days) periods. * Requires admin authentication. + * + * @param request */ export async function GET(request: NextRequest): Promise { return getPrivyLoginsHandler(request); } +/** + * + */ export async function OPTIONS(): Promise { return new NextResponse(null, { status: 204, headers: getCorsHeaders() }); } diff --git a/app/api/artists/route.ts b/app/api/artists/route.ts index 7fe9a21b..ececa5a1 100644 --- a/app/api/artists/route.ts +++ b/app/api/artists/route.ts @@ -45,7 +45,7 @@ export async function GET(request: NextRequest) { * Request body: * - name (required): The name of the artist to create * - account_id (optional): The ID of the account to create the artist for (UUID). - * Only required for organization API keys creating artists on behalf of other accounts. + * Only required when creating artists on behalf of other accounts within a shared organization. * - organization_id (optional): The organization ID to link the new artist to (UUID) * * @param request - The request object containing JSON body diff --git a/app/api/chat/generate/route.ts b/app/api/chat/generate/route.ts index 0176d0ee..8c47cd93 100644 --- a/app/api/chat/generate/route.ts +++ b/app/api/chat/generate/route.ts @@ -31,7 +31,7 @@ export async function OPTIONS() { * - artistId: Optional UUID of the artist account * - model: Optional model ID override * - excludeTools: Optional array of tool names to exclude - * - accountId: Optional accountId override (requires org API key) + * - accountId: Optional accountId override (requires shared org membership or admin access) * * Response body: * - text: The generated text response diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 973166f0..2976cdc5 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -31,7 +31,7 @@ export async function OPTIONS() { * - artistId: Optional UUID of the artist account * - model: Optional model ID override * - excludeTools: Optional array of tool names to exclude - * - accountId: Optional accountId override (requires org API key) + * - accountId: Optional accountId override (requires shared org membership or admin access) * * @param request - The request object * @returns A streaming response or error diff --git a/app/api/coding-agent/[platform]/route.ts b/app/api/coding-agent/[platform]/route.ts index a51a2104..6fa942ac 100644 --- a/app/api/coding-agent/[platform]/route.ts +++ b/app/api/coding-agent/[platform]/route.ts @@ -10,6 +10,7 @@ import "@/lib/coding-agent/handlers/registerHandlers"; * Handles webhook verification handshakes (e.g. WhatsApp hub.challenge). * * @param request - The incoming verification request + * @param params.params * @param params - Route params containing the platform name */ export async function GET( @@ -34,6 +35,7 @@ export async function GET( * Handles Slack and WhatsApp 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( diff --git a/app/api/notifications/route.ts b/app/api/notifications/route.ts index 22597e8f..778e2ea6 100644 --- a/app/api/notifications/route.ts +++ b/app/api/notifications/route.ts @@ -28,7 +28,7 @@ export async function OPTIONS() { * - cc (optional): array of CC email addresses * - headers (optional): custom email headers * - room_id (optional): room ID for chat link in footer - * - account_id (optional): UUID of the account to send to (org keys only) + * - account_id (optional): UUID of the account to send to (requires shared org membership or admin access) * * @param request - The request object. * @returns A NextResponse with send result. diff --git a/app/api/organizations/route.ts b/app/api/organizations/route.ts index 10aae90d..a45d39fb 100644 --- a/app/api/organizations/route.ts +++ b/app/api/organizations/route.ts @@ -21,12 +21,11 @@ export async function OPTIONS() { * Retrieves all organizations an account belongs to. * Requires authentication via x-api-key or Authorization bearer token. * - * For personal keys: returns the key owner's organizations. - * For org keys: returns organizations for all accounts in the org. + * Returns the key owner's organizations. * For Recoup admin: returns all organizations. * * Query parameters: - * - account_id (optional): Filter to a specific account (org keys only) + * - account_id (optional): Filter to a specific account (requires shared org membership or admin access) * * @param request - The request object * @returns A NextResponse with organizations data diff --git a/app/api/pulses/route.ts b/app/api/pulses/route.ts index 5c43dcc0..a0c8732f 100644 --- a/app/api/pulses/route.ts +++ b/app/api/pulses/route.ts @@ -21,12 +21,11 @@ export async function OPTIONS() { * Retrieves pulse statuses for accounts. * Requires authentication via x-api-key header or Authorization bearer token. * - * For personal keys: Returns array with single pulse for the account. - * For org keys: Returns array of pulses for all accounts in the organization. + * Returns array with pulse for the authenticated account. * For Recoup admin key: Returns array of ALL pulse records. * * Query parameters: - * - account_id: For org API keys, filter to a specific account within the organization + * - account_id: Filter to a specific account (requires shared org membership or admin access) * - active: Filter by active status (true/false). If undefined, returns all. * * @param request - The request object. @@ -47,7 +46,7 @@ export async function GET(request: NextRequest) { * * Body parameters: * - active (required): boolean - Whether pulse is active for this account - * - account_id (optional): For org API keys, target a specific account within the organization + * - account_id (optional): Target a specific account (requires shared org membership or admin access) * * @param request - The request object containing the body with active boolean. * @returns A NextResponse with array of pulse statuses. diff --git a/app/api/sandboxes/route.ts b/app/api/sandboxes/route.ts index 7b3152d1..ee158d84 100644 --- a/app/api/sandboxes/route.ts +++ b/app/api/sandboxes/route.ts @@ -106,14 +106,13 @@ export async function PATCH(request: NextRequest): Promise { * DELETE /api/sandboxes * * Deletes the GitHub repository and snapshot record for an account. - * For personal API keys, deletes the sandbox for the key owner's account. - * Organization API keys may specify account_id to target any account - * within their organization. + * Deletes the sandbox for the key owner's account. + * Accounts with shared org membership may specify account_id to target another account. * * Authentication: x-api-key header or Authorization Bearer token required. * * Request body: - * - account_id: string (optional) - UUID of the account to delete for (org keys only) + * - account_id: string (optional) - UUID of the account to delete for (requires shared org membership or admin access) * * Response (200): * - status: "success" diff --git a/app/api/sandboxes/setup/route.ts b/app/api/sandboxes/setup/route.ts index 8ee33199..22cce7d1 100644 --- a/app/api/sandboxes/setup/route.ts +++ b/app/api/sandboxes/setup/route.ts @@ -20,14 +20,13 @@ export async function OPTIONS() { * * Triggers the setup-sandbox background task to create a personal sandbox, * provision a GitHub repo, take a snapshot, and shut down. - * For personal API keys, sets up the sandbox for the key owner's account. - * Organization API keys may specify account_id to target any account - * within their organization. + * Sets up the sandbox for the key owner's account. + * Accounts with shared org membership may specify account_id to target another account. * * Authentication: x-api-key header or Authorization Bearer token required. * * Request body: - * - account_id: string (optional) - UUID of the account to set up for (org keys only) + * - account_id: string (optional) - UUID of the account to set up for (requires shared org membership or admin access) * * Response (200): * - status: "success" 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/transcribe/route.ts b/app/api/transcribe/route.ts index 28cf4261..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(); diff --git a/app/api/workspaces/route.ts b/app/api/workspaces/route.ts index 60d51a32..89b3717e 100644 --- a/app/api/workspaces/route.ts +++ b/app/api/workspaces/route.ts @@ -22,7 +22,7 @@ export async function OPTIONS() { * Request body: * - name (optional): The name of the workspace to create. Defaults to "Untitled". * - account_id (optional): The ID of the account to create the workspace for (UUID). - * Only required for organization API keys creating workspaces on behalf of other accounts. + * Only required when creating workspaces on behalf of other accounts within a shared organization. * - organization_id (optional): The organization ID to link the new workspace to (UUID). * If provided, the workspace will appear in that organization's view. * Access is validated to ensure the user has access to the organization. diff --git a/lib/accounts/validateOverrideAccountId.ts b/lib/accounts/validateOverrideAccountId.ts index 8f52cde0..7683b743 100644 --- a/lib/accounts/validateOverrideAccountId.ts +++ b/lib/accounts/validateOverrideAccountId.ts @@ -15,8 +15,8 @@ export type ValidateOverrideAccountIdResult = { /** * Validates that an API key has permission to override to a target accountId. * - * Used when an org API key wants to create resources on behalf of another account. - * Checks that the API key belongs to an org with access to the target account. + * Used when an account wants to create resources on behalf of another account. + * Checks that the account has shared org membership with the target account. * * @param params.apiKey - The x-api-key header value * @param params.targetAccountId - The accountId to override to diff --git a/lib/admins/emails/__tests__/validateGetAdminEmailsQuery.test.ts b/lib/admins/emails/__tests__/validateGetAdminEmailsQuery.test.ts index 90e1a3d0..7531a477 100644 --- a/lib/admins/emails/__tests__/validateGetAdminEmailsQuery.test.ts +++ b/lib/admins/emails/__tests__/validateGetAdminEmailsQuery.test.ts @@ -12,6 +12,10 @@ vi.mock("@/lib/admins/validateAdminAuth", () => ({ validateAdminAuth: vi.fn(), })); +/** + * + * @param url + */ function createMockRequest(url: string): NextRequest { return { url, diff --git a/lib/admins/privy/countNewAccounts.ts b/lib/admins/privy/countNewAccounts.ts index 012ced53..1d34a14a 100644 --- a/lib/admins/privy/countNewAccounts.ts +++ b/lib/admins/privy/countNewAccounts.ts @@ -5,6 +5,9 @@ import { getCutoffMs } from "./getCutoffMs"; /** * Counts how many users in the list were created within the cutoff period. + * + * @param users + * @param period */ export function countNewAccounts(users: User[], period: PrivyLoginsPeriod): number { const cutoffMs = getCutoffMs(period); diff --git a/lib/admins/privy/fetchPrivyLogins.ts b/lib/admins/privy/fetchPrivyLogins.ts index ae4d4dd0..35ac556c 100644 --- a/lib/admins/privy/fetchPrivyLogins.ts +++ b/lib/admins/privy/fetchPrivyLogins.ts @@ -20,6 +20,10 @@ export type FetchPrivyLoginsResult = { totalPrivyUsers: number; }; +/** + * + * @param period + */ export async function fetchPrivyLogins(period: PrivyLoginsPeriod): Promise { const isAll = period === "all"; const cutoffMs = getCutoffMs(period); diff --git a/lib/admins/privy/getCutoffMs.ts b/lib/admins/privy/getCutoffMs.ts index 8b80ec6a..4de0fa32 100644 --- a/lib/admins/privy/getCutoffMs.ts +++ b/lib/admins/privy/getCutoffMs.ts @@ -5,6 +5,8 @@ import { PERIOD_DAYS } from "./periodDays"; * Returns the cutoff timestamp in milliseconds for a given period. * Uses midnight UTC calendar day boundaries to match Privy dashboard behavior. * Returns 0 for "all" (no cutoff). + * + * @param period */ export function getCutoffMs(period: PrivyLoginsPeriod): number { if (period === "all") return 0; diff --git a/lib/admins/privy/getLatestVerifiedAt.ts b/lib/admins/privy/getLatestVerifiedAt.ts index 465ea876..c7f7ba9b 100644 --- a/lib/admins/privy/getLatestVerifiedAt.ts +++ b/lib/admins/privy/getLatestVerifiedAt.ts @@ -4,6 +4,8 @@ import type { User } from "@privy-io/node"; /** * Returns the most recent latest_verified_at (in ms) across all linked_accounts for a Privy user. * Returns null if no linked account has a latest_verified_at. + * + * @param user */ export function getLatestVerifiedAt(user: User): number | null { const linkedAccounts = user.linked_accounts; diff --git a/lib/admins/privy/toMs.ts b/lib/admins/privy/toMs.ts index 472ff9eb..2daad687 100644 --- a/lib/admins/privy/toMs.ts +++ b/lib/admins/privy/toMs.ts @@ -1,6 +1,8 @@ /** * Normalizes a Privy timestamp to milliseconds. * Privy docs say milliseconds but examples show seconds (10 digits). + * + * @param timestamp */ export function toMs(timestamp: number): number { return timestamp > 1e12 ? timestamp : timestamp * 1000; diff --git a/lib/ai/getModel.ts b/lib/ai/getModel.ts index edf4d425..99ca9c2f 100644 --- a/lib/ai/getModel.ts +++ b/lib/ai/getModel.ts @@ -3,6 +3,7 @@ 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 */ 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/artists/__tests__/buildGetArtistsParams.test.ts b/lib/artists/__tests__/buildGetArtistsParams.test.ts index 17415027..3a3885d5 100644 --- a/lib/artists/__tests__/buildGetArtistsParams.test.ts +++ b/lib/artists/__tests__/buildGetArtistsParams.test.ts @@ -12,7 +12,7 @@ describe("buildGetArtistsParams", () => { vi.clearAllMocks(); }); - it("returns auth accountId for personal key", async () => { + it("returns auth accountId for authenticated account", async () => { const result = await buildGetArtistsParams({ accountId: "personal-account-123", }); @@ -23,7 +23,7 @@ describe("buildGetArtistsParams", () => { }); }); - it("returns auth accountId for org key", async () => { + it("returns auth accountId for account with org membership", async () => { const result = await buildGetArtistsParams({ accountId: "org-owner-123", }); @@ -87,7 +87,7 @@ describe("buildGetArtistsParams", () => { }); }); - it("allows personal key to access targetAccountId via shared org", async () => { + it("allows account to access targetAccountId via shared org", async () => { vi.mocked(canAccessAccount).mockResolvedValue(true); const result = await buildGetArtistsParams({ @@ -105,7 +105,7 @@ describe("buildGetArtistsParams", () => { }); }); - it("returns error when personal key has no shared org with targetAccountId", async () => { + it("returns error when account has no shared org with targetAccountId", async () => { vi.mocked(canAccessAccount).mockResolvedValue(false); const result = await buildGetArtistsParams({ @@ -119,7 +119,7 @@ describe("buildGetArtistsParams", () => { }); }); - it("returns error when org key lacks access to targetAccountId", async () => { + it("returns error when account lacks access to targetAccountId", async () => { vi.mocked(canAccessAccount).mockResolvedValue(false); const result = await buildGetArtistsParams({ diff --git a/lib/artists/__tests__/createArtistPostHandler.test.ts b/lib/artists/__tests__/createArtistPostHandler.test.ts index e63d244d..7b9dc111 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", @@ -60,7 +65,7 @@ describe("createArtistPostHandler", () => { ); }); - it("uses account_id override for org API keys", async () => { + it("uses account_id override for accounts with org access", async () => { mockValidateAuthContext.mockResolvedValue({ accountId: "550e8400-e29b-41d4-a716-446655440000", // Overridden account orgId: "org-account-id", @@ -90,7 +95,7 @@ describe("createArtistPostHandler", () => { expect(response.status).toBe(201); }); - it("returns 403 when org API key lacks access to account_id", async () => { + it("returns 403 when account lacks access to account_id", async () => { mockValidateAuthContext.mockResolvedValue( NextResponse.json( { status: "error", error: "Access denied to specified account_id" }, diff --git a/lib/artists/__tests__/validateCreateArtistBody.test.ts b/lib/artists/__tests__/validateCreateArtistBody.test.ts index 4de5562b..9b67e43a 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", { @@ -63,7 +68,7 @@ describe("validateCreateArtistBody", () => { } }); - it("uses account_id override for org API keys with access", async () => { + it("uses account_id override for accounts with org access", async () => { mockValidateAuthContext.mockResolvedValue({ accountId: "550e8400-e29b-41d4-a716-446655440000", // Overridden account orgId: "org-account-id", @@ -109,7 +114,7 @@ describe("validateCreateArtistBody", () => { } }); - it("returns 403 when org API key lacks access to account_id", async () => { + it("returns 403 when account lacks access to account_id", async () => { mockValidateAuthContext.mockResolvedValue( NextResponse.json( { status: "error", error: "Access denied to specified account_id" }, diff --git a/lib/artists/__tests__/validateGetArtistsRequest.test.ts b/lib/artists/__tests__/validateGetArtistsRequest.test.ts index c2889da6..45c04dac 100644 --- a/lib/artists/__tests__/validateGetArtistsRequest.test.ts +++ b/lib/artists/__tests__/validateGetArtistsRequest.test.ts @@ -34,7 +34,7 @@ describe("validateGetArtistsRequest", () => { expect((result as NextResponse).status).toBe(401); }); - it("returns personal artists for personal key (no query params)", async () => { + it("returns personal artists for authenticated account (no query params)", async () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId: "personal-123", orgId: null, @@ -51,7 +51,7 @@ describe("validateGetArtistsRequest", () => { }); }); - it("returns personal artists for org key (no query params)", async () => { + it("returns personal artists for account with org membership (no query params)", async () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId: "org-owner-123", orgId: "org-123", @@ -114,7 +114,7 @@ describe("validateGetArtistsRequest", () => { expect((result as NextResponse).status).toBe(400); }); - it("allows org key to filter by account_id within org", async () => { + it("allows filtering by account_id within org", async () => { const mockOrgId = "b2222222-2222-4222-8222-222222222222"; const targetAccountId = "c3333333-3333-4333-8333-333333333333"; vi.mocked(validateAuthContext).mockResolvedValue({ @@ -138,7 +138,7 @@ describe("validateGetArtistsRequest", () => { }); }); - it("returns 403 when personal key tries to filter by account_id", async () => { + it("returns 403 when account tries to filter by account_id without org access", async () => { const otherAccountId = "d4444444-4444-4444-8444-444444444444"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: "personal-123", @@ -154,7 +154,7 @@ describe("validateGetArtistsRequest", () => { expect((result as NextResponse).status).toBe(403); }); - it("returns 403 when org key lacks access to account_id", async () => { + it("returns 403 when account lacks access to account_id", async () => { const mockOrgId = "e5555555-5555-4555-8555-555555555555"; const notInOrgId = "f6666666-6666-4666-8666-666666666666"; vi.mocked(validateAuthContext).mockResolvedValue({ diff --git a/lib/artists/buildGetArtistsParams.ts b/lib/artists/buildGetArtistsParams.ts index f5625e7c..873a6136 100644 --- a/lib/artists/buildGetArtistsParams.ts +++ b/lib/artists/buildGetArtistsParams.ts @@ -17,9 +17,9 @@ export type BuildGetArtistsParamsResult = /** * Builds the parameters for getArtists based on auth context. * - * For personal keys: Returns the key owner's accountId - * For org keys: Returns the key owner's accountId (can override with targetAccountId) - * For Recoup admin key: Returns the key owner's accountId (can override with targetAccountId) + * Returns the key owner's accountId. + * For accounts with org membership: Can override with targetAccountId within the org. + * For Recoup admin key: Can override with any targetAccountId. * * If targetAccountId is provided, validates access and returns that account. * diff --git a/lib/artists/createArtistPostHandler.ts b/lib/artists/createArtistPostHandler.ts index 80cb3adf..93c08352 100644 --- a/lib/artists/createArtistPostHandler.ts +++ b/lib/artists/createArtistPostHandler.ts @@ -8,12 +8,12 @@ import { createArtistInDb } from "@/lib/artists/createArtistInDb"; * * Creates a new artist account. Requires authentication via x-api-key header. * The account ID is inferred from the API key, unless an account_id is provided - * in the request body by an organization API key with access to that account. + * in the request body by an account with shared org membership. * * Request body: * - name (required): The name of the artist to create * - account_id (optional): The ID of the account to create the artist for (UUID). - * Only used by organization API keys creating artists on behalf of other accounts. + * Only used when creating artists on behalf of other accounts within a shared organization. * - organization_id (optional): The organization ID to link the new artist to (UUID) * * @param request - The request object containing JSON body diff --git a/lib/artists/getArtistsHandler.ts b/lib/artists/getArtistsHandler.ts index 384e3425..3ee40d91 100644 --- a/lib/artists/getArtistsHandler.ts +++ b/lib/artists/getArtistsHandler.ts @@ -7,10 +7,10 @@ import { getArtists } from "@/lib/artists/getArtists"; * Handler for retrieving artists with authentication and organization filtering. * * Authenticates via x-api-key or Authorization Bearer token. - * The account is derived from auth; org keys can optionally filter by account_id. + * The account is derived from auth; accounts with org membership can optionally filter by account_id. * * Query parameters: - * - account_id (optional): Filter to a specific account (org keys only) + * - account_id (optional): Filter to a specific account (requires shared org membership or admin access) * - org_id (optional): Filter to artists in a specific organization * - personal (optional): Set to "true" to show only personal (non-org) artists * diff --git a/lib/artists/validateGetArtistsRequest.ts b/lib/artists/validateGetArtistsRequest.ts index 03d2f71c..f38a2c9d 100644 --- a/lib/artists/validateGetArtistsRequest.ts +++ b/lib/artists/validateGetArtistsRequest.ts @@ -17,12 +17,12 @@ export type GetArtistsQuery = z.infer; * Validates GET /api/artists request. * Handles authentication via x-api-key or Authorization bearer token. * - * For personal keys/Bearer tokens: Returns the authenticated account's artists - * For org keys: Returns the key owner's artists (can filter by account_id) - * For Recoup admin key: Returns the key owner's artists (can filter by account_id) + * Returns the authenticated account's artists. + * For accounts with org membership: Can filter by account_id within the org. + * For Recoup admin key: Can filter by any account_id. * * Query parameters: - * - account_id: Filter to a specific account (org keys only) + * - account_id: Filter to a specific account (requires shared org membership or admin access) * - org_id: Filter to artists in a specific organization (omit for personal artists) * * @param request - The NextRequest object diff --git a/lib/auth/__tests__/validateAuthContext.test.ts b/lib/auth/__tests__/validateAuthContext.test.ts index 31dda345..c4769178 100644 --- a/lib/auth/__tests__/validateAuthContext.test.ts +++ b/lib/auth/__tests__/validateAuthContext.test.ts @@ -33,6 +33,10 @@ const mockGetAuthenticatedAccountId = vi.mocked(getAuthenticatedAccountId); const mockValidateOrganizationAccess = vi.mocked(validateOrganizationAccess); const mockCanAccessAccount = vi.mocked(canAccessAccount); +/** + * + * @param headers + */ function createMockRequest(headers: Record = {}): Request { return { headers: { diff --git a/lib/auth/validateAccountIdOverride.ts b/lib/auth/validateAccountIdOverride.ts index 75ba67d2..9195ac92 100644 --- a/lib/auth/validateAccountIdOverride.ts +++ b/lib/auth/validateAccountIdOverride.ts @@ -24,7 +24,7 @@ export interface ValidateAccountIdOverrideResult { * * Access rules: * 1. If targetAccountId equals currentAccountId, always allowed (self-access) - * 2. Delegates to canAccessAccount which handles org key, shared org, and admin access + * 2. Delegates to canAccessAccount which handles shared org membership and admin access * * @param params - The validation parameters * @returns NextResponse with error or the validated result diff --git a/lib/auth/validateAuthContext.ts b/lib/auth/validateAuthContext.ts index b2e8b484..233c122b 100644 --- a/lib/auth/validateAuthContext.ts +++ b/lib/auth/validateAuthContext.ts @@ -36,7 +36,7 @@ export interface AuthContext { * This is the single source of truth for: * 1. Authenticating via x-api-key or Authorization bearer token * 2. Resolving the accountId (from auth or body override) - * 3. Validating account_id override access (org keys can access member accounts, personal keys can access own account) + * 3. Validating account_id override access (accounts with shared org membership can access member accounts) * 4. Validating organization_id access (account must be a member of the org) * * @param request - The NextRequest object diff --git a/lib/catalog/formatCatalogSongsAsCSV.ts b/lib/catalog/formatCatalogSongsAsCSV.ts index 5115eece..29cc443c 100644 --- a/lib/catalog/formatCatalogSongsAsCSV.ts +++ b/lib/catalog/formatCatalogSongsAsCSV.ts @@ -2,6 +2,8 @@ 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 => { 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 c58c33be..d7b5ca62 100644 --- a/lib/catalog/getCatalogSongs.ts +++ b/lib/catalog/getCatalogSongs.ts @@ -25,6 +25,13 @@ export interface CatalogSongsResponse { error?: string; } +/** + * + * @param catalogId + * @param pageSize + * @param page + * @param artistName + */ export async function getCatalogSongs( catalogId: string, pageSize: number = 100, diff --git a/lib/catalog/getCatalogs.ts b/lib/catalog/getCatalogs.ts index 9533183b..4ac8a842 100644 --- a/lib/catalog/getCatalogs.ts +++ b/lib/catalog/getCatalogs.ts @@ -8,6 +8,10 @@ export interface CatalogsResponse { error?: string; } +/** + * + * @param accountId + */ export async function getCatalogs(accountId: string): Promise { try { const response = await fetch( diff --git a/lib/chat/__tests__/handleChatGenerate.test.ts b/lib/chat/__tests__/handleChatGenerate.test.ts index 05e219f5..9a3d19a6 100644 --- a/lib/chat/__tests__/handleChatGenerate.test.ts +++ b/lib/chat/__tests__/handleChatGenerate.test.ts @@ -309,7 +309,7 @@ describe("handleChatGenerate", () => { }); describe("accountId override", () => { - it("allows org API key to override accountId", async () => { + it("allows account with org membership to override accountId", async () => { mockGetApiKeyAccountId.mockResolvedValue("org-account-123"); mockValidateOverrideAccountId.mockResolvedValue({ accountId: "target-account-456", diff --git a/lib/chat/__tests__/handleChatStream.test.ts b/lib/chat/__tests__/handleChatStream.test.ts index b98655e1..2a69d4ad 100644 --- a/lib/chat/__tests__/handleChatStream.test.ts +++ b/lib/chat/__tests__/handleChatStream.test.ts @@ -258,7 +258,7 @@ describe("handleChatStream", () => { }); describe("accountId override", () => { - it("allows org API key to override accountId", async () => { + it("allows account with org membership to override accountId", async () => { mockGetApiKeyAccountId.mockResolvedValue("org-account-123"); mockValidateOverrideAccountId.mockResolvedValue({ accountId: "target-account-456", diff --git a/lib/chat/__tests__/integration/chatEndToEnd.test.ts b/lib/chat/__tests__/integration/chatEndToEnd.test.ts index 02e758be..f08d5bcb 100644 --- a/lib/chat/__tests__/integration/chatEndToEnd.test.ts +++ b/lib/chat/__tests__/integration/chatEndToEnd.test.ts @@ -170,6 +170,11 @@ const mockDeductCredits = vi.mocked(deductCredits); const mockGenerateChatTitle = vi.mocked(generateChatTitle); // Helper to create mock NextRequest +/** + * + * @param body + * @param headers + */ function createMockRequest(body: unknown, headers: Record = {}): Request { return { json: () => Promise.resolve(body), diff --git a/lib/chat/__tests__/validateChatRequest.test.ts b/lib/chat/__tests__/validateChatRequest.test.ts index b420c598..ecf614b2 100644 --- a/lib/chat/__tests__/validateChatRequest.test.ts +++ b/lib/chat/__tests__/validateChatRequest.test.ts @@ -237,7 +237,7 @@ describe("validateChatRequest", () => { expect(json.status).toBe("error"); }); - it("returns accountId for org API key", async () => { + it("returns accountId for API key with org membership", async () => { mockGetApiKeyAccountId.mockResolvedValue("org-account-123"); mockGetApiKeyDetails.mockResolvedValue({ accountId: "org-account-123", @@ -251,7 +251,7 @@ describe("validateChatRequest", () => { expect((result as any).accountId).toBe("org-account-123"); }); - it("returns accountId for personal API key", async () => { + it("returns accountId for API key", async () => { mockGetApiKeyAccountId.mockResolvedValue("personal-account-123"); mockGetApiKeyDetails.mockResolvedValue({ accountId: "personal-account-123", @@ -281,7 +281,7 @@ describe("validateChatRequest", () => { }); describe("accountId override", () => { - it("allows org API key to override accountId", async () => { + it("allows account with org membership to override accountId", async () => { mockGetApiKeyAccountId.mockResolvedValue("org-account-123"); mockValidateOverrideAccountId.mockResolvedValue({ accountId: "target-account-456", diff --git a/lib/chat/toolChains/getPrepareStepResult.ts b/lib/chat/toolChains/getPrepareStepResult.ts index 02dd8e71..c011c078 100644 --- a/lib/chat/toolChains/getPrepareStepResult.ts +++ b/lib/chat/toolChains/getPrepareStepResult.ts @@ -12,6 +12,8 @@ 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 { steps } = options; diff --git a/lib/chats/__tests__/buildGetChatsParams.test.ts b/lib/chats/__tests__/buildGetChatsParams.test.ts index 865f38a2..d1e57654 100644 --- a/lib/chats/__tests__/buildGetChatsParams.test.ts +++ b/lib/chats/__tests__/buildGetChatsParams.test.ts @@ -23,7 +23,7 @@ describe("buildGetChatsParams", () => { }); }); - it("returns error when personal key tries to filter by account_id", async () => { + it("returns error when account lacks access to filter by account_id", async () => { vi.mocked(canAccessAccount).mockResolvedValue(false); const result = await buildGetChatsParams({ diff --git a/lib/chats/__tests__/getChatsHandler.test.ts b/lib/chats/__tests__/getChatsHandler.test.ts index 5a85935e..1c8ea1d0 100644 --- a/lib/chats/__tests__/getChatsHandler.test.ts +++ b/lib/chats/__tests__/getChatsHandler.test.ts @@ -68,8 +68,8 @@ describe("getChatsHandler", () => { }); }); - describe("personal key behavior", () => { - it("returns chats for personal key owner without account_id param", async () => { + describe("authenticated account behavior", () => { + it("returns chats for account owner without account_id param", async () => { const accountId = "123e4567-e89b-12d3-a456-426614174000"; const mockChats = [ { @@ -83,7 +83,7 @@ describe("getChatsHandler", () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); vi.mocked(selectRooms).mockResolvedValue(mockChats); @@ -101,13 +101,13 @@ describe("getChatsHandler", () => { }); }); - it("returns 403 when personal key tries to filter by account_id", async () => { + it("returns 403 when account tries to filter by account_id without access", async () => { const accountId = "123e4567-e89b-12d3-a456-426614174000"; const otherAccountId = "223e4567-e89b-12d3-a456-426614174000"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); vi.mocked(canAccessAccount).mockResolvedValue(false); @@ -123,8 +123,8 @@ describe("getChatsHandler", () => { }); }); - describe("org key behavior", () => { - it("returns chats for org key owner without account_id param", async () => { + describe("org membership behavior", () => { + it("returns chats for account without account_id param", async () => { const orgId = "123e4567-e89b-12d3-a456-426614174000"; const mockChats = [ { @@ -156,7 +156,7 @@ describe("getChatsHandler", () => { }); }); - it("allows org key to filter by account_id for org member", async () => { + it("allows filtering by account_id for org member", async () => { const orgId = "123e4567-e89b-12d3-a456-426614174000"; const memberId = "223e4567-e89b-12d3-a456-426614174000"; const mockChats = [ @@ -189,7 +189,7 @@ describe("getChatsHandler", () => { }); }); - it("returns 403 when org key tries to access non-member account", async () => { + it("returns 403 when trying to access non-member account", async () => { const orgId = "123e4567-e89b-12d3-a456-426614174000"; const nonMemberId = "323e4567-e89b-12d3-a456-426614174000"; @@ -294,7 +294,7 @@ describe("getChatsHandler", () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); vi.mocked(selectRooms).mockResolvedValue(mockChats); diff --git a/lib/chats/__tests__/validateGetChatsRequest.test.ts b/lib/chats/__tests__/validateGetChatsRequest.test.ts index ea86c5ab..f581b071 100644 --- a/lib/chats/__tests__/validateGetChatsRequest.test.ts +++ b/lib/chats/__tests__/validateGetChatsRequest.test.ts @@ -45,11 +45,11 @@ describe("validateGetChatsRequest", () => { expect(response.status).toBe(401); }); - it("should return single account ID for personal key", async () => { + it("should return single account ID for authenticated account", async () => { const mockAccountId = "personal-account-123"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: mockAccountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); @@ -65,7 +65,7 @@ describe("validateGetChatsRequest", () => { }); }); - it("should return account_ids for org key", async () => { + it("should return account_ids when account has org membership", async () => { const mockOrgId = "org-123"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: mockOrgId, @@ -143,12 +143,12 @@ describe("validateGetChatsRequest", () => { expect(response.status).toBe(400); }); - it("should reject personal key trying to filter by account_id", async () => { + it("should reject filtering by account_id without org access", async () => { const mockAccountId = "a1111111-1111-4111-8111-111111111111"; const otherAccountId = "b2222222-2222-4222-8222-222222222222"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: mockAccountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); @@ -162,7 +162,7 @@ describe("validateGetChatsRequest", () => { expect(response.status).toBe(403); }); - it("should allow org key to filter by account_id within org", async () => { + it("should allow filtering by account_id within org", async () => { const mockOrgId = "c3333333-3333-4333-8333-333333333333"; const targetAccountId = "d4444444-4444-4444-8444-444444444444"; vi.mocked(validateAuthContext).mockResolvedValue({ @@ -188,7 +188,7 @@ describe("validateGetChatsRequest", () => { }); }); - it("should reject org key filtering by account_id not in org", async () => { + it("should reject filtering by account_id not in org", async () => { const mockOrgId = "f6666666-6666-4666-8666-666666666666"; const notInOrgId = "b8888888-8888-4888-8888-888888888888"; vi.mocked(validateAuthContext).mockResolvedValue({ diff --git a/lib/chats/__tests__/validateUpdateChatBody.test.ts b/lib/chats/__tests__/validateUpdateChatBody.test.ts index e59e8c64..3d1848f5 100644 --- a/lib/chats/__tests__/validateUpdateChatBody.test.ts +++ b/lib/chats/__tests__/validateUpdateChatBody.test.ts @@ -132,7 +132,7 @@ describe("validateUpdateChatBody", () => { expect(result).toEqual({ chatId, topic }); }); - it("allows org key to access member's chat", async () => { + it("allows account with org membership to access member's chat", async () => { const chatId = "123e4567-e89b-12d3-a456-426614174000"; const memberAccountId = "123e4567-e89b-12d3-a456-426614174001"; const orgId = "123e4567-e89b-12d3-a456-426614174002"; @@ -374,7 +374,7 @@ describe("validateUpdateChatBody", () => { expect(body.error).toContain("Access denied"); }); - it("returns 403 when org key cannot access non-member's chat", async () => { + it("returns 403 when account cannot access non-member's chat", async () => { const orgId = "123e4567-e89b-12d3-a456-426614174001"; const nonMemberAccountId = "123e4567-e89b-12d3-a456-426614174002"; const chatId = "123e4567-e89b-12d3-a456-426614174000"; diff --git a/lib/chats/createChatHandler.ts b/lib/chats/createChatHandler.ts index 3c9dcc69..ec2061ed 100644 --- a/lib/chats/createChatHandler.ts +++ b/lib/chats/createChatHandler.ts @@ -13,7 +13,7 @@ import { generateChatTitle } from "@/lib/chats/generateChatTitle"; * * Requires authentication via x-api-key header. * The account ID is inferred from the API key, unless an accountId is provided - * in the request body by an organization API key with access to that account. + * in the request body by an account with shared org membership. * * @param request - The NextRequest object * @returns A NextResponse with the created chat or an error @@ -42,7 +42,7 @@ export async function createChatHandler(request: NextRequest): Promise Promise.resolve(JSON.stringify(body)), diff --git a/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts b/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts index 8af470e1..f173d6ce 100644 --- a/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts +++ b/lib/coding-agent/__tests__/onMergeTestToMainAction.test.ts @@ -12,6 +12,9 @@ beforeEach(() => { process.env.GITHUB_TOKEN = "ghp_test"; }); +/** + * + */ function createMockBot() { return { onAction: vi.fn() } as any; } 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/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/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/content/__tests__/validateCreateContentBody.test.ts b/lib/content/__tests__/validateCreateContentBody.test.ts index 1a71d5ae..31b1c461 100644 --- a/lib/content/__tests__/validateCreateContentBody.test.ts +++ b/lib/content/__tests__/validateCreateContentBody.test.ts @@ -20,6 +20,10 @@ vi.mock("@/lib/content/resolveArtistSlug", () => ({ resolveArtistSlug: vi.fn().mockResolvedValue("gatsby-grace"), })); +/** + * + * @param body + */ function createRequest(body: unknown): NextRequest { return new NextRequest("http://localhost/api/content/create", { method: "POST", diff --git a/lib/content/contentTemplates.ts b/lib/content/contentTemplates.ts index 7d0653e3..66da0d33 100644 --- a/lib/content/contentTemplates.ts +++ b/lib/content/contentTemplates.ts @@ -25,6 +25,10 @@ export const CONTENT_TEMPLATES: ContentTemplate[] = [ /** Derived from the first entry in CONTENT_TEMPLATES to avoid string duplication. */ export const DEFAULT_CONTENT_TEMPLATE = CONTENT_TEMPLATES[0].name; +/** + * + * @param template + */ 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 index b0e845f3..5459cbb8 100644 --- a/lib/content/createContentHandler.ts +++ b/lib/content/createContentHandler.ts @@ -9,6 +9,8 @@ import { selectAccountSnapshots } from "@/lib/supabase/account_snapshots/selectA /** * Handler for POST /api/content/create. * Always returns runIds array (KISS — one response shape for single and batch). + * + * @param request */ export async function createContentHandler(request: NextRequest): Promise { const validated = await validateCreateContentBody(request); diff --git a/lib/content/getArtistContentReadiness.ts b/lib/content/getArtistContentReadiness.ts index a902ce0f..9238598e 100644 --- a/lib/content/getArtistContentReadiness.ts +++ b/lib/content/getArtistContentReadiness.ts @@ -22,6 +22,11 @@ export interface ArtistContentReadiness { /** * Checks whether an artist has the expected files for content creation. * Searches the main repo and org submodule repos. + * + * @param root0 + * @param root0.accountId + * @param root0.artistAccountId + * @param root0.artistSlug */ export async function getArtistContentReadiness({ accountId, diff --git a/lib/content/getArtistFileTree.ts b/lib/content/getArtistFileTree.ts index 908855a0..b5392b52 100644 --- a/lib/content/getArtistFileTree.ts +++ b/lib/content/getArtistFileTree.ts @@ -4,6 +4,9 @@ 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. + * + * @param githubRepo + * @param artistSlug */ export async function getArtistFileTree( githubRepo: string, diff --git a/lib/content/getArtistRootPrefix.ts b/lib/content/getArtistRootPrefix.ts index 5a777abe..bf81d48a 100644 --- a/lib/content/getArtistRootPrefix.ts +++ b/lib/content/getArtistRootPrefix.ts @@ -1,3 +1,8 @@ +/** + * + * @param paths + * @param artistSlug + */ export function getArtistRootPrefix(paths: string[], artistSlug: string): string { const preferredPrefix = `artists/${artistSlug}/`; if (paths.some(path => path.startsWith(preferredPrefix))) { diff --git a/lib/content/getContentValidateHandler.ts b/lib/content/getContentValidateHandler.ts index e0c758b8..81cd0ce8 100644 --- a/lib/content/getContentValidateHandler.ts +++ b/lib/content/getContentValidateHandler.ts @@ -8,6 +8,8 @@ import { getArtistContentReadiness } from "@/lib/content/getArtistContentReadine * 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. + * + * @param request */ export async function getContentValidateHandler(request: NextRequest): Promise { const validated = await validateGetContentValidateQuery(request); diff --git a/lib/content/isCompletedRun.ts b/lib/content/isCompletedRun.ts index 855ea068..951d20b2 100644 --- a/lib/content/isCompletedRun.ts +++ b/lib/content/isCompletedRun.ts @@ -5,6 +5,10 @@ export type TriggerRunLike = { output?: unknown; }; +/** + * + * @param run + */ export function isCompletedRun(run: TriggerRunLike): boolean { return run.status === "COMPLETED"; } diff --git a/lib/content/persistCreateContentRunVideo.ts b/lib/content/persistCreateContentRunVideo.ts index 25a77eed..69bac792 100644 --- a/lib/content/persistCreateContentRunVideo.ts +++ b/lib/content/persistCreateContentRunVideo.ts @@ -27,6 +27,8 @@ type CreateContentOutput = { * and returns the run with normalized output. * * This keeps Supabase writes in API only. + * + * @param run */ export async function persistCreateContentRunVideo(run: T): Promise { if (run.taskIdentifier !== CREATE_CONTENT_TASK_ID || !isCompletedRun(run)) { diff --git a/lib/content/validateCreateContentBody.ts b/lib/content/validateCreateContentBody.ts index 47e3d8fa..2c780e75 100644 --- a/lib/content/validateCreateContentBody.ts +++ b/lib/content/validateCreateContentBody.ts @@ -40,6 +40,8 @@ export type ValidatedCreateContentBody = { /** * Validates auth and request body for POST /api/content/create. + * + * @param request */ export async function validateCreateContentBody( request: NextRequest, diff --git a/lib/content/validateGetContentEstimateQuery.ts b/lib/content/validateGetContentEstimateQuery.ts index 5828e7cc..97af7468 100644 --- a/lib/content/validateGetContentEstimateQuery.ts +++ b/lib/content/validateGetContentEstimateQuery.ts @@ -15,6 +15,8 @@ export type ValidatedGetContentEstimateQuery = z.infer { diff --git a/lib/evals/callChatFunctionsWithResult.ts b/lib/evals/callChatFunctionsWithResult.ts index a792248b..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[] = [ diff --git a/lib/evals/createToolsCalledScorer.ts b/lib/evals/createToolsCalledScorer.ts index 1d838ee3..8a9ac7e7 100644 --- a/lib/evals/createToolsCalledScorer.ts +++ b/lib/evals/createToolsCalledScorer.ts @@ -3,6 +3,9 @@ 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 }) => { diff --git a/lib/evals/extractTextFromResult.ts b/lib/evals/extractTextFromResult.ts index fac24cf6..dc67f3ab 100644 --- a/lib/evals/extractTextFromResult.ts +++ b/lib/evals/extractTextFromResult.ts @@ -3,6 +3,8 @@ import { extractTextResultFromSteps } from "./extractTextResultFromSteps"; /** * Extract text from a GenerateTextResult + * + * @param result */ export function extractTextFromResult(result: Awaited>): string { // Handle multi-step responses (when maxSteps > 1) diff --git a/lib/evals/extractTextResultFromSteps.ts b/lib/evals/extractTextResultFromSteps.ts index 44c0ae0d..16881677 100644 --- a/lib/evals/extractTextResultFromSteps.ts +++ b/lib/evals/extractTextResultFromSteps.ts @@ -4,6 +4,8 @@ import type { TextPart } from "ai"; /** * Extract text from multi-step GenerateTextResult * Handles responses where maxSteps > 1 + * + * @param result */ export function extractTextResultFromSteps( result: Awaited>, 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/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 f4829ea4..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, diff --git a/lib/evals/scorers/QuestionAnswered.ts b/lib/evals/scorers/QuestionAnswered.ts index abe0222c..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, diff --git a/lib/evals/scorers/ToolsCalled.ts b/lib/evals/scorers/ToolsCalled.ts index 2d901ec3..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, 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/getFlamingoPresetsHandler.ts b/lib/flamingo/getFlamingoPresetsHandler.ts index e35b5899..f33d491d 100644 --- a/lib/flamingo/getFlamingoPresetsHandler.ts +++ b/lib/flamingo/getFlamingoPresetsHandler.ts @@ -10,6 +10,7 @@ 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 { 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/getRepoGitModules.ts b/lib/github/getRepoGitModules.ts index caa0304e..8913a6ae 100644 --- a/lib/github/getRepoGitModules.ts +++ b/lib/github/getRepoGitModules.ts @@ -4,9 +4,12 @@ import { parseGitModules, type SubmoduleEntry } from "./parseGitModules"; * Fetches and parses .gitmodules from a GitHub repository. * Uses the GitHub Contents API (works for both public and private repos). * + * @param owner.owner * @param owner - The GitHub repository owner * @param repo - The GitHub repository name * @param branch - The branch to fetch from + * @param owner.repo + * @param owner.branch * @returns Array of submodule entries, or null if .gitmodules doesn't exist or fetch fails */ export async function getRepoGitModules({ diff --git a/lib/github/resolveSubmodulePath.ts b/lib/github/resolveSubmodulePath.ts index 7c3f60ed..029f1b1d 100644 --- a/lib/github/resolveSubmodulePath.ts +++ b/lib/github/resolveSubmodulePath.ts @@ -6,8 +6,10 @@ import { getRepoGitModules } from "./getRepoGitModules"; * If the path falls within a submodule, returns the submodule's repo URL * and the relative path within it. Otherwise returns the original values. * + * @param githubRepo.githubRepo * @param githubRepo - The parent GitHub repository URL * @param path - The file path to resolve + * @param githubRepo.path * @returns The resolved repo URL and path */ export async function resolveSubmodulePath({ diff --git a/lib/keys/createApiKeyHandler.ts b/lib/keys/createApiKeyHandler.ts index 452e9583..74aea0a1 100644 --- a/lib/keys/createApiKeyHandler.ts +++ b/lib/keys/createApiKeyHandler.ts @@ -2,7 +2,6 @@ import { NextRequest, NextResponse } from "next/server"; import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; import { validateCreateApiKeyBody } from "@/lib/keys/validateCreateApiKeyBody"; import { getAuthenticatedAccountId } from "@/lib/auth/getAuthenticatedAccountId"; -import { createOrgApiKeysHandler } from "@/lib/keys/org/createOrgApiKeysHandler"; import { createKey } from "@/lib/keys/createKey"; /** @@ -11,8 +10,6 @@ import { createKey } from "@/lib/keys/createKey"; * * Body parameters: * - key_name (required): The name for the API key - * - organizationId (optional): If provided, creates a key for the organization - * after validating the authenticated account is a member. * * @param request - The request object containing the body with key_name. * @returns A NextResponse with the generated API key. @@ -32,14 +29,8 @@ export async function createApiKeyHandler(request: NextRequest): Promise { try { - const { searchParams } = new URL(request.url); - const organizationId = searchParams.get("organizationId") ?? undefined; - const accountIdOrError = await getAuthenticatedAccountId(request); if (accountIdOrError instanceof NextResponse) { return accountIdOrError; } const accountId = accountIdOrError; - // If organizationId is provided, delegate to org-specific handler - if (organizationId) { - return getOrgApiKeysHandler(accountId, organizationId); - } - - // Default: fetch keys for the authenticated account const { data, error } = await getApiKeys({ accountId }); if (error) { diff --git a/lib/keys/org/createOrgApiKeysHandler.ts b/lib/keys/org/createOrgApiKeysHandler.ts deleted file mode 100644 index c5f178d2..00000000 --- a/lib/keys/org/createOrgApiKeysHandler.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NextResponse } from "next/server"; -import { createKey } from "@/lib/keys/createKey"; -import { onlyOrgAccounts } from "./onlyOrgAccounts"; - -/** - * Internal helper to create an API key scoped to an organization. - * Validates that the authenticated account is a member of the organization - * before creating the key. - * - * @param accountId - The authenticated account ID - * @param organizationId - The organization ID to create the key for - * @param keyName - The name for the API key - * @returns NextResponse with the generated key or an error response - */ -export async function createOrgApiKeysHandler( - accountId: string, - organizationId: string, - keyName: string, -): Promise { - // Verify membership before creating org key - const membershipError = await onlyOrgAccounts(accountId, organizationId); - if (membershipError) { - return membershipError; - } - - // Create key owned by the organization - return createKey(organizationId, keyName); -} diff --git a/lib/keys/org/getOrgApiKeysHandler.ts b/lib/keys/org/getOrgApiKeysHandler.ts deleted file mode 100644 index 2d505452..00000000 --- a/lib/keys/org/getOrgApiKeysHandler.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { getApiKeys } from "@/lib/supabase/account_api_keys/getApiKeys"; -import { onlyOrgAccounts } from "./onlyOrgAccounts"; - -/** - * Internal helper to fetch API keys scoped to an organization. - * Validates that the authenticated account is a member of the organization - * before returning keys. - * - * @param accountId - The authenticated account ID - * @param organizationId - The organization ID whose keys are requested - * @returns NextResponse with keys or an error response - */ -export async function getOrgApiKeysHandler( - accountId: string, - organizationId: string, -): Promise { - // Verify membership before returning org keys - const membershipError = await onlyOrgAccounts(accountId, organizationId); - if (membershipError) { - return membershipError; - } - - const { data, error } = await getApiKeys({ accountId: organizationId }); - - if (error) { - console.error("Error fetching organization API keys:", error); - return NextResponse.json( - { - status: "error", - message: "Failed to fetch organization API keys", - }, - { - status: 500, - headers: getCorsHeaders(), - }, - ); - } - - return NextResponse.json( - { - keys: data || [], - }, - { - status: 200, - headers: getCorsHeaders(), - }, - ); -} diff --git a/lib/keys/org/onlyOrgAccounts.ts b/lib/keys/org/onlyOrgAccounts.ts deleted file mode 100644 index a8c1cf26..00000000 --- a/lib/keys/org/onlyOrgAccounts.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { NextResponse } from "next/server"; -import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; -import { getAccountOrganizations } from "@/lib/supabase/account_organization_ids/getAccountOrganizations"; - -/** - * Validates that an account is a member of the specified organization. - * Returns an error response if validation fails, or null if validation passes. - * - * @param accountId - The account ID to validate - * @param organizationId - The organization ID to check membership for - * @returns NextResponse with 403 error if not a member, or null if member - */ -export async function onlyOrgAccounts( - accountId: string, - organizationId: string, -): Promise { - const orgMemberships = await getAccountOrganizations({ - accountId, - organizationId, - }); - - if (!orgMemberships.length) { - return NextResponse.json( - { - status: "error", - message: "Account is not a member of this organization", - }, - { - status: 403, - headers: getCorsHeaders(), - }, - ); - } - - return null; -} diff --git a/lib/keys/validateCreateApiKeyBody.ts b/lib/keys/validateCreateApiKeyBody.ts index 8f1136f8..51ad8cba 100644 --- a/lib/keys/validateCreateApiKeyBody.ts +++ b/lib/keys/validateCreateApiKeyBody.ts @@ -4,7 +4,6 @@ import { z } from "zod"; export const createApiKeyBodySchema = z.object({ key_name: z.string().min(1, "key_name parameter is required"), - organizationId: z.string().optional(), }); export type CreateApiKeyBody = z.infer; diff --git a/lib/mcp/__tests__/verifyApiKey.test.ts b/lib/mcp/__tests__/verifyApiKey.test.ts index 56936840..eafa6744 100644 --- a/lib/mcp/__tests__/verifyApiKey.test.ts +++ b/lib/mcp/__tests__/verifyApiKey.test.ts @@ -37,7 +37,7 @@ describe("verifyBearerToken", () => { }); }); - it("returns auth info for org API key", async () => { + it("returns auth info for API key with org membership", async () => { vi.mocked(getAccountIdByAuthToken).mockRejectedValue(new Error("Invalid JWT")); vi.mocked(getApiKeyDetails).mockResolvedValue({ accountId: "org-account-123", diff --git a/lib/mcp/resolveAccountId.ts b/lib/mcp/resolveAccountId.ts index 03d1d0d8..bae735f4 100644 --- a/lib/mcp/resolveAccountId.ts +++ b/lib/mcp/resolveAccountId.ts @@ -13,9 +13,11 @@ export interface ResolveAccountIdResult { /** * Resolves the accountId from MCP auth info or an override parameter. - * Validates access when an org API key attempts to use an account_id override. + * Validates access when an account_id override is provided. * * @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({ @@ -25,7 +27,7 @@ export async function resolveAccountId({ const authAccountId = authInfo?.extra?.accountId; if (authAccountId) { - // If account_id override is provided, validate access (for org API keys) + // If account_id override is provided, validate access via org membership if (accountIdOverride && accountIdOverride !== authAccountId) { const hasAccess = await canAccessAccount({ currentAccountId: authAccountId, diff --git a/lib/mcp/tools/artists/registerCreateNewArtistTool.ts b/lib/mcp/tools/artists/registerCreateNewArtistTool.ts index 7495bcbf..344d43ca 100644 --- a/lib/mcp/tools/artists/registerCreateNewArtistTool.ts +++ b/lib/mcp/tools/artists/registerCreateNewArtistTool.ts @@ -15,7 +15,7 @@ const createNewArtistSchema = z.object({ .string() .optional() .describe( - "The account ID to create the artist for. Only required for organization API keys creating artists on behalf of other accounts. " + + "The account ID to create the artist for. Only required when creating artists on behalf of other accounts within a shared organization. " + "If not provided, the account ID will be resolved from the authenticated API key.", ), active_conversation_id: z @@ -60,7 +60,7 @@ export function registerCreateNewArtistTool(server: McpServer): void { description: "Create a new artist account in the system. " + "Requires authentication via API key (Authorization: Bearer header). " + - "The account_id parameter is optional — only provide it when using an organization API key to create artists on behalf of other accounts. " + + "The account_id parameter is optional — only provide it when creating artists on behalf of other accounts within a shared organization. " + "The active_conversation_id parameter is optional — when omitted, use the active_conversation_id from the system prompt " + "to copy the conversation. Never ask the user to provide a room ID. " + "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.", diff --git a/lib/mcp/tools/chats/__tests__/registerGetChatsTool.test.ts b/lib/mcp/tools/chats/__tests__/registerGetChatsTool.test.ts index 4cceb5de..b41af8e2 100644 --- a/lib/mcp/tools/chats/__tests__/registerGetChatsTool.test.ts +++ b/lib/mcp/tools/chats/__tests__/registerGetChatsTool.test.ts @@ -206,7 +206,7 @@ describe("registerGetChatsTool", () => { }); }); - it("returns error when personal key tries to filter by account_id", async () => { + it("returns error when account lacks access to filter by account_id", async () => { mockCanAccessAccount.mockResolvedValue(false); const result = await registeredHandler( diff --git a/lib/mcp/tools/chats/registerGetChatsTool.ts b/lib/mcp/tools/chats/registerGetChatsTool.ts index 7f999b4e..8b2542f3 100644 --- a/lib/mcp/tools/chats/registerGetChatsTool.ts +++ b/lib/mcp/tools/chats/registerGetChatsTool.ts @@ -19,8 +19,7 @@ export type GetChatsArgs = z.infer; * Registers the "get_chats" tool on the MCP server. * Retrieves chat conversations (rooms) for accounts. * - * For personal keys: Returns chats for the key owner's account. - * For org keys: Returns chats for all accounts in the organization. + * Returns chats for the key owner's account. * For Recoup admin key: Returns ALL chat records. * * @param server - The MCP server instance to register the tool on. diff --git a/lib/mcp/tools/pulse/registerGetPulsesTool.ts b/lib/mcp/tools/pulse/registerGetPulsesTool.ts index 935f4a05..f7809805 100644 --- a/lib/mcp/tools/pulse/registerGetPulsesTool.ts +++ b/lib/mcp/tools/pulse/registerGetPulsesTool.ts @@ -19,8 +19,7 @@ export type GetPulsesArgs = z.infer; * Registers the "get_pulses" tool on the MCP server. * Retrieves pulse statuses for accounts. * - * For personal keys: Returns pulses for the key owner's account. - * For org keys: Returns pulses for all accounts in the organization. + * Returns pulses for the key owner's account. * For Recoup admin key: Returns ALL pulse records. * * @param server - The MCP server instance to register the tool on. diff --git a/lib/mcp/tools/sandbox/registerPromptSandboxTool.ts b/lib/mcp/tools/sandbox/registerPromptSandboxTool.ts index cd0ec5fb..6dd4198f 100644 --- a/lib/mcp/tools/sandbox/registerPromptSandboxTool.ts +++ b/lib/mcp/tools/sandbox/registerPromptSandboxTool.ts @@ -18,7 +18,7 @@ const promptSandboxSchema = z.object({ .string() .optional() .describe( - "The account ID to run the sandbox command for. Only applicable for organization API keys — org keys can target any account within their organization. Do not use with personal API keys.", + "The account ID to run the sandbox command for. Only applicable when the authenticated account has shared organization membership with the target account.", ), }); diff --git a/lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts b/lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts index 4942fdfb..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", diff --git a/lib/notifications/__tests__/createNotificationHandler.test.ts b/lib/notifications/__tests__/createNotificationHandler.test.ts index ca7fb677..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", diff --git a/lib/notifications/__tests__/validateCreateNotificationBody.test.ts b/lib/notifications/__tests__/validateCreateNotificationBody.test.ts index 10390b15..a1580b0f 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", { @@ -88,7 +93,7 @@ describe("validateCreateNotificationBody", () => { } }); - it("uses account_id override for org API keys", async () => { + it("uses account_id override when account has org access", async () => { mockValidateAuthContext.mockResolvedValue({ accountId: "550e8400-e29b-41d4-a716-446655440000", orgId: "org-id", @@ -187,7 +192,7 @@ describe("validateCreateNotificationBody", () => { } }); - it("returns 403 when org API key lacks access to account_id", async () => { + it("returns 403 when account lacks access to account_id", async () => { mockValidateAuthContext.mockResolvedValue( NextResponse.json( { status: "error", error: "Access denied to specified account_id" }, diff --git a/lib/notifications/createNotificationHandler.ts b/lib/notifications/createNotificationHandler.ts index 35914a33..7d9c8556 100644 --- a/lib/notifications/createNotificationHandler.ts +++ b/lib/notifications/createNotificationHandler.ts @@ -8,7 +8,7 @@ import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmai * Handler for POST /api/notifications. * Sends a notification email to the authenticated account's email address. * The recipient is automatically resolved from the API key or Bearer token. - * Supports optional account_id override for org API keys. + * Supports optional account_id override (requires shared org membership or admin access). * Requires authentication via x-api-key header or Authorization bearer token. * * @param request - The request object. diff --git a/lib/organizations/__tests__/buildGetOrganizationsParams.test.ts b/lib/organizations/__tests__/buildGetOrganizationsParams.test.ts index 814d886b..fc41a49c 100644 --- a/lib/organizations/__tests__/buildGetOrganizationsParams.test.ts +++ b/lib/organizations/__tests__/buildGetOrganizationsParams.test.ts @@ -12,7 +12,7 @@ describe("buildGetOrganizationsParams", () => { vi.clearAllMocks(); }); - it("returns accountId for personal key", async () => { + it("returns accountId for authenticated account", async () => { const result = await buildGetOrganizationsParams({ accountId: "personal-account-123", }); @@ -41,7 +41,7 @@ describe("buildGetOrganizationsParams", () => { }); }); - it("allows personal key to access targetAccountId via shared org", async () => { + it("allows account to access targetAccountId via shared org", async () => { vi.mocked(canAccessAccount).mockResolvedValue(true); const result = await buildGetOrganizationsParams({ @@ -59,7 +59,7 @@ describe("buildGetOrganizationsParams", () => { }); }); - it("returns error when personal key tries to filter by targetAccountId", async () => { + it("returns error when account lacks access to targetAccountId", async () => { vi.mocked(canAccessAccount).mockResolvedValue(false); const result = await buildGetOrganizationsParams({ diff --git a/lib/organizations/getOrganizationsHandler.ts b/lib/organizations/getOrganizationsHandler.ts index f0061c3a..5cca0405 100644 --- a/lib/organizations/getOrganizationsHandler.ts +++ b/lib/organizations/getOrganizationsHandler.ts @@ -8,12 +8,11 @@ import { formatAccountOrganizations } from "@/lib/organizations/formatAccountOrg * Handler for retrieving organizations for an account. * * Authenticates via x-api-key or Authorization bearer token. - * For personal keys: returns the key owner's organizations. - * For org keys: returns organizations for all accounts in the org. + * Returns the key owner's organizations. * For Recoup admin: returns all organizations. * * Optional query parameter: - * - account_id: Filter to a specific account (org keys only) + * - account_id: Filter to a specific account (requires shared org membership or admin access) * * @param request - The request object * @returns A NextResponse with organizations data diff --git a/lib/organizations/validateGetOrganizationsRequest.ts b/lib/organizations/validateGetOrganizationsRequest.ts index 65dc7c2f..30c70d07 100644 --- a/lib/organizations/validateGetOrganizationsRequest.ts +++ b/lib/organizations/validateGetOrganizationsRequest.ts @@ -14,9 +14,8 @@ const getOrganizationsQuerySchema = z.object({ * Validates GET /api/organizations request. * Handles authentication via x-api-key or Authorization bearer token. * - * For personal keys: Returns accountId with the key owner's account - * For org keys: Returns organizationId for filtering by org membership - * For Recoup admin key: Returns empty params to indicate ALL organization records + * Returns accountId with the key owner's account for filtering. + * For Recoup admin key: Returns empty params to indicate ALL organization records. * * Query parameters: * - account_id: Filter to a specific account (validated against org membership) 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 bac20420..0312c024 100644 --- a/lib/pulse/__tests__/buildGetPulsesParams.test.ts +++ b/lib/pulse/__tests__/buildGetPulsesParams.test.ts @@ -12,7 +12,7 @@ describe("buildGetPulsesParams", () => { vi.clearAllMocks(); }); - it("returns accountIds for personal key", async () => { + it("returns accountIds for authenticated account", async () => { const result = await buildGetPulsesParams({ accountId: "personal-account-123", }); @@ -53,7 +53,7 @@ describe("buildGetPulsesParams", () => { }); }); - it("allows personal key to access targetAccountId via shared org", async () => { + it("allows account to access targetAccountId via shared org", async () => { vi.mocked(canAccessAccount).mockResolvedValue(true); const result = await buildGetPulsesParams({ @@ -71,7 +71,7 @@ describe("buildGetPulsesParams", () => { }); }); - it("returns error when personal key tries to filter by targetAccountId", async () => { + it("returns error when account lacks access to targetAccountId", async () => { vi.mocked(canAccessAccount).mockResolvedValue(false); const result = await buildGetPulsesParams({ diff --git a/lib/pulse/__tests__/getPulsesHandler.test.ts b/lib/pulse/__tests__/getPulsesHandler.test.ts index 028d1af7..05601d94 100644 --- a/lib/pulse/__tests__/getPulsesHandler.test.ts +++ b/lib/pulse/__tests__/getPulsesHandler.test.ts @@ -23,7 +23,7 @@ describe("getPulsesHandler", () => { vi.clearAllMocks(); }); - it("should return array of pulses for personal key", async () => { + it("should return array of pulses for authenticated account", async () => { const mockAccountId = "account-123"; vi.mocked(validateGetPulsesRequest).mockResolvedValue({ accountIds: [mockAccountId], @@ -42,7 +42,7 @@ describe("getPulsesHandler", () => { expect(selectPulseAccounts).toHaveBeenCalledWith({ accountIds: [mockAccountId] }); }); - it("should return pulses for org key using orgId filter", async () => { + it("should return pulses using orgId filter", async () => { const mockOrgId = "org-123"; vi.mocked(validateGetPulsesRequest).mockResolvedValue({ orgId: mockOrgId, diff --git a/lib/pulse/__tests__/validateGetPulsesRequest.test.ts b/lib/pulse/__tests__/validateGetPulsesRequest.test.ts index 788fa1f6..4cc74639 100644 --- a/lib/pulse/__tests__/validateGetPulsesRequest.test.ts +++ b/lib/pulse/__tests__/validateGetPulsesRequest.test.ts @@ -40,11 +40,11 @@ describe("validateGetPulsesRequest", () => { expect(response.status).toBe(401); }); - it("should return single account ID for personal key", async () => { + it("should return single account ID for authenticated account", async () => { const mockAccountId = "personal-account-123"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: mockAccountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); @@ -60,7 +60,7 @@ describe("validateGetPulsesRequest", () => { }); }); - it("should return accountIds for org key", async () => { + it("should return accountIds when account has org membership", async () => { const mockOrgId = "org-123"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: mockOrgId, @@ -135,12 +135,12 @@ describe("validateGetPulsesRequest", () => { expect(response.status).toBe(400); }); - it("should reject personal key trying to filter by account_id", async () => { + it("should reject filtering by account_id without org access", async () => { const mockAccountId = "a1111111-1111-4111-8111-111111111111"; const otherAccountId = "b2222222-2222-4222-8222-222222222222"; vi.mocked(validateAuthContext).mockResolvedValue({ accountId: mockAccountId, - orgId: null, // Personal key + orgId: null, authToken: "test-token", }); @@ -154,7 +154,7 @@ describe("validateGetPulsesRequest", () => { expect(response.status).toBe(403); }); - it("should allow org key to filter by account_id within org", async () => { + it("should allow filtering by account_id within org", async () => { const mockOrgId = "c3333333-3333-4333-8333-333333333333"; const targetAccountId = "d4444444-4444-4444-8444-444444444444"; vi.mocked(validateAuthContext).mockResolvedValue({ @@ -178,7 +178,7 @@ describe("validateGetPulsesRequest", () => { expect(validResult.accountIds).toEqual([targetAccountId]); }); - it("should reject org key filtering by account_id not in org", async () => { + it("should reject filtering by account_id not in org", async () => { const mockOrgId = "f6666666-6666-4666-8666-666666666666"; const notInOrgId = "b8888888-8888-4888-8888-888888888888"; vi.mocked(validateAuthContext).mockResolvedValue({ diff --git a/lib/pulse/getPulsesHandler.ts b/lib/pulse/getPulsesHandler.ts index dea4a3d5..2447b0d8 100644 --- a/lib/pulse/getPulsesHandler.ts +++ b/lib/pulse/getPulsesHandler.ts @@ -7,8 +7,7 @@ import { selectPulseAccounts } from "@/lib/supabase/pulse_accounts/selectPulseAc * Handler for retrieving pulse statuses for accounts. * Requires authentication via x-api-key header or Authorization bearer token. * - * For personal keys: Returns array with pulse for the account (if exists). - * For org keys: Returns array of pulses for accounts in the organization that have records. + * Returns array with pulse for the authenticated account (if exists). * For Recoup admin key: Returns array of ALL pulse records. * * Optional query parameters: diff --git a/lib/pulse/validateGetPulsesRequest.ts b/lib/pulse/validateGetPulsesRequest.ts index 40e75297..aa9db0b8 100644 --- a/lib/pulse/validateGetPulsesRequest.ts +++ b/lib/pulse/validateGetPulsesRequest.ts @@ -18,9 +18,8 @@ const getPulsesQuerySchema = z.object({ * Validates GET /api/pulses request. * Handles authentication via x-api-key or Authorization bearer token. * - * For personal keys: Returns accountIds with the key owner's account - * For org keys: Returns orgId for filtering by org membership in database - * For Recoup admin key: Returns empty params to indicate ALL pulse records + * Returns accountIds with the key owner's account for filtering. + * For Recoup admin key: Returns empty params to indicate ALL pulse records. * * Query parameters: * - account_id: Filter to a specific account (validated against org membership) diff --git a/lib/sandbox/__tests__/buildGetSandboxesParams.test.ts b/lib/sandbox/__tests__/buildGetSandboxesParams.test.ts index f42f488d..09a273b1 100644 --- a/lib/sandbox/__tests__/buildGetSandboxesParams.test.ts +++ b/lib/sandbox/__tests__/buildGetSandboxesParams.test.ts @@ -23,7 +23,7 @@ describe("buildGetSandboxesParams", () => { }); }); - it("returns error when personal key tries to filter by account_id", async () => { + it("returns error when account lacks access to filter by account_id", async () => { vi.mocked(canAccessAccount).mockResolvedValue(false); const result = await buildGetSandboxesParams({ diff --git a/lib/sandbox/__tests__/getSandboxesHandler.test.ts b/lib/sandbox/__tests__/getSandboxesHandler.test.ts index 6c6121f1..eb67f5ce 100644 --- a/lib/sandbox/__tests__/getSandboxesHandler.test.ts +++ b/lib/sandbox/__tests__/getSandboxesHandler.test.ts @@ -324,7 +324,7 @@ describe("getSandboxesHandler", () => { expect(json.github_repo).toBeNull(); }); - it("returns snapshot info for org keys using orgId as accountId", async () => { + it("returns snapshot info using orgId as accountId", async () => { vi.mocked(validateGetSandboxesRequest).mockResolvedValue({ orgId: "org_123", }); diff --git a/lib/sandbox/__tests__/validateGetSandboxesRequest.test.ts b/lib/sandbox/__tests__/validateGetSandboxesRequest.test.ts index 413325b4..e54ffef6 100644 --- a/lib/sandbox/__tests__/validateGetSandboxesRequest.test.ts +++ b/lib/sandbox/__tests__/validateGetSandboxesRequest.test.ts @@ -95,7 +95,7 @@ describe("validateGetSandboxesRequest", () => { }); describe("authorization via buildGetSandboxesParams", () => { - it("returns 403 when buildGetSandboxesParams returns personal key error", async () => { + it("returns 403 when buildGetSandboxesParams returns access error", async () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId: "acc_123", orgId: null, @@ -179,7 +179,7 @@ describe("validateGetSandboxesRequest", () => { }); describe("default behavior", () => { - it("returns params with accountIds for personal key", async () => { + it("returns params with accountIds for authenticated account", async () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId: "acc_123", orgId: null, @@ -205,7 +205,7 @@ describe("validateGetSandboxesRequest", () => { }); }); - it("returns params with org member accountIds for org key", async () => { + it("returns params with org member accountIds", async () => { vi.mocked(validateAuthContext).mockResolvedValue({ accountId: "org_456", orgId: "org_456", diff --git a/lib/sandbox/__tests__/validateSetupSandboxBody.test.ts b/lib/sandbox/__tests__/validateSetupSandboxBody.test.ts index 5a6ab035..919595c8 100644 --- a/lib/sandbox/__tests__/validateSetupSandboxBody.test.ts +++ b/lib/sandbox/__tests__/validateSetupSandboxBody.test.ts @@ -73,7 +73,7 @@ describe("validateSetupSandboxBody", () => { expect(result).toEqual({ accountId: "resolved-account-id" }); }); - it("passes account_id override to validateAuthContext for org keys", async () => { + it("passes account_id override to validateAuthContext", async () => { const targetAccountId = "550e8400-e29b-41d4-a716-446655440000"; vi.mocked(safeParseJson).mockResolvedValue({ account_id: targetAccountId, diff --git a/lib/sandbox/getSandboxesHandler.ts b/lib/sandbox/getSandboxesHandler.ts index 618e6e3b..38829b7d 100644 --- a/lib/sandbox/getSandboxesHandler.ts +++ b/lib/sandbox/getSandboxesHandler.ts @@ -11,8 +11,7 @@ import type { SandboxCreatedResponse } from "./createSandbox"; * Handler for retrieving sandbox statuses for an account. * Requires authentication via x-api-key header or Authorization bearer token. * - * For personal keys: Returns sandboxes for the key owner's account. - * For org keys: Returns sandboxes for all accounts in the organization. + * Returns sandboxes for the key owner's account. * * Optional query parameters: * - sandbox_id: Filter to a specific sandbox (must belong to account/org) diff --git a/lib/sandbox/validateDeleteSandboxBody.ts b/lib/sandbox/validateDeleteSandboxBody.ts index 1823d1d4..d86a6930 100644 --- a/lib/sandbox/validateDeleteSandboxBody.ts +++ b/lib/sandbox/validateDeleteSandboxBody.ts @@ -16,7 +16,7 @@ export type DeleteSandboxBody = { /** * Validates auth and request body for DELETE /api/sandboxes. * Handles authentication via x-api-key or Authorization bearer token, - * body validation, and optional account_id override for organization API keys. + * body validation, and optional account_id override (requires shared org membership or admin access). * * @param request - The NextRequest object * @returns A NextResponse with an error if validation fails, or the validated body with auth context. diff --git a/lib/sandbox/validateSandboxBody.ts b/lib/sandbox/validateSandboxBody.ts index ccde70e5..7384471c 100644 --- a/lib/sandbox/validateSandboxBody.ts +++ b/lib/sandbox/validateSandboxBody.ts @@ -14,7 +14,7 @@ export type SandboxBody = z.infer & AuthContext; /** * Validates auth and request body for POST /api/sandboxes. - * Supports optional account_id in the body for org API keys to target a specific account. + * Supports optional account_id in the body to target a specific account (requires shared org membership or admin access). * * @param request - The NextRequest object * @returns A NextResponse with an error if validation fails, or the validated body with auth context. diff --git a/lib/sandbox/validateSetupSandboxBody.ts b/lib/sandbox/validateSetupSandboxBody.ts index 15ee78ab..4c45a74a 100644 --- a/lib/sandbox/validateSetupSandboxBody.ts +++ b/lib/sandbox/validateSetupSandboxBody.ts @@ -16,7 +16,7 @@ export type SetupSandboxBody = { /** * Validates auth and request body for POST /api/sandboxes/setup. * Handles authentication via x-api-key or Authorization bearer token, - * body validation, and optional account_id override for organization API keys. + * body validation, and optional account_id override (requires shared org membership or admin access). * * @param request - The NextRequest object * @returns A NextResponse with an error if validation fails, or the validated body with auth context. diff --git a/lib/sandbox/validateSnapshotPatchBody.ts b/lib/sandbox/validateSnapshotPatchBody.ts index c31c75aa..eb378fe0 100644 --- a/lib/sandbox/validateSnapshotPatchBody.ts +++ b/lib/sandbox/validateSnapshotPatchBody.ts @@ -23,7 +23,7 @@ export type SnapshotPatchBody = { /** * Validates auth and request body for PATCH /api/sandboxes/snapshot. * Handles authentication via x-api-key or Authorization bearer token, - * body validation, and optional account_id override for organization API keys. + * body validation, and optional account_id override (requires shared org membership or admin access). * * @param request - The NextRequest object * @returns A NextResponse with an error if validation fails, or the validated body with auth context. diff --git a/lib/spotify/getSpotifyFollowers.ts b/lib/spotify/getSpotifyFollowers.ts index 235de41e..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 */ diff --git a/lib/supabase/account_artist_ids/getAccountArtistIds.ts b/lib/supabase/account_artist_ids/getAccountArtistIds.ts index e4e6b809..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: { diff --git a/lib/supabase/account_workspace_ids/getAccountWorkspaceIds.ts b/lib/supabase/account_workspace_ids/getAccountWorkspaceIds.ts index ae121fdd..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 { diff --git a/lib/supabase/files/createFileRecord.ts b/lib/supabase/files/createFileRecord.ts index 6f836f3c..3182de11 100644 --- a/lib/supabase/files/createFileRecord.ts +++ b/lib/supabase/files/createFileRecord.ts @@ -25,6 +25,8 @@ export interface CreateFileRecordParams { /** * Create a file record in the database + * + * @param params */ export async function createFileRecord(params: CreateFileRecordParams): Promise { const { diff --git a/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts b/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts index 3bb90258..59f23f26 100644 --- a/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts +++ b/lib/supabase/pulse_accounts/__tests__/selectPulseAccounts.test.ts @@ -46,7 +46,7 @@ describe("selectPulseAccounts", () => { expect(result).toEqual(pulses); }); - it("filters by accountIds with UUID (personal key scenario)", async () => { + it("filters by accountIds with UUID", async () => { const accountId = "fb678396-a68f-4294-ae50-b8cacf9ce77b"; const pulses = [{ id: "pulse-uuid-123", account_id: accountId, active: true }]; mockIn.mockResolvedValue({ data: pulses, error: null }); diff --git a/lib/supabase/song_artists/insertSongArtists.ts b/lib/supabase/song_artists/insertSongArtists.ts index b81879e3..69878d6d 100644 --- a/lib/supabase/song_artists/insertSongArtists.ts +++ b/lib/supabase/song_artists/insertSongArtists.ts @@ -5,6 +5,8 @@ export type SongArtistInsert = TablesInsert<"song_artists">; /** * Inserts song-artist relationships, skipping duplicates. + * + * @param songArtists */ export async function insertSongArtists(songArtists: SongArtistInsert[]): Promise { const records = songArtists.filter( diff --git a/lib/supabase/storage/uploadFileByKey.ts b/lib/supabase/storage/uploadFileByKey.ts index ba146fa3..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, diff --git a/lib/tasks/__tests__/getTaskRunHandler.test.ts b/lib/tasks/__tests__/getTaskRunHandler.test.ts index 270b3d15..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", diff --git a/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts b/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts index f7126175..77d410da 100644 --- a/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts +++ b/lib/tasks/__tests__/validateGetTaskRunQuery.test.ts @@ -24,6 +24,8 @@ vi.mock("@/lib/admins/checkIsAdmin", () => ({ /** * 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 b4bdf169..beb4242e 100644 --- a/lib/tasks/validateGetTaskRunQuery.ts +++ b/lib/tasks/validateGetTaskRunQuery.ts @@ -33,8 +33,8 @@ export type GetTaskRunQuery = ValidatedRetrieveQuery | ValidatedListQuery; * * When account_id is provided: * - Admin accounts (Bearer token) may query any account - * - Org API keys may query accounts within their organization - * - Personal API keys may only query their own account + * - Accounts with org membership may query accounts within their organization + * - Other accounts may only query their own account * * @param request - The NextRequest object * @returns A NextResponse with an error if validation fails, or the validated query if validation passes. diff --git a/lib/transcribe/processAudioTranscription.ts b/lib/transcribe/processAudioTranscription.ts index 351eee34..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,6 +66,10 @@ 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"; diff --git a/lib/transcribe/saveAudioToFiles.ts b/lib/transcribe/saveAudioToFiles.ts index 12bda1ef..2124e512 100644 --- a/lib/transcribe/saveAudioToFiles.ts +++ b/lib/transcribe/saveAudioToFiles.ts @@ -2,6 +2,10 @@ 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, 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/types.ts b/lib/transcribe/types.ts index 91c0ac10..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"; diff --git a/lib/trigger/triggerCreateContent.ts b/lib/trigger/triggerCreateContent.ts index 494f7063..e8732539 100644 --- a/lib/trigger/triggerCreateContent.ts +++ b/lib/trigger/triggerCreateContent.ts @@ -16,6 +16,8 @@ export interface TriggerCreateContentPayload { /** * Triggers the create-content task in Trigger.dev. + * + * @param payload */ export async function triggerCreateContent(payload: TriggerCreateContentPayload) { const handle = await tasks.trigger(CREATE_CONTENT_TASK_ID, payload); diff --git a/lib/workspaces/createWorkspacePostHandler.ts b/lib/workspaces/createWorkspacePostHandler.ts index 0a7c7ac6..73dbbe42 100644 --- a/lib/workspaces/createWorkspacePostHandler.ts +++ b/lib/workspaces/createWorkspacePostHandler.ts @@ -8,12 +8,12 @@ import { createWorkspaceInDb } from "@/lib/workspaces/createWorkspaceInDb"; * * Creates a new workspace account. Requires authentication via x-api-key header. * The account ID is inferred from the API key, unless an account_id is provided - * in the request body by an organization API key with access to that account. + * in the request body by an account with shared org membership. * * Request body: * - name (optional): The name of the workspace to create. Defaults to "Untitled". * - account_id (optional): The ID of the account to create the workspace for (UUID). - * Only used by organization API keys creating workspaces on behalf of other accounts. + * Only used when creating workspaces on behalf of other accounts within a shared organization. * - organization_id (optional): The organization ID to link the new workspace to (UUID). * If provided, the workspace will appear in that organization's view. *