diff --git a/.gitignore b/.gitignore index 35f575af52..65a97aa75d 100644 --- a/.gitignore +++ b/.gitignore @@ -59,8 +59,9 @@ apps/web/scripts-dist # File uploads in development apps/web/uploads +apps/web/storage apps/web/public/uploads -apps/web/public/storage +apps/web/public/files uploads storage diff --git a/AGENTS.md b/AGENTS.md index 854ce2c879..45fc2a3930 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,7 +60,7 @@ ### Action Layer (`apps/web/src/actions/`) -- Server actions use `authProcedure.createServerAction()` pattern +- Server actions use `authProcedure` pattern - Input validation with Zod schemas - Actions fetch model instances using repositories before calling services - **Admin-only actions**: Place under `actions/admin/` directory for backoffice functionality @@ -68,15 +68,33 @@ ```typescript export const updateApiKeyAction = authProcedure - .createServerAction() - .input(z.object({ id: z.number(), name: z.string() })) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.number(), name: z.string() })) + .action(async ({ parsedInput, ctx }) => { const repo = new Repository(ctx.workspace.id) - const model = await repo.find(input.id).then((r) => r.unwrap()) - return updateService(model, { name: input.name }).then((r) => r.unwrap()) + const model = await repo.find(parsedInput.id).then((r) => r.unwrap()) + return updateService(model, { name: parsedInput.name }).then((r) => + r.unwrap(), + ) }) ``` +- For writing an action with a different scope. Let's say withing projects: + + ```typescript + import { withProject, withProjectSchema } from '../../procedures' + export const updateProjectAction = withProject + .inputSchema(withProjectSchema.extend({ id: z.number(), name: z.string() })) + .action(async ({ parsedInput, ctx }) => { + const repo = new ProjectRepository(ctx.workspace.id) + const model = await repo.find(parsedInput.id).then((r) => r.unwrap()) + return updateProjectService(model, { name: parsedInput.name }).then((r) => + r.unwrap(), + ) + }) + ``` + + `withProject` procedure inherits from `authProcedure` and adds project validation. + ### Store Layer (`apps/web/src/stores/`) - Use SWR for data fetching with custom hooks diff --git a/apps/gateway/package.json b/apps/gateway/package.json index 49c89a43cf..9ce5422365 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -16,7 +16,7 @@ "dependencies": { "@hono/node-server": "1.13.2", "@hono/swagger-ui": "0.4.1", - "@hono/zod-openapi": "0.16.4", + "@hono/zod-openapi": "1.1.1", "@latitude-data/constants": "workspace:^", "@latitude-data/core": "workspace:^", "@latitude-data/env": "workspace:^", diff --git a/apps/gateway/src/openApi/schemas/ai.ts b/apps/gateway/src/openApi/schemas/ai.ts index d0fa3156f8..cc7dc401e7 100644 --- a/apps/gateway/src/openApi/schemas/ai.ts +++ b/apps/gateway/src/openApi/schemas/ai.ts @@ -15,11 +15,11 @@ export const languageModelUsageSchema = z.object({ export const toolCallSchema = z.object({ id: z.string(), name: z.string(), - arguments: z.record(z.any()), + arguments: z.record(z.string(), z.any()), }) -export const configSchema = z.object({}).passthrough() -export const providerLogSchema = z.object({}).passthrough() +export const configSchema = z.record(z.string(), z.any()) +export const providerLogSchema = z.record(z.string(), z.any()) export const chainStepResponseSchema = z.discriminatedUnion('streamType', [ z.object({ streamType: z.literal('text'), @@ -58,7 +58,7 @@ export const chainEventDtoResponseSchema = z.discriminatedUnion('streamType', [ export const legacyChainEventDtoSchema = z.discriminatedUnion('event', [ z.object({ event: z.literal(StreamEventTypes.Provider), - data: z.object({}).passthrough(), + data: z.record(z.string(), z.any()), }), z.object({ event: z.literal(StreamEventTypes.Latitude), @@ -79,7 +79,7 @@ export const legacyChainEventDtoSchema = z.discriminatedUnion('event', [ type: z.literal(LegacyChainEventTypes.Complete), config: configSchema, messages: z.array(messageSchema).optional(), - object: z.object({}).passthrough().optional(), + object: z.record(z.string(), z.any()).optional(), response: chainEventDtoResponseSchema, uuid: z.string().optional(), }), @@ -110,8 +110,8 @@ export const ProjectSchema = z.object({ id: z.number(), name: z.string(), workspaceId: z.number(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - lastEditedAt: z.string().datetime().optional(), - deletedAt: z.string().datetime().nullable().optional(), + createdAt: z.iso.datetime(), + updatedAt: z.iso.datetime(), + lastEditedAt: z.iso.datetime().optional(), + deletedAt: z.iso.datetime().nullable().optional(), }) diff --git a/apps/gateway/src/openApi/schemas/utils.ts b/apps/gateway/src/openApi/schemas/utils.ts index ca6c26245b..a0d09cafb9 100644 --- a/apps/gateway/src/openApi/schemas/utils.ts +++ b/apps/gateway/src/openApi/schemas/utils.ts @@ -4,7 +4,7 @@ import { LogSources } from '@latitude-data/core/browser' export const internalInfoSchema = z.object({ __internal: z .object({ - source: z.nativeEnum(LogSources).optional(), + source: z.enum(LogSources).optional(), }) .optional(), }) diff --git a/apps/gateway/src/presenters/documentPresenter.ts b/apps/gateway/src/presenters/documentPresenter.ts index cc97f26f67..f00feb8917 100644 --- a/apps/gateway/src/presenters/documentPresenter.ts +++ b/apps/gateway/src/presenters/documentPresenter.ts @@ -16,9 +16,9 @@ export const documentPresenterSchema = z.object({ path: z.string(), content: z.string(), contentHash: z.string().optional(), - config: z.object({}).passthrough(), - parameters: z.record(z.object({ type: z.nativeEnum(ParameterType) })), - provider: z.nativeEnum(Providers).optional(), + config: z.record(z.string(), z.any()), + parameters: z.record(z.string(), z.object({ type: z.enum(ParameterType) })), + provider: z.enum(Providers).optional(), }) type Parameters = z.infer['parameters'] diff --git a/apps/gateway/src/routes/api/v1/run/run.route.ts b/apps/gateway/src/routes/api/v1/run/run.route.ts index ad763577a1..0178e346d7 100644 --- a/apps/gateway/src/routes/api/v1/run/run.route.ts +++ b/apps/gateway/src/routes/api/v1/run/run.route.ts @@ -20,7 +20,7 @@ export const runRoute = createRoute({ schema: internalInfoSchema.extend({ path: z.string(), customIdentifier: z.string().optional(), - parameters: z.record(z.any()).optional().default({}), + parameters: z.record(z.string(), z.any()).optional().default({}), }), }, }, diff --git a/apps/gateway/src/routes/api/v2/documents/run/run.route.ts b/apps/gateway/src/routes/api/v2/documents/run/run.route.ts index 93006ddd5e..76a3b6d5ad 100644 --- a/apps/gateway/src/routes/api/v2/documents/run/run.route.ts +++ b/apps/gateway/src/routes/api/v2/documents/run/run.route.ts @@ -22,7 +22,7 @@ export const runRoute = createRoute({ path: z.string(), stream: z.boolean().default(false), customIdentifier: z.string().optional(), - parameters: z.record(z.any()).optional().default({}), + parameters: z.record(z.string(), z.any()).optional().default({}), }), }, }, diff --git a/apps/gateway/src/routes/api/v3/conversations/chat/chat.handler.test.ts b/apps/gateway/src/routes/api/v3/conversations/chat/chat.handler.test.ts index f22feb8a8b..d8f051b89f 100644 --- a/apps/gateway/src/routes/api/v3/conversations/chat/chat.handler.test.ts +++ b/apps/gateway/src/routes/api/v3/conversations/chat/chat.handler.test.ts @@ -77,7 +77,15 @@ const step: ChainStepResponse<'text'> = { streamType: 'text', text: 'fake-response-text', reasoning: undefined, - usage: { promptTokens: 4, completionTokens: 6, totalTokens: 10 }, + usage: { + inputTokens: 4, + outputTokens: 6, + promptTokens: 4, + completionTokens: 6, + totalTokens: 10, + reasoningTokens: 0, + cachedInputTokens: 0, + }, toolCalls: [], documentLogUuid: 'fake-document-log-uuid', providerLog: { diff --git a/apps/gateway/src/routes/api/v3/projects/versions/create/createCommit.route.ts b/apps/gateway/src/routes/api/v3/projects/versions/create/createCommit.route.ts index f9f9c5cba6..c92581adbb 100644 --- a/apps/gateway/src/routes/api/v3/projects/versions/create/createCommit.route.ts +++ b/apps/gateway/src/routes/api/v3/projects/versions/create/createCommit.route.ts @@ -11,8 +11,8 @@ export const CommitSchema = z.object({ authorName: z.string().nullable(), authorEmail: z.string().nullable(), authorId: z.number().nullable(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), + createdAt: z.iso.datetime(), + updatedAt: z.iso.datetime(), status: z.string(), parentCommitUuid: z.string().nullable(), }) diff --git a/apps/gateway/src/routes/api/v3/projects/versions/documents/logs/create.route.ts b/apps/gateway/src/routes/api/v3/projects/versions/documents/logs/create.route.ts index e67328e389..8764f22e70 100644 --- a/apps/gateway/src/routes/api/v3/projects/versions/documents/logs/create.route.ts +++ b/apps/gateway/src/routes/api/v3/projects/versions/documents/logs/create.route.ts @@ -11,10 +11,10 @@ const documentLogSchema = z.object({ commitId: z.number(), resolvedContent: z.string(), contentHash: z.string(), - parameters: z.record(z.any()), + parameters: z.record(z.string(), z.any()), customIdentifier: z.string().optional(), duration: z.number().optional(), - source: z.nativeEnum(LogSources), + source: z.enum(LogSources), createdAt: z.date(), updatedAt: z.date(), }) diff --git a/apps/gateway/src/routes/api/v3/projects/versions/documents/run/run.route.ts b/apps/gateway/src/routes/api/v3/projects/versions/documents/run/run.route.ts index c637343b19..79e10e2933 100644 --- a/apps/gateway/src/routes/api/v3/projects/versions/documents/run/run.route.ts +++ b/apps/gateway/src/routes/api/v3/projects/versions/documents/run/run.route.ts @@ -24,7 +24,7 @@ export const runRoute = createRoute({ path: z.string(), stream: z.boolean().default(false), customIdentifier: z.string().optional(), - parameters: z.record(z.any()).optional().default({}), + parameters: z.record(z.string(), z.any()).optional().default({}), tools: z.array(z.string()).optional().default([]), userMessage: z.string().optional(), background: z.boolean().default(false), diff --git a/apps/gateway/src/routes/api/v3/projects/versions/get/getCommit.route.ts b/apps/gateway/src/routes/api/v3/projects/versions/get/getCommit.route.ts index 6a1e8348fd..cfc1c12b80 100644 --- a/apps/gateway/src/routes/api/v3/projects/versions/get/getCommit.route.ts +++ b/apps/gateway/src/routes/api/v3/projects/versions/get/getCommit.route.ts @@ -11,8 +11,8 @@ export const VersionSchema = z.object({ authorName: z.string().nullable(), aucommitUuidthorEmail: z.string().nullable(), authorId: z.number().nullable(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), + createdAt: z.iso.datetime(), + updatedAt: z.iso.datetime(), status: z.string(), parentCommitUuid: z.string().nullable(), }) diff --git a/apps/gateway/src/routes/api/v3/projects/versions/getAll/getAllVersions.route.ts b/apps/gateway/src/routes/api/v3/projects/versions/getAll/getAllVersions.route.ts index 9330a64cef..3e9490ec54 100644 --- a/apps/gateway/src/routes/api/v3/projects/versions/getAll/getAllVersions.route.ts +++ b/apps/gateway/src/routes/api/v3/projects/versions/getAll/getAllVersions.route.ts @@ -12,8 +12,8 @@ export const VersionSchema = z.object({ version: z.number().nullable(), userId: z.string(), mergedAt: z.string().nullable(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), + createdAt: z.iso.datetime(), + updatedAt: z.iso.datetime(), deletedAt: z.string().nullable(), }) diff --git a/apps/web/package.json b/apps/web/package.json index 240a0fccc6..08e37ec349 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,6 +17,7 @@ "test:watch": "TZ=UTC vitest" }, "dependencies": { + "@ai-sdk/rsc": "^1.0.44", "@aws-sdk/client-s3": "3.850.0", "@datadog/browser-rum": "^6.21.1", "@intercom/messenger-js-sdk": "0.0.14", @@ -38,10 +39,10 @@ "@pilcrowjs/object-parser": "0.0.4", "@pipedream/sdk": "^2.0.0", "@sindresorhus/slugify": "2.2.1", - "@t3-oss/env-nextjs": "0.10.1", + "@t3-oss/env-nextjs": "0.13.8", "@types/diff-match-patch": "1.0.36", "@types/ip": "1.1.3", - "ai": "4.2.1", + "ai": "catalog:", "arctic": "3.6.0", "argon2": "0.44.0", "bullmq": "5.44.4", @@ -59,6 +60,7 @@ "monaco-editor": "0.50.0", "nanoid": "5.0.9", "next": "15.5.4", + "next-safe-action": "^8.0.11", "next-themes": "0.3.0", "nextjs-toploader": "1.6.12", "nprogress": "0.2.0", @@ -78,9 +80,6 @@ "use-debounce": "10.0.1", "yaml": "2.4.5", "zod": "catalog:", - "zod-to-json-schema": "3.24.5", - "zsa": "0.5.1", - "zsa-react": "0.2.3", "zustand": "4.5.6" }, "devDependencies": { @@ -93,6 +92,7 @@ "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6", + "@standard-schema/spec": "^1.0.0", "@testing-library/dom": "^10.3.2", "@testing-library/react": "^16.0.0", "@testing-library/react-hooks": "^8.0.1", diff --git a/apps/web/rollup.config.workers.mjs b/apps/web/rollup.config.workers.mjs index d53dfbe450..f071c66c52 100644 --- a/apps/web/rollup.config.workers.mjs +++ b/apps/web/rollup.config.workers.mjs @@ -15,6 +15,17 @@ import terser from '@rollup/plugin-terser' const isProduction = process.env.NODE_ENV === 'production' export default defineConfig({ + onwarn(warning, warn) { + if ( + // Suppress circular dependency warnings from Zod v4. + // issue: https://github.com/colinhacks/zod/issues/5275 + warning.code === 'CIRCULAR_DEPENDENCY' && + /zod\/v4/.test(warning.message) + ) { + return + } + warn(warning) + }, input: 'src/workers/readMetadata.ts', output: { entryFileNames: 'readMetadata.[hash].js', diff --git a/apps/web/src/actions/actions/execute.ts b/apps/web/src/actions/actions/execute.ts index 33731fda7f..cf624ef18b 100644 --- a/apps/web/src/actions/actions/execute.ts +++ b/apps/web/src/actions/actions/execute.ts @@ -9,23 +9,18 @@ import { executeAction } from '@latitude-data/core/services/actions/execute' import { z } from 'zod' import { authProcedure, withRateLimit } from '../procedures' -export const executeBackendAction = ( - await withRateLimit(authProcedure, { - limit: 10, - period: 60, - }) -) - .createServerAction() - .input( +export const executeBackendAction = authProcedure + .use(withRateLimit({ limit: 10, period: 60 })) + .inputSchema( z.object({ - type: z.nativeEnum(ActionType), + type: z.enum(ActionType), parameters: z.custom(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const result = await executeAction({ - type: input.type, - parameters: input.parameters, + type: parsedInput.type, + parameters: parsedInput.parameters, user: ctx.user, workspace: ctx.workspace, }).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/admin/documentTriggers/manualTrigger/email.ts b/apps/web/src/actions/admin/documentTriggers/manualTrigger/email.ts index 9c8b03c545..3497524bbe 100644 --- a/apps/web/src/actions/admin/documentTriggers/manualTrigger/email.ts +++ b/apps/web/src/actions/admin/documentTriggers/manualTrigger/email.ts @@ -10,11 +10,10 @@ import { Result, TypedResult } from '@latitude-data/core/lib/Result' import { Commit } from '@latitude-data/core/browser' export const manualEmailTriggerAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ - recipient: z.string().email(), - senderEmail: z.string().email(), + recipient: z.string().pipe(z.email()), + senderEmail: z.string().pipe(z.email()), senderName: z.string(), subject: z.string(), body: z.string(), @@ -25,7 +24,7 @@ export const manualEmailTriggerAction = withAdmin commitUuid: z.string().optional(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput: input }) => { let commitResult: TypedResult = Result.ok(undefined) if (input.commitUuid && input.projectId) { diff --git a/apps/web/src/actions/admin/features/create.ts b/apps/web/src/actions/admin/features/create.ts index 1f93cb5efa..0d3996c930 100644 --- a/apps/web/src/actions/admin/features/create.ts +++ b/apps/web/src/actions/admin/features/create.ts @@ -5,17 +5,16 @@ import { withAdmin } from '../../procedures' import { createFeature } from '@latitude-data/core/services/features/create' export const createFeatureAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ - name: z.string().min(1, { message: 'Name is required' }), + name: z.string().min(1, { error: 'Name is required' }), description: z.string().optional(), }), ) - .handler(async ({ input }) => { + .action(async ({ parsedInput }) => { const result = await createFeature({ - name: input.name, - description: input.description, + name: parsedInput.name, + description: parsedInput.description, }) return result.unwrap() diff --git a/apps/web/src/actions/admin/features/destroy.ts b/apps/web/src/actions/admin/features/destroy.ts index 7ee57ecd57..4fe75c9029 100644 --- a/apps/web/src/actions/admin/features/destroy.ts +++ b/apps/web/src/actions/admin/features/destroy.ts @@ -6,15 +6,12 @@ import { destroyFeature } from '@latitude-data/core/services/features/destroy' import { FeaturesRepository } from '@latitude-data/core/repositories/featuresRepository' export const destroyFeatureAction = withAdmin - .createServerAction() - .input( - z.object({ - id: z.number(), - }), - ) - .handler(async ({ input }) => { + .inputSchema(z.object({ id: z.number() })) + .action(async ({ parsedInput }) => { const featuresRepo = new FeaturesRepository() - const feature = await featuresRepo.find(input.id).then((r) => r.unwrap()) + const feature = await featuresRepo + .find(parsedInput.id) + .then((r) => r.unwrap()) const result = await destroyFeature(feature) return result.unwrap() diff --git a/apps/web/src/actions/admin/grants/issue.ts b/apps/web/src/actions/admin/grants/issue.ts index f718e90d9c..f75412d2db 100644 --- a/apps/web/src/actions/admin/grants/issue.ts +++ b/apps/web/src/actions/admin/grants/issue.ts @@ -8,28 +8,27 @@ import { z } from 'zod' import { withAdmin } from '../../procedures' export const issueGrantAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ - type: z.nativeEnum(QuotaType), + type: z.enum(QuotaType), amount: z.union([z.number(), z.literal('unlimited')]), periods: z.number().optional(), workspaceId: z.number(), }), ) - .handler(async ({ ctx, input }) => { - const workspace = await unsafelyFindWorkspace(input.workspaceId) + .action(async ({ ctx, parsedInput }) => { + const workspace = await unsafelyFindWorkspace(parsedInput.workspaceId) if (!workspace) { throw new BadRequestError('Workspace not found') } const grant = await issueGrant({ - type: input.type, - amount: input.amount, + type: parsedInput.type, + amount: parsedInput.amount, source: GrantSource.System, referenceId: ctx.user.id, workspace: workspace, - periods: input.periods, + periods: parsedInput.periods, }).then((r) => r.unwrap()) return grant diff --git a/apps/web/src/actions/admin/grants/revoke.ts b/apps/web/src/actions/admin/grants/revoke.ts index 483f5ec32b..400a168306 100644 --- a/apps/web/src/actions/admin/grants/revoke.ts +++ b/apps/web/src/actions/admin/grants/revoke.ts @@ -8,22 +8,21 @@ import { z } from 'zod' import { withAdmin } from '../../procedures' export const revokeGrantAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ grantId: z.number(), workspaceId: z.number(), }), ) - .handler(async ({ input }) => { - const workspace = await unsafelyFindWorkspace(input.workspaceId) + .action(async ({ parsedInput }) => { + const workspace = await unsafelyFindWorkspace(parsedInput.workspaceId) if (!workspace) { throw new BadRequestError('Workspace not found') } - const grantsRepository = new GrantsRepository(input.workspaceId) + const grantsRepository = new GrantsRepository(parsedInput.workspaceId) let grant = await grantsRepository - .find(input.grantId) + .find(parsedInput.grantId) .then((r) => r.unwrap()) grant = await revokeGrant({ grant, workspace }).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/admin/promocodes/createPromocode.ts b/apps/web/src/actions/admin/promocodes/createPromocode.ts index 9f4a108600..fb74a2e744 100644 --- a/apps/web/src/actions/admin/promocodes/createPromocode.ts +++ b/apps/web/src/actions/admin/promocodes/createPromocode.ts @@ -6,21 +6,20 @@ import { createPromocode } from '@latitude-data/core/services/promocodes/create' import { QuotaType } from '@latitude-data/constants' export const createPromocodeAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ code: z.string(), - quotaType: z.nativeEnum(QuotaType), + quotaType: z.enum(QuotaType), description: z.string(), amount: z.number(), }), ) - .handler(async ({ input }) => { + .action(async ({ parsedInput }) => { const createdPromocodeResult = await createPromocode({ - code: input.code, - quotaType: input.quotaType, - description: input.description, - amount: input.amount, + code: parsedInput.code, + quotaType: parsedInput.quotaType, + description: parsedInput.description, + amount: parsedInput.amount, }) return createdPromocodeResult.unwrap() diff --git a/apps/web/src/actions/admin/promocodes/deletePromocode.ts b/apps/web/src/actions/admin/promocodes/deletePromocode.ts index d36358f396..965293cee8 100644 --- a/apps/web/src/actions/admin/promocodes/deletePromocode.ts +++ b/apps/web/src/actions/admin/promocodes/deletePromocode.ts @@ -5,15 +5,10 @@ import { withAdmin } from '../../procedures' import { deletePromocode } from '@latitude-data/core/services/promocodes/delete' export const deletePromocodeAction = withAdmin - .createServerAction() - .input( - z.object({ - code: z.string(), - }), - ) - .handler(async ({ input }) => { + .inputSchema(z.object({ code: z.string() })) + .action(async ({ parsedInput }) => { const deletedPromocodeResult = await deletePromocode({ - code: input.code, + code: parsedInput.code, }) return deletedPromocodeResult.unwrap() }) diff --git a/apps/web/src/actions/admin/promocodes/expirePromocode.ts b/apps/web/src/actions/admin/promocodes/expirePromocode.ts index 0ca99256d8..2cfb9b52d5 100644 --- a/apps/web/src/actions/admin/promocodes/expirePromocode.ts +++ b/apps/web/src/actions/admin/promocodes/expirePromocode.ts @@ -5,15 +5,10 @@ import { withAdmin } from '../../procedures' import { expirePromocode } from '@latitude-data/core/services/promocodes/expire' export const expirePromocodeAction = withAdmin - .createServerAction() - .input( - z.object({ - code: z.string(), - }), - ) - .handler(async ({ input }) => { + .inputSchema(z.object({ code: z.string() })) + .action(async ({ parsedInput }) => { const expiredPromocodeResult = await expirePromocode({ - code: input.code, + code: parsedInput.code, }) return expiredPromocodeResult.unwrap() }) diff --git a/apps/web/src/actions/admin/users/impersonateAction.ts b/apps/web/src/actions/admin/users/impersonateAction.ts index d3710afd2b..b88253cf13 100644 --- a/apps/web/src/actions/admin/users/impersonateAction.ts +++ b/apps/web/src/actions/admin/users/impersonateAction.ts @@ -2,22 +2,17 @@ import { getUserFromCredentials } from '$/data-access' import { ROUTES } from '$/services/routes' -import { redirect } from 'next/navigation' import { z } from 'zod' import { setSession } from '$/services/auth/setSession' import { withAdmin } from '../../procedures' +import { frontendRedirect } from '$/lib/frontendRedirect' export const impersonateAction = withAdmin - .createServerAction() - .input( - z.object({ - email: z.string().email(), - }), - ) - .handler(async ({ input }) => { - const { user, workspace } = await getUserFromCredentials(input).then((r) => - r.unwrap(), + .inputSchema(z.object({ email: z.string().pipe(z.email()) })) + .action(async ({ parsedInput }) => { + const { user, workspace } = await getUserFromCredentials(parsedInput).then( + (r) => r.unwrap(), ) await setSession({ sessionData: { @@ -27,5 +22,5 @@ export const impersonateAction = withAdmin }, }) - redirect(ROUTES.root) + return frontendRedirect(ROUTES.root) }) diff --git a/apps/web/src/actions/admin/users/updateUserAction.ts b/apps/web/src/actions/admin/users/updateUserAction.ts index 0bb7200f8f..3be6aa7597 100644 --- a/apps/web/src/actions/admin/users/updateUserAction.ts +++ b/apps/web/src/actions/admin/users/updateUserAction.ts @@ -8,21 +8,20 @@ import { unsafelyGetUserByEmail } from '@latitude-data/core/data-access' import { NotFoundError } from '@latitude-data/constants/errors' export const updateUserAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ userEmail: z.string(), email: z.string(), }), ) - .handler(async ({ input }) => { - const user = await unsafelyGetUserByEmail(input.userEmail) + .action(async ({ parsedInput }) => { + const user = await unsafelyGetUserByEmail(parsedInput.userEmail) if (!user) { - throw new NotFoundError(`Not found user with email: ${input.email}`) + throw new NotFoundError(`Not found user with email: ${parsedInput.email}`) } return updateUser(user, { - email: input.email, + email: parsedInput.email, }).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/admin/workspaceFeatures/toggle.ts b/apps/web/src/actions/admin/workspaceFeatures/toggle.ts index 1bfa1f3100..bcf36b0417 100644 --- a/apps/web/src/actions/admin/workspaceFeatures/toggle.ts +++ b/apps/web/src/actions/admin/workspaceFeatures/toggle.ts @@ -5,18 +5,17 @@ import { withAdmin } from '../../procedures' import { toggleWorkspaceFeature } from '@latitude-data/core/services/workspaceFeatures/toggle' export const toggleWorkspaceFeatureAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ featureId: z.number(), enabled: z.boolean(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const result = await toggleWorkspaceFeature( ctx.workspace.id, - input.featureId, - input.enabled, + parsedInput.featureId, + parsedInput.enabled, ) return result.unwrap() diff --git a/apps/web/src/actions/admin/workspaceFeatures/toggleForWorkspaces.ts b/apps/web/src/actions/admin/workspaceFeatures/toggleForWorkspaces.ts index de5d06aa54..9498025a1c 100644 --- a/apps/web/src/actions/admin/workspaceFeatures/toggleForWorkspaces.ts +++ b/apps/web/src/actions/admin/workspaceFeatures/toggleForWorkspaces.ts @@ -5,19 +5,18 @@ import { withAdmin } from '../../procedures' import { toggleWorkspaceFeatureForMultipleWorkspaces } from '@latitude-data/core/services/workspaceFeatures/toggleForMultipleWorkspaces' export const toggleFeatureForWorkspacesAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ featureId: z.number(), workspaceIds: z.array(z.number()), enabled: z.boolean(), }), ) - .handler(async ({ input }) => { + .action(async ({ parsedInput }) => { const result = await toggleWorkspaceFeatureForMultipleWorkspaces( - input.featureId, - input.workspaceIds, - input.enabled, + parsedInput.featureId, + parsedInput.workspaceIds, + parsedInput.enabled, ) return result.unwrap() diff --git a/apps/web/src/actions/apiKeys/create.test.ts b/apps/web/src/actions/apiKeys/create.test.ts index fce394c625..ff96be7ecb 100644 --- a/apps/web/src/actions/apiKeys/create.test.ts +++ b/apps/web/src/actions/apiKeys/create.test.ts @@ -18,11 +18,11 @@ describe('createApiKeyAction', () => { it('errors when the user is not authenticated', async () => { mocks.getSession.mockResolvedValue(null) - const [_, error] = await createApiKeyAction({ + const { serverError } = await createApiKeyAction({ name: 'Test API Key', }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -37,11 +37,12 @@ describe('createApiKeyAction', () => { }) it('successfully creates an API key', async () => { - const [data, error] = await createApiKeyAction({ + const { data, serverError, validationErrors } = await createApiKeyAction({ name: 'Test API Key', }) - expect(error).toBeNull() + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.name).toEqual('Test API Key') expect(data!.id).toBeDefined() diff --git a/apps/web/src/actions/apiKeys/create.ts b/apps/web/src/actions/apiKeys/create.ts index b880880a1f..ecbeecfe6f 100644 --- a/apps/web/src/actions/apiKeys/create.ts +++ b/apps/web/src/actions/apiKeys/create.ts @@ -6,15 +6,14 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const createApiKeyAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ name: z.string(), }), ) - .handler(async ({ input, ctx }) => + .action(async ({ parsedInput, ctx }) => createApiKey({ - name: input.name, + name: parsedInput.name, workspace: ctx.workspace, }).then((r) => r.unwrap()), ) diff --git a/apps/web/src/actions/apiKeys/destroy.ts b/apps/web/src/actions/apiKeys/destroy.ts index 015028b67d..18536c7a2c 100644 --- a/apps/web/src/actions/apiKeys/destroy.ts +++ b/apps/web/src/actions/apiKeys/destroy.ts @@ -7,15 +7,12 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const destroyApiKeyAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.number(), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.number() })) + .action(async ({ parsedInput, ctx }) => { const apiKeysRepo = new ApiKeysRepository(ctx.workspace.id) - const apiKey = await apiKeysRepo.find(input.id).then((r) => r.unwrap()) + const apiKey = await apiKeysRepo + .find(parsedInput.id) + .then((r) => r.unwrap()) return destroyApiKey(apiKey).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/apiKeys/update.ts b/apps/web/src/actions/apiKeys/update.ts index 1f95f0c066..1bacc26d15 100644 --- a/apps/web/src/actions/apiKeys/update.ts +++ b/apps/web/src/actions/apiKeys/update.ts @@ -7,16 +7,14 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const updateApiKeyAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.coerce.number(), - name: z.string(), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.coerce.number(), name: z.string() })) + .action(async ({ parsedInput, ctx }) => { const apiKeysRepo = new ApiKeysRepository(ctx.workspace.id) - const apiKey = await apiKeysRepo.find(input.id).then((r) => r.unwrap()) + const apiKey = await apiKeysRepo + .find(parsedInput.id) + .then((r) => r.unwrap()) - return updateApiKey(apiKey, { name: input.name }).then((r) => r.unwrap()) + return updateApiKey(apiKey, { name: parsedInput.name }).then((r) => + r.unwrap(), + ) }) diff --git a/apps/web/src/actions/billing/createCustomerPortalAction.ts b/apps/web/src/actions/billing/createCustomerPortalAction.ts index cde4ec1187..889c015dc7 100644 --- a/apps/web/src/actions/billing/createCustomerPortalAction.ts +++ b/apps/web/src/actions/billing/createCustomerPortalAction.ts @@ -6,9 +6,8 @@ import { authProcedure } from '$/actions/procedures' /** * Creates a Stripe customer portal session for subscription management */ -export const createCustomerPortalAction = authProcedure - .createServerAction() - .handler(async ({ ctx }) => { +export const createCustomerPortalAction = authProcedure.action( + async ({ ctx }) => { const url = await createCustomerPortalSession({ currentUser: ctx.user, }) @@ -16,4 +15,5 @@ export const createCustomerPortalAction = authProcedure if (!url) throw new Error('No customer portal URL returned') return { url } - }) + }, +) diff --git a/apps/web/src/actions/commits/create.ts b/apps/web/src/actions/commits/create.ts index 3d303aeed7..a0153b8371 100644 --- a/apps/web/src/actions/commits/create.ts +++ b/apps/web/src/actions/commits/create.ts @@ -6,20 +6,19 @@ import { z } from 'zod' import { withProject } from '../procedures' export const createDraftCommitAction = withProject - .createServerAction() - .input( + .inputSchema( z.object({ - title: z.string().min(1, { message: 'Title is required' }), + title: z.string().min(1, { error: 'Title is required' }), description: z.string().optional().default(''), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const result = await createCommit({ project: ctx.project, user: ctx.user, data: { - title: input.title, - description: input.description, + title: parsedInput.title, + description: parsedInput.description, }, }) diff --git a/apps/web/src/actions/commits/createDraftWithContentAction.test.ts b/apps/web/src/actions/commits/createDraftWithContentAction.test.ts index a23cc66479..3ebbda2020 100644 --- a/apps/web/src/actions/commits/createDraftWithContentAction.test.ts +++ b/apps/web/src/actions/commits/createDraftWithContentAction.test.ts @@ -47,7 +47,7 @@ describe('createDraftWithContentAction', () => { describe('unauthorized', () => { it('errors when the user is not authenticated', async () => { - const [_, error] = await createDraftWithContentAction({ + const { serverError } = await createDraftWithContentAction({ projectId: project.id, title: 'New Draft', description: 'Draft Description', @@ -55,7 +55,7 @@ describe('createDraftWithContentAction', () => { content: 'New content', }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -68,7 +68,7 @@ describe('createDraftWithContentAction', () => { }) it('returns error when project is not found', async () => { - const [_, error] = await createDraftWithContentAction({ + const { serverError } = await createDraftWithContentAction({ projectId: 999992, title: 'New Draft', description: 'Draft Description', @@ -76,11 +76,11 @@ describe('createDraftWithContentAction', () => { content: 'New content', }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Project not found') }) it('returns error when document is not found', async () => { - const [_, error] = await createDraftWithContentAction({ + const { serverError } = await createDraftWithContentAction({ projectId: project.id, title: 'New Draft', description: 'Draft Description', @@ -88,32 +88,33 @@ describe('createDraftWithContentAction', () => { content: 'New content', }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Document not found') }) it('creates a draft and updates document content', async () => { const newContent = 'Updated content' - const [draft, error] = await createDraftWithContentAction({ - projectId: project.id, - title: 'New Draft', - description: 'Draft Description', - documentUuid: document.documentUuid, - content: newContent, - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = + await createDraftWithContentAction({ + projectId: project.id, + title: 'New Draft', + description: 'Draft Description', + documentUuid: document.documentUuid, + content: newContent, + }) const docsRepo = new DocumentVersionsRepository(workspace.id) const newDocumentVersion = await docsRepo .getDocumentAtCommit({ projectId: project.id, - commitUuid: draft!.uuid, + commitUuid: data?.uuid, documentUuid: document.documentUuid, }) .then((r) => r.unwrap()) - expect(draft!.mergedAt).toBeNull() - expect(draft!.title).toEqual('New Draft') + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() + expect(data?.mergedAt).toBeNull() + expect(data?.title).toEqual('New Draft') expect(newDocumentVersion.content).toEqual(newContent) }) diff --git a/apps/web/src/actions/commits/createDraftWithContentAction.ts b/apps/web/src/actions/commits/createDraftWithContentAction.ts index d569ae0c3d..9e5cd747a1 100644 --- a/apps/web/src/actions/commits/createDraftWithContentAction.ts +++ b/apps/web/src/actions/commits/createDraftWithContentAction.ts @@ -8,8 +8,7 @@ import { z } from 'zod' import { withProject } from '../procedures' export const createDraftWithContentAction = withProject - .createServerAction() - .input( + .inputSchema( z.object({ title: z.string(), description: z.string().optional().default(''), @@ -17,15 +16,15 @@ export const createDraftWithContentAction = withProject content: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { user, workspace, project } = ctx const draft = await createCommit({ project, user, data: { - title: input.title, - description: input.description, + title: parsedInput.title, + description: parsedInput.description, }, }).then((r) => r.unwrap()) @@ -34,14 +33,14 @@ export const createDraftWithContentAction = withProject .getDocumentAtCommit({ commitUuid: draft.uuid, projectId: project.id, - documentUuid: input.documentUuid, + documentUuid: parsedInput.documentUuid, }) .then((r) => r.unwrap()) await updateDocument({ commit: draft, document, - content: input.content, + content: parsedInput.content, }).then((r) => r.unwrap()) return draft diff --git a/apps/web/src/actions/commits/createDraftWithPromptlUpgrade.ts b/apps/web/src/actions/commits/createDraftWithPromptlUpgrade.ts index 220c59b327..99944925e2 100644 --- a/apps/web/src/actions/commits/createDraftWithPromptlUpgrade.ts +++ b/apps/web/src/actions/commits/createDraftWithPromptlUpgrade.ts @@ -9,19 +9,18 @@ import { createCommit } from '@latitude-data/core/services/commits/create' import { updateDocument } from '@latitude-data/core/services/documents/update' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const createDraftWithPromptlUpgradeAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ documentUuid: z.string().optional(), draftUuid: z.string().optional(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { user, workspace, project } = ctx - const { documentUuid, draftUuid } = input + const { documentUuid, draftUuid } = parsedInput let draft: Commit diff --git a/apps/web/src/actions/commits/deleteDraftCommit.test.ts b/apps/web/src/actions/commits/deleteDraftCommit.test.ts index cb45ffa139..fa5aceedbf 100644 --- a/apps/web/src/actions/commits/deleteDraftCommit.test.ts +++ b/apps/web/src/actions/commits/deleteDraftCommit.test.ts @@ -43,12 +43,12 @@ describe('deleteDraftCommitAction', () => { describe('unauthorized', () => { it('errors when the user is not authenticated', async () => { - const [_, error] = await deleteDraftCommitAction({ + const { serverError } = await deleteDraftCommitAction({ projectId: project.id, id: commit.id, }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -61,12 +61,12 @@ describe('deleteDraftCommitAction', () => { }) it('returns error when project is not found', async () => { - const [_, error] = await deleteDraftCommitAction({ + const { serverError } = await deleteDraftCommitAction({ projectId: 33, id: commit.id, }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Project not found') }) it('returns error when commit does not belongs to project', async () => { @@ -76,20 +76,20 @@ describe('deleteDraftCommitAction', () => { project: urelatedProject, user: unrelatedUser, }) - const [_, error] = await deleteDraftCommitAction({ + const { serverError } = await deleteDraftCommitAction({ projectId: project.id, id: unrelatedCommit.id, }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Commit not found') }) it('returns error when the commit is merged', async () => { - const [_, error] = await deleteDraftCommitAction({ + const { serverError } = await deleteDraftCommitAction({ projectId: project.id, id: commit.id, }) - expect(error!.name).toEqual('BadRequestError') + expect(serverError).toEqual('Cannot modify a merged commit') }) it('returns all draft commits', async () => { @@ -97,7 +97,7 @@ describe('deleteDraftCommitAction', () => { project, user, }) - const [data] = await deleteDraftCommitAction({ + const { data } = await deleteDraftCommitAction({ projectId: project.id, id: draft.id, }) diff --git a/apps/web/src/actions/commits/deleteDraftCommitAction.ts b/apps/web/src/actions/commits/deleteDraftCommitAction.ts index fa3874bb33..3dcccd0f6b 100644 --- a/apps/web/src/actions/commits/deleteDraftCommitAction.ts +++ b/apps/web/src/actions/commits/deleteDraftCommitAction.ts @@ -4,19 +4,18 @@ import { CommitsRepository } from '@latitude-data/core/repositories' import { deleteCommitDraft } from '@latitude-data/core/services/commits/delete' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const deleteDraftCommitAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ id: z.number(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const commitScope = new CommitsRepository(ctx.workspace.id) const commit = await commitScope - .getCommitById(input.id) + .getCommitById(parsedInput.id) .then((r) => r.unwrap()) return deleteCommitDraft(commit).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/commits/publishDraftCommit.test.ts b/apps/web/src/actions/commits/publishDraftCommit.test.ts index e98a4dc5e3..aadbd4e8e7 100644 --- a/apps/web/src/actions/commits/publishDraftCommit.test.ts +++ b/apps/web/src/actions/commits/publishDraftCommit.test.ts @@ -49,12 +49,12 @@ describe('publishDraftCommitAction', () => { describe('unauthorized', () => { it('errors when the user is not authenticated', async () => { - const [_, error] = await publishDraftCommitAction({ + const { serverError } = await publishDraftCommitAction({ projectId: project.id, id: commit.id, }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -67,12 +67,12 @@ describe('publishDraftCommitAction', () => { }) it('returns error when project is not found', async () => { - const [_, error] = await publishDraftCommitAction({ + const { serverError } = await publishDraftCommitAction({ projectId: 999992, id: commit.id, }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Project not found') }) it('returns error when commit does not belongs to project', async () => { @@ -82,20 +82,20 @@ describe('publishDraftCommitAction', () => { project: urelatedProject, user: unrelatedUser, }) - const [_, error] = await publishDraftCommitAction({ + const { serverError } = await publishDraftCommitAction({ projectId: project.id, id: unrelatedCommit.id, }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Commit not found') }) it('returns error when the commit is merged', async () => { - const [_, error] = await publishDraftCommitAction({ + const { serverError } = await publishDraftCommitAction({ projectId: project.id, id: commit.id, }) - expect(error!.name).toEqual('BadRequestError') + expect(serverError).toEqual('Cannot modify a merged commit') }) it('merge the commit', async () => { @@ -108,7 +108,7 @@ describe('publishDraftCommitAction', () => { content: 'conentt updated', commit: draft, }).then((r) => r.unwrap()) - const [data] = await publishDraftCommitAction({ + const { data } = await publishDraftCommitAction({ projectId: project.id, id: draft.id, }) diff --git a/apps/web/src/actions/commits/publishDraftCommitAction.ts b/apps/web/src/actions/commits/publishDraftCommitAction.ts index 5a2f654f4a..c8d11dc3d2 100644 --- a/apps/web/src/actions/commits/publishDraftCommitAction.ts +++ b/apps/web/src/actions/commits/publishDraftCommitAction.ts @@ -5,26 +5,25 @@ import { CommitsRepository } from '@latitude-data/core/repositories' import { updateAndMergeCommit } from '@latitude-data/core/services/commits/updateAndMerge' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const publishDraftCommitAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ id: z.number(), title: z.string().optional(), description: z.string().optional(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const commitScope = new CommitsRepository(ctx.workspace.id) const commit = await commitScope - .getCommitById(input.id) + .getCommitById(parsedInput.id) .then((r) => r.unwrap()) const merged = await updateAndMergeCommit(commit, { - title: input.title, - description: input.description, + title: parsedInput.title, + description: parsedInput.description, }).then((r) => r.unwrap()) publisher.publishLater({ diff --git a/apps/web/src/actions/copilot/refineApply.ts b/apps/web/src/actions/copilot/refineApply.ts index c7d0c1865a..a23a4f1d97 100644 --- a/apps/web/src/actions/copilot/refineApply.ts +++ b/apps/web/src/actions/copilot/refineApply.ts @@ -6,16 +6,11 @@ import Transaction from '@latitude-data/core/lib/Transaction' import { createCommit } from '@latitude-data/core/services/commits/create' import { updateDocument } from '@latitude-data/core/services/documents/update' import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' export const refineApplyAction = withDocument - .createServerAction() - .input( - z.object({ - prompt: z.string(), - }), - ) - .handler(async ({ ctx, input }) => { + .inputSchema(withDocumentSchema.extend({ prompt: z.string() })) + .action(async ({ ctx, parsedInput }) => { const transaction = new Transaction() const result = transaction .call( @@ -38,13 +33,13 @@ export const refineApplyAction = withDocument { commit: draft, document: ctx.document, - content: input.prompt, + content: parsedInput.prompt, }, transaction, ).then((r) => r.unwrap()) } - return Result.ok({ prompt: input.prompt, draft }) + return Result.ok({ prompt: parsedInput.prompt, draft }) }, () => publisher.publishLater({ diff --git a/apps/web/src/actions/copilot/refinePrompt.ts b/apps/web/src/actions/copilot/refinePrompt.ts index 44b9238b77..bea4be5465 100644 --- a/apps/web/src/actions/copilot/refinePrompt.ts +++ b/apps/web/src/actions/copilot/refinePrompt.ts @@ -17,17 +17,16 @@ import { } from '@latitude-data/core/services/documentSuggestions/serialize' import { env } from '@latitude-data/env' import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' export const refinePromptAction = withDocument - .createServerAction() - .input( - z.object({ + .inputSchema( + withDocumentSchema.extend({ evaluationUuid: z.string().optional(), resultUuids: z.array(z.string()).optional(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { if (!env.LATITUDE_CLOUD) { throw new BadRequestError(CLOUD_MESSAGES.refinePrompt) } @@ -44,7 +43,7 @@ export const refinePromptAction = withDocument throw new BadRequestError('COPILOT_PROMPT_REFINE_PATH is not set') } - const { evaluationUuid, resultUuids } = input + const { evaluationUuid, resultUuids } = parsedInput let evaluation let serializedEvaluation diff --git a/apps/web/src/actions/copilot/requestSuggestion.ts b/apps/web/src/actions/copilot/requestSuggestion.ts index 997894101b..2c1b81835a 100644 --- a/apps/web/src/actions/copilot/requestSuggestion.ts +++ b/apps/web/src/actions/copilot/requestSuggestion.ts @@ -17,8 +17,7 @@ import { authProcedure } from '../procedures' // Pass entityUuid and entityType so this can be used to track // events for documents and evaluations. Now the event is tied to documents export const requestSuggestionAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ projectId: z.number(), commitUuid: z.string(), @@ -26,7 +25,7 @@ export const requestSuggestionAction = authProcedure request: z.string(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { if (!env.LATITUDE_CLOUD) { throw new BadRequestError(CLOUD_MESSAGES.promptSuggestions) } @@ -43,7 +42,7 @@ export const requestSuggestionAction = authProcedure throw new BadRequestError('COPILOT_PROMPT_EDITOR_COPILOT_PATH is not set') } - const { projectId, commitUuid, documentUuid, request } = input + const { projectId, commitUuid, documentUuid, request } = parsedInput const documentsScope = new DocumentVersionsRepository(ctx.workspace.id) const document = await documentsScope diff --git a/apps/web/src/actions/datasetRows/create.ts b/apps/web/src/actions/datasetRows/create.ts index 613dcddc76..a1ef6a8a86 100644 --- a/apps/web/src/actions/datasetRows/create.ts +++ b/apps/web/src/actions/datasetRows/create.ts @@ -6,12 +6,11 @@ import { DatasetsRepository } from '@latitude-data/core/repositories' import { authProcedure } from '$/actions/procedures' export const createDatasetRowAction = authProcedure - .createServerAction() - .input(z.object({ datasetId: z.number() }), { type: 'json' }) - .handler(async ({ ctx, input }) => { + .inputSchema(z.object({ datasetId: z.number() })) + .action(async ({ ctx, parsedInput }) => { const datasetRepo = new DatasetsRepository(ctx.workspace.id) const dataset = await datasetRepo - .find(input.datasetId) + .find(parsedInput.datasetId) .then((r) => r.unwrap()) return createDatasetEmptyRow({ diff --git a/apps/web/src/actions/datasetRows/delete.ts b/apps/web/src/actions/datasetRows/delete.ts index fd096e0285..1c06f9fded 100644 --- a/apps/web/src/actions/datasetRows/delete.ts +++ b/apps/web/src/actions/datasetRows/delete.ts @@ -9,23 +9,21 @@ import { import { authProcedure } from '$/actions/procedures' export const deleteRowsAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ datasetId: z.number(), rowIds: z.array(z.number()), }), - { type: 'json' }, ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const datasetRepo = new DatasetsRepository(ctx.workspace.id) const dataset = await datasetRepo - .find(input.datasetId) + .find(parsedInput.datasetId) .then((r) => r.unwrap()) const scope = new DatasetRowsRepository(ctx.workspace.id) const rows = await scope.findManyByDataset({ dataset, - rowIds: input.rowIds, + rowIds: parsedInput.rowIds, }) return deleteManyRows({ diff --git a/apps/web/src/actions/datasetRows/update.ts b/apps/web/src/actions/datasetRows/update.ts index 05cbec5fc0..fd4b7aa9a0 100644 --- a/apps/web/src/actions/datasetRows/update.ts +++ b/apps/web/src/actions/datasetRows/update.ts @@ -10,18 +10,18 @@ import { authProcedure } from '$/actions/procedures' import { DatasetRowDataContent } from '@latitude-data/core/schema' const rowDataSchema = z.record( - z.custom((val) => { - return ( - ['string', 'number', 'boolean', 'object'].includes(typeof val) || + z.string(), + z.custom( + (val): val is DatasetRowDataContent => val === null || - val === undefined - ) - }), + val === undefined || + ['string', 'number', 'boolean', 'object'].includes(typeof val), + { message: 'Invalid row data' }, + ), ) export const updateDatasetRowAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ datasetId: z.number(), rows: z.array( @@ -31,19 +31,18 @@ export const updateDatasetRowAction = authProcedure }), ), }), - { type: 'json' }, ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const datasetRepo = new DatasetsRepository(ctx.workspace.id) const dataset = await datasetRepo - .find(input.datasetId) + .find(parsedInput.datasetId) .then((r) => r.unwrap()) const scope = new DatasetRowsRepository(ctx.workspace.id) const rows = await scope.findManyByDataset({ dataset, - rowIds: input.rows.map((r) => r.rowId), + rowIds: parsedInput.rows.map((r) => r.rowId), }) - const rowsByRowId = new Map(input.rows.map((r) => [r.rowId, r])) + const rowsByRowId = new Map(parsedInput.rows.map((r) => [r.rowId, r])) const rowsMap = rows.map((r) => ({ rowId: rowsByRowId.get(r.id)!.rowId, rowData: rowsByRowId.get(r.id)!.rowData, diff --git a/apps/web/src/actions/datasets/createFromLogs.test.ts b/apps/web/src/actions/datasets/createFromLogs.test.ts index 767820f29a..aa551d2685 100644 --- a/apps/web/src/actions/datasets/createFromLogs.test.ts +++ b/apps/web/src/actions/datasets/createFromLogs.test.ts @@ -114,7 +114,7 @@ describe('createDatasetFromLogsAction', () => { // Setup unauthorized session mocks.getSession.mockResolvedValue(null) - const [_, error] = await createDatasetFromLogsAction({ + const { serverError } = await createDatasetFromLogsAction({ projectId: project.id, commitUuid: commit.uuid, documentUuid: document.documentUuid, @@ -125,7 +125,7 @@ describe('createDatasetFromLogsAction', () => { filterOptions, }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -133,19 +133,21 @@ describe('createDatasetFromLogsAction', () => { it('processes logs synchronously when in PARTIAL mode with fewer logs than the batch limit', async () => { const selectedDocumentLogIds = [1, 2, 3] - const [result, error] = await createDatasetFromLogsAction({ - projectId: project.id, - commitUuid: commit.uuid, - documentUuid: document.documentUuid, - name: 'Test Dataset', - selectionMode: 'PARTIAL', - selectedDocumentLogIds, - excludedDocumentLogIds: [], - filterOptions, - }) + const { data, serverError, validationErrors } = + await createDatasetFromLogsAction({ + projectId: project.id, + commitUuid: commit.uuid, + documentUuid: document.documentUuid, + name: 'Test Dataset', + selectionMode: 'PARTIAL', + selectedDocumentLogIds, + excludedDocumentLogIds: [], + filterOptions, + }) - expect(error).toBeNull() - expect(result).toEqual({ mode: 'sync', result: { success: true } }) + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() + expect(data).toEqual({ mode: 'sync', result: { success: true } }) // Verify findOrCreateDataset was called with correct params expect(mocks.findOrCreateDataset).toHaveBeenCalledWith({ @@ -171,19 +173,21 @@ describe('createDatasetFromLogsAction', () => { // Create an array with more IDs than the batch limit const manyIds = Array.from({ length: 30 }, (_, i) => i + 1) - const [result, error] = await createDatasetFromLogsAction({ - projectId: project.id, - commitUuid: commit.uuid, - documentUuid: document.documentUuid, - name: 'Test Dataset', - selectionMode: 'PARTIAL', - selectedDocumentLogIds: manyIds, - excludedDocumentLogIds: [], - filterOptions, - }) + const { data, serverError, validationErrors } = + await createDatasetFromLogsAction({ + projectId: project.id, + commitUuid: commit.uuid, + documentUuid: document.documentUuid, + name: 'Test Dataset', + selectionMode: 'PARTIAL', + selectedDocumentLogIds: manyIds, + excludedDocumentLogIds: [], + filterOptions, + }) - expect(error).toBeNull() - expect(result).toEqual({ mode: 'async' }) + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() + expect(data).toEqual({ mode: 'async' }) // Verify findOrCreateDataset was not called expect(mocks.findOrCreateDataset).not.toHaveBeenCalled() @@ -208,19 +212,21 @@ describe('createDatasetFromLogsAction', () => { }) it('processes logs asynchronously when in ALL mode', async () => { - const [result, error] = await createDatasetFromLogsAction({ - projectId: project.id, - commitUuid: commit.uuid, - documentUuid: document.documentUuid, - name: 'Test Dataset', - selectionMode: 'ALL', - selectedDocumentLogIds: [], - excludedDocumentLogIds: [], - filterOptions, - }) + const { data, serverError, validationErrors } = + await createDatasetFromLogsAction({ + projectId: project.id, + commitUuid: commit.uuid, + documentUuid: document.documentUuid, + name: 'Test Dataset', + selectionMode: 'ALL', + selectedDocumentLogIds: [], + excludedDocumentLogIds: [], + filterOptions, + }) - expect(error).toBeNull() - expect(result).toEqual({ mode: 'async' }) + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() + expect(data).toEqual({ mode: 'async' }) // Verify queue was used with correct params expect(mocks.defaultQueueAddMock).toHaveBeenCalledWith( @@ -239,19 +245,21 @@ describe('createDatasetFromLogsAction', () => { }) it('processes logs asynchronously when in ALL_EXCEPT mode', async () => { - const [result, error] = await createDatasetFromLogsAction({ - projectId: project.id, - commitUuid: commit.uuid, - documentUuid: document.documentUuid, - name: 'Test Dataset', - selectionMode: 'ALL_EXCEPT', - selectedDocumentLogIds: [], - excludedDocumentLogIds: [1, 2], - filterOptions, - }) + const { data, serverError, validationErrors } = + await createDatasetFromLogsAction({ + projectId: project.id, + commitUuid: commit.uuid, + documentUuid: document.documentUuid, + name: 'Test Dataset', + selectionMode: 'ALL_EXCEPT', + selectedDocumentLogIds: [], + excludedDocumentLogIds: [1, 2], + filterOptions, + }) - expect(error).toBeNull() - expect(result).toEqual({ mode: 'async' }) + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() + expect(data).toEqual({ mode: 'async' }) // Verify queue was used with correct params expect(mocks.defaultQueueAddMock).toHaveBeenCalledWith( diff --git a/apps/web/src/actions/datasets/createFromLogs.ts b/apps/web/src/actions/datasets/createFromLogs.ts index 3249e78336..05d743d7c8 100644 --- a/apps/web/src/actions/datasets/createFromLogs.ts +++ b/apps/web/src/actions/datasets/createFromLogs.ts @@ -2,7 +2,7 @@ import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' import { documentLogFilterOptionsSchema } from '@latitude-data/core/browser' import { queues } from '@latitude-data/core/queues' import { findOrCreateDataset } from '@latitude-data/core/services/datasets/findOrCreate' @@ -11,9 +11,8 @@ import { updateDatasetFromLogs } from '@latitude-data/core/services/datasets/cre const MAX_SYNC_LOGS_BATCH_SIZE = 25 export const createDatasetFromLogsAction = withDocument - .createServerAction() - .input( - z.object({ + .inputSchema( + withDocumentSchema.extend({ name: z.string(), selectionMode: z.enum(['ALL', 'ALL_EXCEPT', 'PARTIAL']), selectedDocumentLogIds: z.array(z.number().or(z.string())), @@ -21,13 +20,13 @@ export const createDatasetFromLogsAction = withDocument filterOptions: documentLogFilterOptionsSchema, }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { if ( - input.selectionMode === 'PARTIAL' && - input.selectedDocumentLogIds.length <= MAX_SYNC_LOGS_BATCH_SIZE + parsedInput.selectionMode === 'PARTIAL' && + parsedInput.selectedDocumentLogIds.length <= MAX_SYNC_LOGS_BATCH_SIZE ) { const dataset = await findOrCreateDataset({ - name: input.name, + name: parsedInput.name, author: ctx.user, workspace: ctx.workspace, }).then((r) => r.unwrap()) @@ -35,7 +34,7 @@ export const createDatasetFromLogsAction = withDocument const result = await updateDatasetFromLogs({ dataset, workspace: ctx.workspace, - documentLogIds: input.selectedDocumentLogIds as number[], + documentLogIds: parsedInput.selectedDocumentLogIds as number[], }).then((r) => r.unwrap()) return { @@ -47,14 +46,14 @@ export const createDatasetFromLogsAction = withDocument const { defaultQueue } = await queues() defaultQueue.add('createDatasetFromLogsJob', { - name: input.name, + name: parsedInput.name, userId: ctx.user.id, workspaceId: ctx.workspace.id, documentVersionId: ctx.document.id, - selectionMode: input.selectionMode, - selectedDocumentLogIds: input.selectedDocumentLogIds, - excludedDocumentLogIds: input.excludedDocumentLogIds, - filterOptions: input.filterOptions, + selectionMode: parsedInput.selectionMode, + selectedDocumentLogIds: parsedInput.selectedDocumentLogIds, + excludedDocumentLogIds: parsedInput.excludedDocumentLogIds, + filterOptions: parsedInput.filterOptions, }) return { diff --git a/apps/web/src/actions/datasets/destroy.ts b/apps/web/src/actions/datasets/destroy.ts index 3a37f76c22..027283c7c1 100644 --- a/apps/web/src/actions/datasets/destroy.ts +++ b/apps/web/src/actions/datasets/destroy.ts @@ -7,14 +7,9 @@ import { destroyDataset } from '@latitude-data/core/services/datasets/destroy' import { authProcedure } from '../procedures' export const destroyDatasetAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.string(), - }), - ) - .handler(async ({ input, ctx }) => { - const { id } = input + .inputSchema(z.object({ id: z.string() })) + .action(async ({ parsedInput, ctx }) => { + const { id } = parsedInput const repo = new DatasetsRepository(ctx.workspace.id) const dataset = await repo.find(id).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/datasets/generateDataset.ts b/apps/web/src/actions/datasets/generateDataset.ts index 355d4a8c10..96ddc30a45 100644 --- a/apps/web/src/actions/datasets/generateDataset.ts +++ b/apps/web/src/actions/datasets/generateDataset.ts @@ -14,8 +14,7 @@ import { authProcedure } from '$/actions/procedures' import { createDatasetFromJson } from '@latitude-data/core/services/datasets/createFromJson' export const generateDatasetAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ parameters: z.string(), description: z.string(), @@ -23,7 +22,7 @@ export const generateDatasetAction = authProcedure name: z.string(), }), ) - .handler(async ({ input }) => { + .action(async ({ parsedInput }) => { if (!env.LATITUDE_CLOUD) { throw new BadRequestError(CLOUD_MESSAGES.generateDatasets) } @@ -52,9 +51,9 @@ export const generateDatasetAction = authProcedure { stream: false, parameters: { - row_count: input.rowCount, - parameters: input.parameters, - user_message: input.description, + row_count: parsedInput.rowCount, + parameters: parsedInput.parameters, + user_message: parsedInput.description, }, }, ) @@ -67,7 +66,7 @@ export const generateDatasetAction = authProcedure } const response = sdkResult.response as ChainStepResponse<'object'> - const name = input.name + const name = parsedInput.name const result = await createDatasetFromJson({ author: user, workspace, diff --git a/apps/web/src/actions/datasets/updateColumn.ts b/apps/web/src/actions/datasets/updateColumn.ts index 732174b2b9..1b9e51886e 100644 --- a/apps/web/src/actions/datasets/updateColumn.ts +++ b/apps/web/src/actions/datasets/updateColumn.ts @@ -16,24 +16,25 @@ const datasetColumnRoleSchema = z.enum( ], ) export const updateDatasetColumnAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ datasetId: z.string(), identifier: z.string(), - name: z.string().min(1, { message: 'Name is required' }), + name: z.string().min(1, { error: 'Name is required' }), role: datasetColumnRoleSchema, }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const repo = new DatasetsRepository(ctx.workspace.id) - const dataset = await repo.find(input.datasetId).then((r) => r.unwrap()) + const dataset = await repo + .find(parsedInput.datasetId) + .then((r) => r.unwrap()) return updateDatasetColumn({ dataset, data: { - identifier: input.identifier, - name: input.name, - role: input.role, + identifier: parsedInput.identifier, + name: parsedInput.name, + role: parsedInput.role, }, }).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/documentLogs/downloadLogs.ts b/apps/web/src/actions/documentLogs/downloadLogs.ts index c8e77e42c1..0a1ee6ea1a 100644 --- a/apps/web/src/actions/documentLogs/downloadLogs.ts +++ b/apps/web/src/actions/documentLogs/downloadLogs.ts @@ -1,23 +1,22 @@ 'use server' import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' import { documentLogFilterOptionsSchema } from '@latitude-data/core/browser' import { queues } from '@latitude-data/core/queues' import { generateUUIDIdentifier } from '@latitude-data/core/lib/generateUUID' export const downloadLogsAsyncAction = withDocument - .createServerAction() - .input( - z.object({ + .inputSchema( + withDocumentSchema.extend({ selectionMode: z.enum(['ALL', 'ALL_EXCEPT']), excludedDocumentLogIds: z.array(z.number()), filterOptions: documentLogFilterOptionsSchema, }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { user, document } = ctx - const { selectionMode, excludedDocumentLogIds, filterOptions } = input + const { selectionMode, excludedDocumentLogIds, filterOptions } = parsedInput const { defaultQueue } = await queues() defaultQueue.add('downloadLogsJob', { diff --git a/apps/web/src/actions/documentLogs/upload.ts b/apps/web/src/actions/documentLogs/upload.ts index 419408d5bf..9733156e53 100644 --- a/apps/web/src/actions/documentLogs/upload.ts +++ b/apps/web/src/actions/documentLogs/upload.ts @@ -12,8 +12,7 @@ import { z } from 'zod' import { withDocument } from '../procedures' export const uploadDocumentLogsAction = withDocument - .createServerAction() - .input( + .inputSchema( z.object({ csvDelimiter: z.enum(DELIMITERS_KEYS, { message: 'Choose a valid delimiter option', @@ -29,7 +28,7 @@ export const uploadDocumentLogsAction = withDocument ), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const commitsScope = new CommitsRepository(ctx.workspace.id) const commit = await commitsScope .getCommitByUuid({ @@ -42,8 +41,8 @@ export const uploadDocumentLogsAction = withDocument workspace: ctx.workspace, document: ctx.document, commit, - csvDelimiter: input.csvDelimiter, - logsFile: input.logsFile, + csvDelimiter: parsedInput.csvDelimiter, + logsFile: parsedInput.logsFile, }) return { success: true } diff --git a/apps/web/src/actions/documentSuggestions/apply.ts b/apps/web/src/actions/documentSuggestions/apply.ts index 7a236060c4..a3bfccbfda 100644 --- a/apps/web/src/actions/documentSuggestions/apply.ts +++ b/apps/web/src/actions/documentSuggestions/apply.ts @@ -3,28 +3,27 @@ import { DocumentSuggestionsRepository } from '@latitude-data/core/repositories' import { applyDocumentSuggestion } from '@latitude-data/core/services/documentSuggestions/apply' import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' export const applyDocumentSuggestionAction = withDocument - .createServerAction() - .input( - z.object({ + .inputSchema( + withDocumentSchema.extend({ suggestionId: z.number(), prompt: z.string().optional(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const suggestionsRepository = new DocumentSuggestionsRepository( ctx.workspace.id, ) const suggestion = await suggestionsRepository - .find(input.suggestionId) + .find(parsedInput.suggestionId) .then((r) => r.unwrap()) const result = await applyDocumentSuggestion({ suggestion: suggestion, commit: ctx.commit, - prompt: input.prompt, + prompt: parsedInput.prompt, workspace: ctx.workspace, project: ctx.project, user: ctx.user, diff --git a/apps/web/src/actions/documentSuggestions/discard.ts b/apps/web/src/actions/documentSuggestions/discard.ts index d6a8ddb47f..b4f9ba97ef 100644 --- a/apps/web/src/actions/documentSuggestions/discard.ts +++ b/apps/web/src/actions/documentSuggestions/discard.ts @@ -3,19 +3,14 @@ import { DocumentSuggestionsRepository } from '@latitude-data/core/repositories' import { discardDocumentSuggestion } from '@latitude-data/core/services/documentSuggestions/discard' import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' export const discardDocumentSuggestionAction = withDocument - .createServerAction() - .input( - z.object({ - suggestionId: z.number(), - }), - ) - .handler(async ({ ctx, input }) => { + .inputSchema(withDocumentSchema.extend({ suggestionId: z.number() })) + .action(async ({ ctx, parsedInput }) => { const repository = new DocumentSuggestionsRepository(ctx.workspace.id) const suggestion = await repository - .find(input.suggestionId) + .find(parsedInput.suggestionId) .then((r) => r.unwrap()) const result = await discardDocumentSuggestion({ diff --git a/apps/web/src/actions/documents/assignDatasetAction.ts b/apps/web/src/actions/documents/assignDatasetAction.ts index cd181875ca..32dd5e7a5e 100644 --- a/apps/web/src/actions/documents/assignDatasetAction.ts +++ b/apps/web/src/actions/documents/assignDatasetAction.ts @@ -1,12 +1,12 @@ 'use server' import { assignDataset } from '@latitude-data/core/services/documents/assignDataset' -import { withDataset } from '../procedures' +import { withDataset, withDatasetSchema } from '../procedures' export const assignDatasetAction = withDataset - .createServerAction() - .handler(async ({ ctx }) => { - return await assignDataset({ + .inputSchema(withDatasetSchema.extend({})) + .action(async ({ ctx }) => { + return assignDataset({ document: ctx.document, dataset: ctx.dataset, }).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/documents/create.ts b/apps/web/src/actions/documents/create.ts index 5719759319..f3584f98f6 100644 --- a/apps/web/src/actions/documents/create.ts +++ b/apps/web/src/actions/documents/create.ts @@ -1,34 +1,35 @@ 'use server' +import { z } from 'zod' import { CommitsRepository } from '@latitude-data/core/repositories' import { createNewDocument } from '@latitude-data/core/services/documents/create' -import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const createDocumentVersionAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ commitUuid: z.string(), path: z.string(), agent: z.boolean().optional().default(false), content: z.string().optional(), }), - { type: 'json' }, ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const commitsScope = new CommitsRepository(ctx.project.workspaceId) const commit = await commitsScope - .getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id }) + .getCommitByUuid({ + uuid: parsedInput.commitUuid, + projectId: ctx.project.id, + }) .then((r) => r.unwrap()) const result = await createNewDocument({ workspace: ctx.workspace, user: ctx.user, commit, - path: input.path, - content: input.content, - agent: input.agent, + path: parsedInput.path, + content: parsedInput.content, + agent: parsedInput.agent, createDemoEvaluation: true, }) diff --git a/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts b/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts index f985dd51d7..4b4125d683 100644 --- a/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts +++ b/apps/web/src/actions/documents/destroyDocumentAction/index.test.ts @@ -56,13 +56,13 @@ describe('destroyDocumentAction', async () => { describe('unauthorized', () => { it('fails when the user is not authenticated', async () => { - const [_, error] = await destroyDocumentAction({ + const { serverError } = await destroyDocumentAction({ projectId: project.id, commitUuid: draft.uuid, documentUuid: document.documentUuid, }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -89,21 +89,21 @@ describe('destroyDocumentAction', async () => { }, }) const otherWorkspaceDocument = allDocs[0]! - const [_, error] = await destroyDocumentAction({ + const { serverError } = await destroyDocumentAction({ projectId: otherWorkspaceProject.id, commitUuid: otherCommit.uuid, documentUuid: otherWorkspaceDocument.documentUuid, }) - expect(error?.name).toEqual('NotFoundError') + expect(serverError).toEqual('Project not found') }) it('fails when trying to remove a document from a merged commit', async () => { - const [_, error] = await destroyDocumentAction({ + const { serverError } = await destroyDocumentAction({ projectId: project.id, commitUuid: merged.uuid, documentUuid: document.documentUuid, }) - expect(error?.name).toEqual('BadRequestError') + expect(serverError).toEqual('Cannot modify a merged commit') }) it('creates a soft deleted documents in draft document', async () => { diff --git a/apps/web/src/actions/documents/destroyDocumentAction/index.ts b/apps/web/src/actions/documents/destroyDocumentAction/index.ts index 03c449ffc2..9112fbef58 100644 --- a/apps/web/src/actions/documents/destroyDocumentAction/index.ts +++ b/apps/web/src/actions/documents/destroyDocumentAction/index.ts @@ -1,6 +1,6 @@ 'use server' -import { withProject } from '$/actions/procedures' +import { withProject, withProjectSchema } from '$/actions/procedures' import { CommitsRepository, DocumentVersionsRepository, @@ -9,21 +9,26 @@ import { destroyDocument } from '@latitude-data/core/services/documents/destroyD import { z } from 'zod' export const destroyDocumentAction = withProject - .createServerAction() - .input(z.object({ documentUuid: z.string(), commitUuid: z.string() }), { - type: 'json', - }) - .handler(async ({ input, ctx }) => { + .inputSchema( + withProjectSchema.extend({ + documentUuid: z.string(), + commitUuid: z.string(), + }), + ) + .action(async ({ parsedInput, ctx }) => { const commitsScope = new CommitsRepository(ctx.workspace.id) const commit = await commitsScope - .getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id }) + .getCommitByUuid({ + uuid: parsedInput.commitUuid, + projectId: ctx.project.id, + }) .then((r) => r.unwrap()) const docsScope = new DocumentVersionsRepository(ctx.workspace.id) const document = await docsScope .getDocumentAtCommit({ - commitUuid: input.commitUuid, + commitUuid: parsedInput.commitUuid, projectId: ctx.project.id, - documentUuid: input.documentUuid, + documentUuid: parsedInput.documentUuid, }) .then((r) => r.unwrap()) await destroyDocument({ diff --git a/apps/web/src/actions/documents/destroyFolderAction.ts b/apps/web/src/actions/documents/destroyFolderAction.ts index 52a9283ebf..1bc1c9f658 100644 --- a/apps/web/src/actions/documents/destroyFolderAction.ts +++ b/apps/web/src/actions/documents/destroyFolderAction.ts @@ -4,20 +4,22 @@ import { CommitsRepository } from '@latitude-data/core/repositories' import { destroyFolder } from '@latitude-data/core/services/documents/destroyFolder' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const destroyFolderAction = withProject - .createServerAction() - .input(z.object({ path: z.string(), commitUuid: z.string() }), { - type: 'json', - }) - .handler(async ({ input, ctx }) => { + .inputSchema( + withProjectSchema.extend({ path: z.string(), commitUuid: z.string() }), + ) + .action(async ({ parsedInput, ctx }) => { const commitsScope = new CommitsRepository(ctx.workspace.id) const commit = await commitsScope - .getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id }) + .getCommitByUuid({ + uuid: parsedInput.commitUuid, + projectId: ctx.project.id, + }) .then((r) => r.unwrap()) const result = await destroyFolder({ - path: input.path, + path: parsedInput.path, commit, workspace: ctx.workspace, }) diff --git a/apps/web/src/actions/documents/renamePathsAction.ts b/apps/web/src/actions/documents/renamePathsAction.ts index 3a11e218e1..54bc71a67c 100644 --- a/apps/web/src/actions/documents/renamePathsAction.ts +++ b/apps/web/src/actions/documents/renamePathsAction.ts @@ -4,28 +4,29 @@ import { CommitsRepository } from '@latitude-data/core/repositories' import { renameDocumentPaths } from '@latitude-data/core/services/documents/renameDocumentPaths' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const renameDocumentPathsAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ commitUuid: z.string(), oldPath: z.string(), newPath: z.string(), }), - { type: 'json' }, ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const commitsScope = new CommitsRepository(ctx.project.workspaceId) const commit = await commitsScope - .getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id }) + .getCommitByUuid({ + uuid: parsedInput.commitUuid, + projectId: ctx.project.id, + }) .then((r) => r.unwrap()) const result = await renameDocumentPaths({ commit, - oldPath: input.oldPath, - newPath: input.newPath, + oldPath: parsedInput.oldPath, + newPath: parsedInput.newPath, }) return result.unwrap() diff --git a/apps/web/src/actions/documents/saveLinkedDatasetAction.ts b/apps/web/src/actions/documents/saveLinkedDatasetAction.ts index b29bec80ff..3c53e3625c 100644 --- a/apps/web/src/actions/documents/saveLinkedDatasetAction.ts +++ b/apps/web/src/actions/documents/saveLinkedDatasetAction.ts @@ -4,9 +4,9 @@ import { z } from 'zod' import { ParameterType } from '@latitude-data/constants' import { saveLinkedDataset } from '@latitude-data/core/services/documents/saveLinkedDataset' -import { withDataset } from '../procedures' +import { withDataset, withDatasetSchema } from '../procedures' -const parameterTypeSchema = z.nativeEnum(ParameterType) +const parameterTypeSchema = z.enum(ParameterType) const datasetInputMetadataSchema = z.object({ type: parameterTypeSchema.optional(), filename: z.string().optional(), @@ -19,22 +19,21 @@ const datasetInputSchema = z.object({ }) export const saveLinkedDatasetAction = withDataset - .createServerAction() - .input( - z.object({ + .inputSchema( + withDatasetSchema.extend({ datasetRowId: z.number(), - mappedInputs: z.record(z.string()), - inputs: z.record(datasetInputSchema), + mappedInputs: z.record(z.string(), z.string()), + inputs: z.record(z.string(), datasetInputSchema), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { return await saveLinkedDataset({ document: ctx.document, dataset: ctx.dataset, data: { - datasetRowId: input.datasetRowId, - mappedInputs: input.mappedInputs, - inputs: input.inputs, + datasetRowId: parsedInput.datasetRowId, + mappedInputs: parsedInput.mappedInputs, + inputs: parsedInput.inputs, }, }).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/documents/sharing/createPublishedDocumentAction.ts b/apps/web/src/actions/documents/sharing/createPublishedDocumentAction.ts index 185d66d791..f5e7f0fb0a 100644 --- a/apps/web/src/actions/documents/sharing/createPublishedDocumentAction.ts +++ b/apps/web/src/actions/documents/sharing/createPublishedDocumentAction.ts @@ -4,13 +4,13 @@ import { createPublishedDocument } from '@latitude-data/core/services/publishedD import { withDocument } from '../../procedures' -export const createPublishedDocumentAction = withDocument - .createServerAction() - .handler(async ({ ctx }) => { +export const createPublishedDocumentAction = withDocument.action( + async ({ ctx }) => { return createPublishedDocument({ workspace: ctx.workspace, project: ctx.project, document: ctx.document, commitUuid: ctx.currentCommitUuid, }).then((r) => r.unwrap()) - }) + }, +) diff --git a/apps/web/src/actions/documents/sharing/forkDocumentAction.ts b/apps/web/src/actions/documents/sharing/forkDocumentAction.ts index b64119b3d5..ba1d683d85 100644 --- a/apps/web/src/actions/documents/sharing/forkDocumentAction.ts +++ b/apps/web/src/actions/documents/sharing/forkDocumentAction.ts @@ -7,11 +7,10 @@ import { z } from 'zod' import { authProcedure } from '../../procedures' export const forkDocumentAction = authProcedure - .createServerAction() - .input(z.object({ publishedDocumentUuid: z.string() })) - .handler(async ({ ctx, input }) => { + .inputSchema(z.object({ publishedDocumentUuid: z.string() })) + .action(async ({ ctx, parsedInput }) => { const { workspace, commit, document, shared } = await findSharedDocument({ - publishedDocumentUuid: input.publishedDocumentUuid, + publishedDocumentUuid: parsedInput.publishedDocumentUuid, }).then((r) => r.unwrap()) return forkDocument({ diff --git a/apps/web/src/actions/documents/sharing/publishDocumentAction.test.ts b/apps/web/src/actions/documents/sharing/publishDocumentAction.test.ts index db42d95574..d82860628d 100644 --- a/apps/web/src/actions/documents/sharing/publishDocumentAction.test.ts +++ b/apps/web/src/actions/documents/sharing/publishDocumentAction.test.ts @@ -50,13 +50,12 @@ describe('publishDocumentAction', () => { describe('unauthorized', () => { it('errors when the user is not authenticated', async () => { - const [_, error] = await publishDocumentAction({ + const { serverError } = await publishDocumentAction({ projectId: project.id, commitUuid: commit.uuid, documentUuid: document.documentUuid, }) - - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -69,17 +68,17 @@ describe('publishDocumentAction', () => { }) it('returns error when project is not found', async () => { - const [_, error] = await publishDocumentAction({ + const { serverError } = await publishDocumentAction({ projectId: 999992, commitUuid: commit.uuid, documentUuid: document.documentUuid, }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Project not found') }) it('creates a new published document when document has not been published before', async () => { - const [data] = await publishDocumentAction({ + const { data } = await publishDocumentAction({ projectId: project.id, commitUuid: commit.uuid, documentUuid: document.documentUuid, @@ -105,7 +104,7 @@ describe('publishDocumentAction', () => { expect(publishedDocs.length).toBe(1) // Update the published document - const [data] = await publishDocumentAction({ + const { data } = await publishDocumentAction({ projectId: project.id, commitUuid: commit.uuid, documentUuid: document.documentUuid, @@ -131,13 +130,13 @@ describe('publishDocumentAction', () => { }, }) - const [_, error] = await publishDocumentAction({ + const { serverError } = await publishDocumentAction({ projectId: project.id, commitUuid: commit.uuid, documentUuid: otherDocs[0]!.documentUuid, }) - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Document not found') }) }) }) diff --git a/apps/web/src/actions/documents/sharing/publishDocumentAction.ts b/apps/web/src/actions/documents/sharing/publishDocumentAction.ts index aca5987d71..06971831f7 100644 --- a/apps/web/src/actions/documents/sharing/publishDocumentAction.ts +++ b/apps/web/src/actions/documents/sharing/publishDocumentAction.ts @@ -1,13 +1,13 @@ 'use server' -import { withDocument } from '$/actions/procedures' +import { withDocument, withDocumentSchema } from '$/actions/procedures' import { PublishedDocumentRepository } from '@latitude-data/core/repositories/publishedDocumentsRepository' import { createPublishedDocument } from '@latitude-data/core/services/publishedDocuments/create' import { updatePublishedDocument } from '@latitude-data/core/services/publishedDocuments/update' export const publishDocumentAction = withDocument - .createServerAction() - .handler(async ({ ctx }) => { + .inputSchema(withDocumentSchema.extend({})) + .action(async ({ ctx }) => { const scope = new PublishedDocumentRepository(ctx.workspace.id) const rows = await scope.findByProject(Number(ctx.project.id)) const publishedDocument = rows.find( diff --git a/apps/web/src/actions/documents/sharing/updatePublishedDocumentAction.ts b/apps/web/src/actions/documents/sharing/updatePublishedDocumentAction.ts index c16181fa8d..636560ba9f 100644 --- a/apps/web/src/actions/documents/sharing/updatePublishedDocumentAction.ts +++ b/apps/web/src/actions/documents/sharing/updatePublishedDocumentAction.ts @@ -4,7 +4,7 @@ import { PublishedDocumentRepository } from '@latitude-data/core/repositories/pu import { updatePublishedDocument } from '@latitude-data/core/services/publishedDocuments/update' import { z } from 'zod' -import { withDocument } from '../../procedures' +import { withDocument, withDocumentSchema } from '../../procedures' const input = z.object({ uuid: z.string(), @@ -17,12 +17,11 @@ const input = z.object({ export type UpdatePublishedDocumentInput = z.infer export const updatePublishedDocumentAction = withDocument - .createServerAction() - .input(input) - .handler(async ({ ctx, input }) => { + .inputSchema(withDocumentSchema.extend(input.shape)) + .action(async ({ ctx, parsedInput }) => { const repo = new PublishedDocumentRepository(ctx.workspace.id) const publishedDocument = await repo - .findByUuid(input.uuid) + .findByUuid(parsedInput.uuid) .then((r) => r.unwrap()) return updatePublishedDocument({ publishedDocument, diff --git a/apps/web/src/actions/documents/triggers/createDocumentTriggerAction.ts b/apps/web/src/actions/documents/triggers/createDocumentTriggerAction.ts index 146dc874bb..3c9bc3b10c 100644 --- a/apps/web/src/actions/documents/triggers/createDocumentTriggerAction.ts +++ b/apps/web/src/actions/documents/triggers/createDocumentTriggerAction.ts @@ -2,23 +2,22 @@ import { createDocumentTrigger } from '@latitude-data/core/services/documentTriggers/create' -import { withCommit } from '../../procedures' +import { withCommit, withCommitSchema } from '../../procedures' import { DocumentTriggerType } from '@latitude-data/constants' import { z } from 'zod' import { documentTriggerConfigurationSchema } from '@latitude-data/constants/documentTriggers' import { DocumentVersionsRepository } from '@latitude-data/core/repositories' export const createDocumentTriggerAction = withCommit - .createServerAction() - .input( - z.object({ + .inputSchema( + withCommitSchema.extend({ documentUuid: z.string(), - triggerType: z.nativeEnum(DocumentTriggerType), + triggerType: z.enum(DocumentTriggerType), configuration: documentTriggerConfigurationSchema, }), ) - .handler(async ({ input, ctx }) => { - const { documentUuid, triggerType, configuration } = input + .action(async ({ parsedInput, ctx }) => { + const { documentUuid, triggerType, configuration } = parsedInput const { workspace, project, commit } = ctx const documentsScope = new DocumentVersionsRepository(workspace.id) diff --git a/apps/web/src/actions/documents/triggers/deleteDocumentTriggerAction.ts b/apps/web/src/actions/documents/triggers/deleteDocumentTriggerAction.ts index 4f56029418..33e12fe60c 100644 --- a/apps/web/src/actions/documents/triggers/deleteDocumentTriggerAction.ts +++ b/apps/web/src/actions/documents/triggers/deleteDocumentTriggerAction.ts @@ -2,18 +2,13 @@ import { deleteDocumentTrigger } from '@latitude-data/core/services/documentTriggers/delete' -import { withCommit } from '$/actions/procedures' +import { withCommit, withCommitSchema } from '$/actions/procedures' import { z } from 'zod' export const deleteDocumentTriggerAction = withCommit - .createServerAction() - .input( - z.object({ - documentTriggerUuid: z.string(), - }), - ) - .handler(async ({ input, ctx }) => { - const { documentTriggerUuid } = input + .inputSchema(withCommitSchema.extend({ documentTriggerUuid: z.string() })) + .action(async ({ parsedInput, ctx }) => { + const { documentTriggerUuid } = parsedInput const { workspace, commit } = ctx return deleteDocumentTrigger({ diff --git a/apps/web/src/actions/documents/triggers/toggleEnabledDocumentTriggerAction.ts b/apps/web/src/actions/documents/triggers/toggleEnabledDocumentTriggerAction.ts index 3639b73bfb..ebc71860f7 100644 --- a/apps/web/src/actions/documents/triggers/toggleEnabledDocumentTriggerAction.ts +++ b/apps/web/src/actions/documents/triggers/toggleEnabledDocumentTriggerAction.ts @@ -2,19 +2,15 @@ import { setDocumentTriggerEnabled } from '@latitude-data/core/services/documentTriggers/enable' -import { withCommit } from '../../procedures' +import { withCommit, withCommitSchema } from '../../procedures' import { z } from 'zod' export const toggleEnabledDocumentTriggerAction = withCommit - .createServerAction() - .input( - z.object({ - triggerUuid: z.string(), - enabled: z.boolean(), - }), + .inputSchema( + withCommitSchema.extend({ triggerUuid: z.string(), enabled: z.boolean() }), ) - .handler(async ({ input, ctx }) => { - const { triggerUuid, enabled } = input + .action(async ({ parsedInput, ctx }) => { + const { triggerUuid, enabled } = parsedInput const { workspace, commit } = ctx return setDocumentTriggerEnabled({ diff --git a/apps/web/src/actions/documents/triggers/updateDocumentTriggerConfigurationAction.ts b/apps/web/src/actions/documents/triggers/updateDocumentTriggerConfigurationAction.ts index c2c4885854..765624700f 100644 --- a/apps/web/src/actions/documents/triggers/updateDocumentTriggerConfigurationAction.ts +++ b/apps/web/src/actions/documents/triggers/updateDocumentTriggerConfigurationAction.ts @@ -4,26 +4,25 @@ import { updateDocumentTriggerConfiguration } from '@latitude-data/core/services import { documentTriggerConfigurationSchema } from '@latitude-data/constants/documentTriggers' import { z } from 'zod' -import { withCommit } from '$/actions/procedures' +import { withCommit, withCommitSchema } from '$/actions/procedures' export const updateDocumentTriggerConfigurationAction = withCommit - .createServerAction() - .input( - z.object({ + .inputSchema( + withCommitSchema.extend({ documentTriggerUuid: z.string(), documentUuid: z.string().optional(), configuration: documentTriggerConfigurationSchema, }), ) - .handler(async ({ input, ctx }) => { - const { documentTriggerUuid, configuration } = input + .action(async ({ parsedInput, ctx }) => { + const { documentTriggerUuid, configuration } = parsedInput const { workspace, commit } = ctx return updateDocumentTriggerConfiguration({ workspace, commit, triggerUuid: documentTriggerUuid, - documentUuid: input.documentUuid, + documentUuid: parsedInput.documentUuid, configuration, }).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/documents/updateContent.test.ts b/apps/web/src/actions/documents/updateContent.test.ts index 1349df2e95..d0c6db332a 100644 --- a/apps/web/src/actions/documents/updateContent.test.ts +++ b/apps/web/src/actions/documents/updateContent.test.ts @@ -43,14 +43,14 @@ describe('updateDocumentAction', async () => { it('errors when the user is not authenticated', async () => { const commit = await findCommitById(doc1.commitId) - const [_, error] = await updateDocumentContentAction({ + const { serverError } = await updateDocumentContentAction({ projectId, documentUuid: doc1.documentUuid, commitUuid: commit!.uuid, content: 'foo2', }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -91,14 +91,16 @@ describe('updateDocumentAction', async () => { content: 'foo2', }) - const [data, error] = await updateDocumentContentAction({ - projectId: project.id, - documentUuid: doc1.documentUuid, - commitUuid: draft.uuid, - content: 'foo3', - }) + const { data, serverError, validationErrors } = + await updateDocumentContentAction({ + projectId: project.id, + documentUuid: doc1.documentUuid, + commitUuid: draft.uuid, + content: 'foo3', + }) - expect(error).toBeNull() + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toMatchObject({ documentUuid: doc1.documentUuid, commitId: draft.id, @@ -109,14 +111,16 @@ describe('updateDocumentAction', async () => { it('creates a new document version when it does not exist in the draft', async () => { const { commit: draft } = await factories.createDraft({ project, user }) - const [data, error] = await updateDocumentContentAction({ - projectId: project.id, - documentUuid: doc1.documentUuid, - commitUuid: draft.uuid, - content: 'foo2', - }) + const { data, serverError, validationErrors } = + await updateDocumentContentAction({ + projectId: project.id, + documentUuid: doc1.documentUuid, + commitUuid: draft.uuid, + content: 'foo2', + }) - expect(error).toBeNull() + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toMatchObject({ documentUuid: doc1.documentUuid, commitId: draft.id, diff --git a/apps/web/src/actions/documents/updateContent.ts b/apps/web/src/actions/documents/updateContent.ts index c97733be03..cec188dc65 100644 --- a/apps/web/src/actions/documents/updateContent.ts +++ b/apps/web/src/actions/documents/updateContent.ts @@ -8,36 +8,37 @@ import { import { updateDocument } from '@latitude-data/core/services/documents/update' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const updateDocumentContentAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ documentUuid: z.string(), commitUuid: z.string(), content: z.string(), }), - { type: 'json' }, ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const commitsScope = new CommitsRepository(ctx.project.workspaceId) const commit = await commitsScope - .getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id }) + .getCommitByUuid({ + uuid: parsedInput.commitUuid, + projectId: ctx.project.id, + }) .then((r) => r.unwrap()) const docsScope = new DocumentVersionsRepository(ctx.project.workspaceId) const document = await docsScope .getDocumentAtCommit({ - commitUuid: input.commitUuid, + commitUuid: parsedInput.commitUuid, projectId: ctx.project.id, - documentUuid: input.documentUuid, + documentUuid: parsedInput.documentUuid, }) .then((r) => r.unwrap()) const result = await updateDocument({ commit, document, - content: input.content, + content: parsedInput.content, }) const updatedDocument = result.unwrap() diff --git a/apps/web/src/actions/documents/upload.ts b/apps/web/src/actions/documents/upload.ts index 60fffa75c0..facca1ba16 100644 --- a/apps/web/src/actions/documents/upload.ts +++ b/apps/web/src/actions/documents/upload.ts @@ -9,12 +9,11 @@ import { import { convertFile } from '@latitude-data/core/services/files/convert' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const uploadDocumentAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ path: z.string(), commitUuid: z.string(), file: z.instanceof(File).refine(async (file) => { @@ -22,13 +21,16 @@ export const uploadDocumentAction = withProject }, `Your file must be less than ${MAX_SIZE}MB in size. You can split it into smaller files and upload them separately.`), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const commitsScope = new CommitsRepository(ctx.project.workspaceId) const commit = await commitsScope - .getCommitByUuid({ uuid: input.commitUuid, projectId: ctx.project.id }) + .getCommitByUuid({ + uuid: parsedInput.commitUuid, + projectId: ctx.project.id, + }) .then((r) => r.unwrap()) - const content = await convertFile(input.file).then((r) => r.unwrap()) + const content = await convertFile(parsedInput.file).then((r) => r.unwrap()) const { metadata } = await defaultDocumentContent({ workspace: ctx.workspace, }) @@ -37,7 +39,7 @@ export const uploadDocumentAction = withProject workspace: ctx.workspace, user: ctx.user, commit: commit, - path: input.path, + path: parsedInput.path, content: metadata + content, }) diff --git a/apps/web/src/actions/evaluationTemplates/create.ts b/apps/web/src/actions/evaluationTemplates/create.ts index fdc328c28d..7da6c1782a 100644 --- a/apps/web/src/actions/evaluationTemplates/create.ts +++ b/apps/web/src/actions/evaluationTemplates/create.ts @@ -1,22 +1,20 @@ 'use server' import { EvaluationResultableType } from '@latitude-data/core/browser' -import { UnauthorizedError } from '@latitude-data/constants/errors' import { createEvaluationTemplate } from '@latitude-data/core/services/evaluationAdvancedTemplates/create' import { z } from 'zod' -import { authProcedure } from '../procedures' +import { withAdmin } from '../procedures' -export const createEvaluationTemplateAction = authProcedure - .createServerAction() - .input( +export const createEvaluationTemplateAction = withAdmin + .inputSchema( z.object({ name: z.string(), description: z.string(), categoryId: z.number().optional().default(1), categoryName: z.string().optional(), configuration: z.object({ - type: z.nativeEnum(EvaluationResultableType), + type: z.enum(EvaluationResultableType), detail: z .object({ range: z.object({ from: z.number(), to: z.number() }) }) .optional(), @@ -24,13 +22,7 @@ export const createEvaluationTemplateAction = authProcedure prompt: z.string(), }), ) - .handler(async ({ ctx, input }) => { - // TODO: move this check to a procedure - if (!ctx.user.admin) - throw new UnauthorizedError( - 'You must be an admin to create an evaluation template', - ) - + .action(async ({ parsedInput }) => { const { name, description, @@ -38,7 +30,7 @@ export const createEvaluationTemplateAction = authProcedure categoryName, configuration, prompt, - } = input + } = parsedInput return await createEvaluationTemplate({ name, diff --git a/apps/web/src/actions/evaluationTemplates/destroy.ts b/apps/web/src/actions/evaluationTemplates/destroy.ts index 341ef3749f..2d8b7866be 100644 --- a/apps/web/src/actions/evaluationTemplates/destroy.ts +++ b/apps/web/src/actions/evaluationTemplates/destroy.ts @@ -1,26 +1,14 @@ 'use server' -import { UnauthorizedError } from '@latitude-data/constants/errors' import { destroyEvaluationTemplate } from '@latitude-data/core/services/evaluationAdvancedTemplates/destroy' import { z } from 'zod' -import { authProcedure } from '../procedures' +import { withAdmin } from '../procedures' -export const destroyEvaluationTemplateAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.number(), - }), - ) - .handler(async ({ ctx, input }) => { - // TODO: move this check to a procedure - if (!ctx.user.admin) - throw new UnauthorizedError( - 'You must be an admin to destroy an evaluation template', - ) - - const { id } = input +export const destroyEvaluationTemplateAction = withAdmin + .inputSchema(z.object({ id: z.number() })) + .action(async ({ parsedInput }) => { + const { id } = parsedInput return await destroyEvaluationTemplate({ id, diff --git a/apps/web/src/actions/evaluationsV2/annotate.ts b/apps/web/src/actions/evaluationsV2/annotate.ts index 50ada96244..76e6fcfe9c 100644 --- a/apps/web/src/actions/evaluationsV2/annotate.ts +++ b/apps/web/src/actions/evaluationsV2/annotate.ts @@ -5,27 +5,26 @@ import { ProviderLogsRepository } from '@latitude-data/core/repositories' import { annotateEvaluationV2 } from '@latitude-data/core/services/evaluationsV2/annotate' import serializeProviderLog from '@latitude-data/core/services/providerLogs/serialize' import { z } from 'zod' -import { withEvaluation } from '../procedures' +import { withEvaluation, withEvaluationSchema } from '../procedures' export const annotateEvaluationV2Action = withEvaluation - .createServerAction() - .input( - z.object({ + .inputSchema( + withEvaluationSchema.extend({ resultScore: z.number(), resultMetadata: z.custom>().optional(), providerLogUuid: z.string(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const providerLogsRepository = new ProviderLogsRepository(ctx.workspace.id) const providerLog = await providerLogsRepository - .findByUuid(input.providerLogUuid) + .findByUuid(parsedInput.providerLogUuid) .then((r) => r.unwrap()) .then((r) => serializeProviderLog(r)) const result = await annotateEvaluationV2({ - resultScore: input.resultScore, - resultMetadata: input.resultMetadata, + resultScore: parsedInput.resultScore, + resultMetadata: parsedInput.resultMetadata, evaluation: ctx.evaluation, providerLog: providerLog, commit: ctx.commit, diff --git a/apps/web/src/actions/evaluationsV2/clone.ts b/apps/web/src/actions/evaluationsV2/clone.ts index a7bae73a32..d51e9fb7c1 100644 --- a/apps/web/src/actions/evaluationsV2/clone.ts +++ b/apps/web/src/actions/evaluationsV2/clone.ts @@ -1,11 +1,11 @@ 'use server' import { cloneEvaluationV2 } from '@latitude-data/core/services/evaluationsV2/clone' -import { withEvaluation } from '../procedures' +import { withEvaluation, withEvaluationSchema } from '../procedures' export const cloneEvaluationV2Action = withEvaluation - .createServerAction() - .handler(async ({ ctx }) => { + .inputSchema(withEvaluationSchema.extend({})) + .action(async ({ ctx }) => { const result = await cloneEvaluationV2({ evaluation: ctx.evaluation, commit: ctx.commit, diff --git a/apps/web/src/actions/evaluationsV2/create.ts b/apps/web/src/actions/evaluationsV2/create.ts index a528cde57f..420583dda3 100644 --- a/apps/web/src/actions/evaluationsV2/create.ts +++ b/apps/web/src/actions/evaluationsV2/create.ts @@ -1,29 +1,33 @@ 'use server' +import { z } from 'zod' +import { returnValidationErrors } from 'next-safe-action' import { EvaluationOptionsSchema, EvaluationSettingsSchema, } from '@latitude-data/core/browser' import { createEvaluationV2 } from '@latitude-data/core/services/evaluationsV2/create' -import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' +const evaluationSchema = withDocumentSchema.extend({ + settings: EvaluationSettingsSchema, + options: EvaluationOptionsSchema.partial().optional(), +}) export const createEvaluationV2Action = withDocument - .createServerAction() - .input( - z.object({ - settings: EvaluationSettingsSchema, - options: EvaluationOptionsSchema.partial().optional(), - }), - ) - .handler(async ({ ctx, input }) => { + .inputSchema(evaluationSchema) + .action(async ({ ctx, parsedInput }) => { const result = await createEvaluationV2({ document: ctx.document, commit: ctx.commit, - settings: input.settings, - options: input.options, + settings: parsedInput.settings, + options: parsedInput.options, workspace: ctx.workspace, - }).then((r) => r.unwrap()) + }) + + const error = result.error + if (error && error instanceof z.ZodError) { + return returnValidationErrors(evaluationSchema, error.format()) + } - return result + return result.unwrap() }) diff --git a/apps/web/src/actions/evaluationsV2/delete.ts b/apps/web/src/actions/evaluationsV2/delete.ts index 63b5dae98d..51fd1acd9a 100644 --- a/apps/web/src/actions/evaluationsV2/delete.ts +++ b/apps/web/src/actions/evaluationsV2/delete.ts @@ -1,13 +1,11 @@ 'use server' import { deleteEvaluationV2 } from '@latitude-data/core/services/evaluationsV2/delete' -import { z } from 'zod' -import { withEvaluation } from '../procedures' +import { withEvaluation, withEvaluationSchema } from '../procedures' export const deleteEvaluationV2Action = withEvaluation - .createServerAction() - .input(z.object({})) - .handler(async ({ ctx }) => { + .inputSchema(withEvaluationSchema.extend({})) + .action(async ({ ctx }) => { const result = await deleteEvaluationV2({ evaluation: ctx.evaluation, commit: ctx.commit, diff --git a/apps/web/src/actions/evaluationsV2/generate.ts b/apps/web/src/actions/evaluationsV2/generate.ts index 77ccd943da..9886b308b8 100644 --- a/apps/web/src/actions/evaluationsV2/generate.ts +++ b/apps/web/src/actions/evaluationsV2/generate.ts @@ -2,18 +2,15 @@ import { generateEvaluationV2 } from '@latitude-data/core/services/evaluationsV2/generate' import { z } from 'zod' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' export const generateEvaluationV2Action = withDocument - .createServerAction() - .input( - z.object({ - instructions: z.string().optional(), - }), + .inputSchema( + withDocumentSchema.extend({ instructions: z.string().optional() }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const result = await generateEvaluationV2({ - instructions: input.instructions, + instructions: parsedInput.instructions, document: ctx.document, commit: ctx.commit, workspace: ctx.workspace, diff --git a/apps/web/src/actions/evaluationsV2/update.ts b/apps/web/src/actions/evaluationsV2/update.ts index 87f5aaf4b3..2074fe6fe6 100644 --- a/apps/web/src/actions/evaluationsV2/update.ts +++ b/apps/web/src/actions/evaluationsV2/update.ts @@ -1,31 +1,36 @@ 'use server' +import { z } from 'zod' +import { returnValidationErrors } from 'next-safe-action' import { EvaluationOptionsSchema, EvaluationSettingsSchema, } from '@latitude-data/core/browser' import { updateEvaluationV2 } from '@latitude-data/core/services/evaluationsV2/update' -import { z } from 'zod' -import { withEvaluation } from '../procedures' +import { withEvaluation, withEvaluationSchema } from '../procedures' +const evaluationSchema = withEvaluationSchema.extend({ + settings: EvaluationSettingsSchema.omit({ type: true, metric: true }) + .partial() + .optional(), + options: EvaluationOptionsSchema.partial().optional(), +}) export const updateEvaluationV2Action = withEvaluation - .createServerAction() - .input( - z.object({ - settings: EvaluationSettingsSchema.omit({ type: true, metric: true }) - .partial() - .optional(), - options: EvaluationOptionsSchema.partial().optional(), - }), - ) - .handler(async ({ ctx, input }) => { + .inputSchema(evaluationSchema) + .action(async ({ ctx, parsedInput }) => { const result = await updateEvaluationV2({ evaluation: ctx.evaluation, commit: ctx.commit, - settings: input.settings, - options: input.options, + settings: parsedInput.settings, + options: parsedInput.options, workspace: ctx.workspace, - }).then((r) => r.unwrap()) + }) + + const error = result.error + + if (error && error instanceof z.ZodError) { + return returnValidationErrors(evaluationSchema, error.format()) + } - return result + return result.unwrap() }) diff --git a/apps/web/src/actions/events/publishEventAction.ts b/apps/web/src/actions/events/publishEventAction.ts index a697be4c8f..46a6be0ee2 100644 --- a/apps/web/src/actions/events/publishEventAction.ts +++ b/apps/web/src/actions/events/publishEventAction.ts @@ -6,15 +6,14 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const publishEventAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ eventType: z.string(), - payload: z.record(z.any()).optional(), + payload: z.record(z.string(), z.any()).optional(), }), ) - .handler(async ({ ctx, input }) => { - const { eventType, payload } = input + .action(async ({ ctx, parsedInput }) => { + const { eventType, payload } = parsedInput const data = { userEmail: ctx.user.email, workspaceId: ctx.workspace.id, diff --git a/apps/web/src/actions/experiments/create.ts b/apps/web/src/actions/experiments/create.ts index d091697f24..6bc641147a 100644 --- a/apps/web/src/actions/experiments/create.ts +++ b/apps/web/src/actions/experiments/create.ts @@ -1,6 +1,6 @@ 'use server' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' import { createExperimentVariants } from '@latitude-data/core/services/experiments/createVariants' import { startExperiment } from '@latitude-data/core/services/experiments/start/index' import { @@ -11,9 +11,8 @@ import { experimentVariantSchema } from '@latitude-data/constants/experiments' import { z } from 'zod' export const createExperimentAction = withDocument - .createServerAction() - .input( - z.object({ + .inputSchema( + withDocumentSchema.extend({ variants: z.array(experimentVariantSchema), evaluationUuids: z.array(z.string()), datasetId: z.number().optional(), @@ -23,7 +22,7 @@ export const createExperimentAction = withDocument toRow: z.number().optional(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { variants, evaluationUuids, @@ -32,7 +31,7 @@ export const createExperimentAction = withDocument datasetLabels, fromRow, toRow, - } = input + } = parsedInput const datasetsScope = new DatasetsRepository(ctx.workspace.id) const dataset = datasetId ? await datasetsScope.find(datasetId).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/experiments/stop.ts b/apps/web/src/actions/experiments/stop.ts index 094d903c9f..9c41f099ac 100644 --- a/apps/web/src/actions/experiments/stop.ts +++ b/apps/web/src/actions/experiments/stop.ts @@ -1,19 +1,14 @@ 'use server' -import { withDocument } from '../procedures' +import { withDocument, withDocumentSchema } from '../procedures' import { ExperimentsRepository } from '@latitude-data/core/repositories' import { z } from 'zod' import { completeExperiment } from '@latitude-data/core/services/experiments/complete' export const stopExperimentAction = withDocument - .createServerAction() - .input( - z.object({ - experimentUuid: z.string(), - }), - ) - .handler(async ({ ctx, input }) => { - const { experimentUuid } = input + .inputSchema(withDocumentSchema.extend({ experimentUuid: z.string() })) + .action(async ({ ctx, parsedInput }) => { + const { experimentUuid } = parsedInput const experimentsScope = new ExperimentsRepository(ctx.workspace.id) const experiment = await experimentsScope .findByUuid(experimentUuid) diff --git a/apps/web/src/actions/files/convert.ts b/apps/web/src/actions/files/convert.ts index e5bd5355b4..9977cb6557 100644 --- a/apps/web/src/actions/files/convert.ts +++ b/apps/web/src/actions/files/convert.ts @@ -7,16 +7,15 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const convertFileAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ file: z.instanceof(File).refine(async (file) => { return file?.size <= MAX_UPLOAD_SIZE_IN_MB }, `Your file must be less than ${MAX_SIZE}MB in size. You can split it into smaller files and upload them separately.`), }), ) - .handler(async ({ input }) => { - const result = await convertFile(input.file) + .action(async ({ parsedInput }) => { + const result = await convertFile(parsedInput.file) return result.unwrap() }) diff --git a/apps/web/src/actions/files/upload.ts b/apps/web/src/actions/files/upload.ts index 8b919a3026..4f7171fc37 100644 --- a/apps/web/src/actions/files/upload.ts +++ b/apps/web/src/actions/files/upload.ts @@ -9,28 +9,24 @@ import { z } from 'zod' import { maybeAuthProcedure, withRateLimit } from '../procedures' -export const uploadFileAction = ( - await withRateLimit(maybeAuthProcedure, { - limit: 10, - period: 60, - }) -) - .createServerAction() - .input( +export const uploadFileAction = maybeAuthProcedure + .use(withRateLimit({ limit: 10, period: 60 })) + .inputSchema( z.object({ file: z.instanceof(File).refine(async (file) => { return file?.size <= MAX_UPLOAD_SIZE_IN_MB }, `Your file must be less than ${MAX_SIZE}MB in size. You can split it into smaller files and upload them separately.`), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const ip = getUnsafeIp(await headers()) || 'unknown' const fingerprint = createHash('sha1').update(ip).digest('hex') + const workspace = ctx.workspace === null ? undefined : ctx.workspace const result = await uploadFile({ - file: input.file, + file: parsedInput.file, prefix: ctx.workspace ? undefined : fingerprint, - workspace: ctx.workspace, + workspace, }) return result.unwrap() diff --git a/apps/web/src/actions/history/resetCommitVersion/getChangesToResetCommitAction.ts b/apps/web/src/actions/history/resetCommitVersion/getChangesToResetCommitAction.ts index 9e7b434a36..609da5ddd7 100644 --- a/apps/web/src/actions/history/resetCommitVersion/getChangesToResetCommitAction.ts +++ b/apps/web/src/actions/history/resetCommitVersion/getChangesToResetCommitAction.ts @@ -2,20 +2,19 @@ import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { getChangesToResetProjectToCommit } from '@latitude-data/core/services/history/resetProjectToCommit' export const getChangesToResetCommitAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), commitUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { workspace, project } = ctx - const { targetDraftUuid, commitUuid } = input + const { targetDraftUuid, commitUuid } = parsedInput const changes = await getChangesToResetProjectToCommit({ workspace, diff --git a/apps/web/src/actions/history/resetCommitVersion/resetCommitVersionAction.ts b/apps/web/src/actions/history/resetCommitVersion/resetCommitVersionAction.ts index 373d3a65be..8d4bb43280 100644 --- a/apps/web/src/actions/history/resetCommitVersion/resetCommitVersionAction.ts +++ b/apps/web/src/actions/history/resetCommitVersion/resetCommitVersionAction.ts @@ -2,20 +2,19 @@ import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { resetProjectToCommit } from '@latitude-data/core/services/history/resetProjectToCommit' export const resetCommitVersionAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), commitUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { user, workspace, project } = ctx - const { targetDraftUuid, commitUuid } = input + const { targetDraftUuid, commitUuid } = parsedInput const draft = await resetProjectToCommit({ workspace, diff --git a/apps/web/src/actions/history/resetDocumentVersion/getChangesToResetDocumentAction.ts b/apps/web/src/actions/history/resetDocumentVersion/getChangesToResetDocumentAction.ts index a5319c738b..dd46a9e3d8 100644 --- a/apps/web/src/actions/history/resetDocumentVersion/getChangesToResetDocumentAction.ts +++ b/apps/web/src/actions/history/resetDocumentVersion/getChangesToResetDocumentAction.ts @@ -2,21 +2,20 @@ import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { getChangesToResetDocumentToVersion } from '@latitude-data/core/services/history/resetDocumentToVersion' export const getChangesToResetDocumentAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), documentCommitUuid: z.string(), documentUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { workspace, project } = ctx - const { targetDraftUuid, documentCommitUuid, documentUuid } = input + const { targetDraftUuid, documentCommitUuid, documentUuid } = parsedInput const change = await getChangesToResetDocumentToVersion({ workspace, diff --git a/apps/web/src/actions/history/resetDocumentVersion/resetDocumentVersionAction.ts b/apps/web/src/actions/history/resetDocumentVersion/resetDocumentVersionAction.ts index 55f97b072e..15f94b9809 100644 --- a/apps/web/src/actions/history/resetDocumentVersion/resetDocumentVersionAction.ts +++ b/apps/web/src/actions/history/resetDocumentVersion/resetDocumentVersionAction.ts @@ -2,21 +2,20 @@ import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { resetDocumentToVersion } from '@latitude-data/core/services/history/resetDocumentToVersion' export const resetDocumentVersionAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), documentCommitUuid: z.string(), documentUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { user, workspace, project } = ctx - const { targetDraftUuid, documentCommitUuid, documentUuid } = input + const { targetDraftUuid, documentCommitUuid, documentUuid } = parsedInput const result = await resetDocumentToVersion({ user, diff --git a/apps/web/src/actions/history/revertCommitVersion/getChangesToRevertCommitAction.ts b/apps/web/src/actions/history/revertCommitVersion/getChangesToRevertCommitAction.ts index 6590d3e74c..d92602921b 100644 --- a/apps/web/src/actions/history/revertCommitVersion/getChangesToRevertCommitAction.ts +++ b/apps/web/src/actions/history/revertCommitVersion/getChangesToRevertCommitAction.ts @@ -2,21 +2,20 @@ import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { getChangesToRevertCommit } from '@latitude-data/core/services/history/revertCommit' export const getChangesToRevertCommitAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ commitUuid: z.string(), targetDraftUuid: z.string().optional(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { workspace, project } = ctx - const { targetDraftUuid, commitUuid } = input + const { targetDraftUuid, commitUuid } = parsedInput const changes = await getChangesToRevertCommit({ workspace, diff --git a/apps/web/src/actions/history/revertCommitVersion/revertCommitAction.ts b/apps/web/src/actions/history/revertCommitVersion/revertCommitAction.ts index 8d8fd5f6a6..dde2b0dba1 100644 --- a/apps/web/src/actions/history/revertCommitVersion/revertCommitAction.ts +++ b/apps/web/src/actions/history/revertCommitVersion/revertCommitAction.ts @@ -2,20 +2,19 @@ import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { revertCommit } from '@latitude-data/core/services/history/revertCommit' export const revertCommitChangesAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), commitUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { user, workspace, project } = ctx - const { targetDraftUuid, commitUuid } = input + const { targetDraftUuid, commitUuid } = parsedInput const draft = await revertCommit({ workspace, diff --git a/apps/web/src/actions/history/revertDocumentVersion/getChangesToRevertDocumentAction.ts b/apps/web/src/actions/history/revertDocumentVersion/getChangesToRevertDocumentAction.ts index ee3bf6cf59..776d416950 100644 --- a/apps/web/src/actions/history/revertDocumentVersion/getChangesToRevertDocumentAction.ts +++ b/apps/web/src/actions/history/revertDocumentVersion/getChangesToRevertDocumentAction.ts @@ -1,21 +1,20 @@ 'use server' import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { getChangesToRevertDocumentChanges } from '@latitude-data/core/services/history/revertDocumentVersion' export const getChangesToRevertDocumentAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), documentCommitUuid: z.string(), documentUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { workspace, project } = ctx - const { targetDraftUuid, documentCommitUuid, documentUuid } = input + const { targetDraftUuid, documentCommitUuid, documentUuid } = parsedInput const change = await getChangesToRevertDocumentChanges({ workspace, diff --git a/apps/web/src/actions/history/revertDocumentVersion/revertDocumentAction.ts b/apps/web/src/actions/history/revertDocumentVersion/revertDocumentAction.ts index 2896d82cef..96f4f3a56f 100644 --- a/apps/web/src/actions/history/revertDocumentVersion/revertDocumentAction.ts +++ b/apps/web/src/actions/history/revertDocumentVersion/revertDocumentAction.ts @@ -1,21 +1,20 @@ 'use server' import { z } from 'zod' -import { withProject } from '../../procedures' +import { withProject, withProjectSchema } from '../../procedures' import { revertChangesToDocument } from '@latitude-data/core/services/history/revertDocumentVersion' export const revertDocumentChangesAction = withProject - .createServerAction() - .input( - z.object({ + .inputSchema( + withProjectSchema.extend({ targetDraftUuid: z.string().optional(), documentCommitUuid: z.string(), documentUuid: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const { user, workspace, project } = ctx - const { targetDraftUuid, documentCommitUuid, documentUuid } = input + const { targetDraftUuid, documentCommitUuid, documentUuid } = parsedInput const result = await revertChangesToDocument({ workspace, diff --git a/apps/web/src/actions/integrations/create.ts b/apps/web/src/actions/integrations/create.ts index 3238159c45..24b8b76d1d 100644 --- a/apps/web/src/actions/integrations/create.ts +++ b/apps/web/src/actions/integrations/create.ts @@ -1,9 +1,9 @@ 'use server' import { createIntegration } from '@latitude-data/core/services/integrations/create' +import { returnValidationErrors } from 'next-safe-action' import { z } from 'zod' -import { authProcedure } from '../procedures' import { IntegrationType } from '@latitude-data/constants' import { externalMcpIntegrationConfigurationSchema, @@ -12,58 +12,64 @@ import { } from '@latitude-data/core/services/integrations/helpers/schema' import { Workspace } from '@latitude-data/core/browser' import { IntegrationsRepository } from '@latitude-data/core/repositories' +import { authProcedure } from '../procedures' + +const nameSchema = z + .string() + .min(1) + .max(255) + .refine((name) => !name.includes(' '), { + error: 'Name cannot contain spaces', + }) + .refine((name) => !name.includes('/'), { + error: 'Name cannot contain slashes', + }) -const nameSchema = (workspace: Workspace) => - z - .string() - .min(1) - .max(255) - .refine((name) => !name.includes(' '), { - message: 'Name cannot contain spaces', - }) - .refine((name) => !name.includes('/'), { - message: 'Name cannot contain slashes', - }) - .refine((name) => name !== 'latitude', { - message: 'An integration with this name already exists', - }) - .refine( - async (name) => { - const integrationsScope = new IntegrationsRepository(workspace.id) - const integration = await integrationsScope.findByName(name) - return !integration.ok - }, - { message: 'An integration with this name already exists' }, - ) +async function validateNameUnique( + name: string, + workspace: Workspace, +): Promise { + const integrationsScope = new IntegrationsRepository(workspace.id) + const integration = await integrationsScope.findByName(name) + return !integration.ok +} + +const integrationSchema = z.discriminatedUnion('type', [ + z.object({ + name: nameSchema, + type: z.literal(IntegrationType.ExternalMCP), + configuration: externalMcpIntegrationConfigurationSchema, + }), + z.object({ + name: nameSchema, + type: z.literal(IntegrationType.HostedMCP), + configuration: hostedMcpIntegrationConfigurationFormSchema, + }), + z.object({ + name: nameSchema, + type: z.literal(IntegrationType.Pipedream), + configuration: pipedreamIntegrationConfigurationSchema, + }), +]) export const createIntegrationAction = authProcedure - .createServerAction() - .input(async ({ ctx }) => - z.discriminatedUnion('type', [ - z.object({ - name: nameSchema(ctx.workspace), - type: z.literal(IntegrationType.ExternalMCP), - configuration: externalMcpIntegrationConfigurationSchema, - }), - z.object({ - name: nameSchema(ctx.workspace), - type: z.literal(IntegrationType.HostedMCP), - configuration: hostedMcpIntegrationConfigurationFormSchema, - }), - z.object({ - name: nameSchema(ctx.workspace), - type: z.literal(IntegrationType.Pipedream), - configuration: pipedreamIntegrationConfigurationSchema, - }), - ]), - ) - .handler( - async ({ input, ctx }) => - await createIntegration({ - workspace: ctx.workspace, - name: input.name, - type: input.type, - configuration: input.configuration, - author: ctx.user, - }).then((r) => r.unwrap()), - ) + .inputSchema(integrationSchema) + .action(async ({ parsedInput, ctx }) => { + const uniqueName = await validateNameUnique(parsedInput.name, ctx.workspace) + + if (!uniqueName) { + return returnValidationErrors(integrationSchema, { + name: { + _errors: ['An integration with this name already exists'], + }, + }) + } + + return await createIntegration({ + workspace: ctx.workspace, + name: parsedInput.name, + type: parsedInput.type, + configuration: parsedInput.configuration, + author: ctx.user, + }).then((r) => r.unwrap()) + }) diff --git a/apps/web/src/actions/integrations/destroy.ts b/apps/web/src/actions/integrations/destroy.ts index 2a69e4e198..9a39c90c66 100644 --- a/apps/web/src/actions/integrations/destroy.ts +++ b/apps/web/src/actions/integrations/destroy.ts @@ -7,16 +7,11 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const destroyIntegrationAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.number(), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.number() })) + .action(async ({ parsedInput, ctx }) => { const integrationsRepo = new IntegrationsRepository(ctx.workspace.id) const integration = await integrationsRepo - .find(input.id) + .find(parsedInput.id) .then((r) => r.unwrap()) return destroyIntegration(integration).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/integrations/inputSchema.ts b/apps/web/src/actions/integrations/inputSchema.ts index 3dbb42a577..65e91bb824 100644 --- a/apps/web/src/actions/integrations/inputSchema.ts +++ b/apps/web/src/actions/integrations/inputSchema.ts @@ -8,7 +8,7 @@ import { z } from 'zod' const baseSchema = z.object({ name: z.string(), - type: z.nativeEnum(IntegrationType), + type: z.enum(IntegrationType), }) const fullCustomMcpConfigurationSchema = z.object({ diff --git a/apps/web/src/actions/integrations/pingCustomMcpServer.ts b/apps/web/src/actions/integrations/pingCustomMcpServer.ts index dd5c745291..bcd081c2e6 100644 --- a/apps/web/src/actions/integrations/pingCustomMcpServer.ts +++ b/apps/web/src/actions/integrations/pingCustomMcpServer.ts @@ -6,13 +6,8 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const pingCustomMcpAction = authProcedure - .createServerAction() - .input( - z.object({ - url: z.string(), - }), - ) - .handler( - async ({ input }) => - await pingCustomMCPServer(input.url).then((r) => r.unwrap()), + .inputSchema(z.object({ url: z.string() })) + .action( + async ({ parsedInput }) => + await pingCustomMCPServer(parsedInput.url).then((r) => r.unwrap()), ) diff --git a/apps/web/src/actions/integrations/pipedream/configureComponent.ts b/apps/web/src/actions/integrations/pipedream/configureComponent.ts index 8cd4b09208..1534b58224 100644 --- a/apps/web/src/actions/integrations/pipedream/configureComponent.ts +++ b/apps/web/src/actions/integrations/pipedream/configureComponent.ts @@ -7,19 +7,18 @@ import { IntegrationType } from '@latitude-data/constants' import { configureComponent } from '@latitude-data/core/services/integrations/pipedream/components/configureComponent' export const configurePipedreamComponentAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ integrationName: z.string(), componentId: z.string(), propName: z.string(), - configuredProps: z.record(z.any()).optional(), + configuredProps: z.record(z.string(), z.any()).optional(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const integrationScope = new IntegrationsRepository(ctx.workspace.id) const integrationResult = await integrationScope.findByName( - input.integrationName, + parsedInput.integrationName, ) const integration = integrationResult.unwrap() @@ -29,8 +28,8 @@ export const configurePipedreamComponentAction = authProcedure return configureComponent({ integration, - componentId: input.componentId, - propName: input.propName, - configuredProps: input.configuredProps, + componentId: parsedInput.componentId, + propName: parsedInput.propName, + configuredProps: parsedInput.configuredProps, }).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/integrations/pipedream/createToken.ts b/apps/web/src/actions/integrations/pipedream/createToken.ts index d5da2cb5e1..e3f9ec2001 100644 --- a/apps/web/src/actions/integrations/pipedream/createToken.ts +++ b/apps/web/src/actions/integrations/pipedream/createToken.ts @@ -5,14 +5,13 @@ import { authProcedure } from '../../procedures' import { createConnectToken } from '@latitude-data/core/services/integrations/pipedream/createConnectToken' export const createPipedreamTokenAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ externalUserId: z.string(), }), ) - .handler(async ({ input, ctx }) => { - const { externalUserId } = input + .action(async ({ ctx, parsedInput }) => { + const { externalUserId } = parsedInput return createConnectToken({ workspace: ctx.workspace, diff --git a/apps/web/src/actions/integrations/pipedream/reloadComponentProps.ts b/apps/web/src/actions/integrations/pipedream/reloadComponentProps.ts index 1e58c3cc33..63b4c421d2 100644 --- a/apps/web/src/actions/integrations/pipedream/reloadComponentProps.ts +++ b/apps/web/src/actions/integrations/pipedream/reloadComponentProps.ts @@ -7,18 +7,17 @@ import { IntegrationsRepository } from '@latitude-data/core/repositories' import { IntegrationType } from '@latitude-data/constants' export const reloadPipedreamComponentPropsAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ integrationName: z.string(), componentId: z.string(), - configuredProps: z.record(z.any()).optional(), + configuredProps: z.record(z.string(), z.any()).optional(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const integrationScope = new IntegrationsRepository(ctx.workspace.id) const integrationResult = await integrationScope.findByName( - input.integrationName, + parsedInput.integrationName, ) const integration = integrationResult.unwrap() @@ -28,7 +27,7 @@ export const reloadPipedreamComponentPropsAction = authProcedure return reloadComponentProps({ integration, - componentId: input.componentId, - configuredProps: input.configuredProps ?? {}, + componentId: parsedInput.componentId, + configuredProps: parsedInput.configuredProps ?? {}, }).then((r) => r.unwrap()) }) diff --git a/apps/web/src/actions/integrations/scaleDown.ts b/apps/web/src/actions/integrations/scaleDown.ts index d3bbd467d0..166713dce4 100644 --- a/apps/web/src/actions/integrations/scaleDown.ts +++ b/apps/web/src/actions/integrations/scaleDown.ts @@ -7,16 +7,11 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const scaleDownMcpServerAction = authProcedure - .createServerAction() - .input( - z.object({ - mcpServerId: z.number(), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ mcpServerId: z.number() })) + .action(async ({ parsedInput, ctx }) => { const mcpServerRepo = new McpServerRepository(ctx.workspace.id) const mcpServer = await mcpServerRepo - .find(input.mcpServerId) + .find(parsedInput.mcpServerId) .then((r) => r.unwrap()) return scaleMcpServer({ diff --git a/apps/web/src/actions/integrations/scaleUp.ts b/apps/web/src/actions/integrations/scaleUp.ts index 3ec816fdc0..955fa56546 100644 --- a/apps/web/src/actions/integrations/scaleUp.ts +++ b/apps/web/src/actions/integrations/scaleUp.ts @@ -7,16 +7,11 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const scaleUpMcpServerAction = authProcedure - .createServerAction() - .input( - z.object({ - mcpServerId: z.number(), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ mcpServerId: z.number() })) + .action(async ({ parsedInput, ctx }) => { const mcpServerRepo = new McpServerRepository(ctx.workspace.id) const mcpServer = await mcpServerRepo - .find(input.mcpServerId) + .find(parsedInput.mcpServerId) .then((r) => r.unwrap()) return scaleMcpServer({ diff --git a/apps/web/src/actions/integrations/updateConfiguration.ts b/apps/web/src/actions/integrations/updateConfiguration.ts index ca6687a91e..21bbd9a5e0 100644 --- a/apps/web/src/actions/integrations/updateConfiguration.ts +++ b/apps/web/src/actions/integrations/updateConfiguration.ts @@ -8,15 +8,14 @@ import { IntegrationsRepository } from '@latitude-data/core/repositories' import { updateIntegrationConfiguration } from '@latitude-data/core/services/integrations/updateConfiguration' export const updateIntegrationConfigurationAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ integrationName: z.string(), configuration: pipedreamIntegrationConfigurationSchema, }), ) - .handler(async ({ input, ctx }) => { - const { integrationName, configuration } = input + .action(async ({ parsedInput, ctx }) => { + const { integrationName, configuration } = parsedInput const { workspace } = ctx const integrationsScope = new IntegrationsRepository(workspace.id) diff --git a/apps/web/src/actions/invitations/accept.ts b/apps/web/src/actions/invitations/accept.ts index 546d138e41..d2699572f5 100644 --- a/apps/web/src/actions/invitations/accept.ts +++ b/apps/web/src/actions/invitations/accept.ts @@ -1,5 +1,6 @@ 'use server' +import { z } from 'zod' import { unsafelyFindMembershipByToken, unsafelyFindWorkspace, @@ -9,20 +10,18 @@ import { NotFoundError } from '@latitude-data/constants/errors' import { acceptInvitation } from '@latitude-data/core/services/invitations/accept' import { setSession } from '$/services/auth/setSession' import { ROUTES } from '$/services/routes' -import { redirect } from 'next/navigation' -import { z } from 'zod' -import { createServerAction } from 'zsa' +import { errorHandlingProcedure } from '$/actions/procedures' +import { frontendRedirect } from '$/lib/frontendRedirect' -export const acceptInvitationAction = createServerAction() - .input( +export const acceptInvitationAction = errorHandlingProcedure + .inputSchema( z.object({ membershipToken: z.string(), email: z.string().optional(), }), - { type: 'formData' }, ) - .handler(async ({ input }) => { - const { membershipToken } = input + .action(async ({ parsedInput }) => { + const { membershipToken } = parsedInput const membership = await unsafelyFindMembershipByToken( membershipToken, ).then((r) => r.unwrap()) @@ -43,5 +42,5 @@ export const acceptInvitationAction = createServerAction() }, }) - return redirect(ROUTES.root) + return frontendRedirect(ROUTES.root) }) diff --git a/apps/web/src/actions/latte/acceptChanges.ts b/apps/web/src/actions/latte/acceptChanges.ts index e51b989f07..105f94b889 100644 --- a/apps/web/src/actions/latte/acceptChanges.ts +++ b/apps/web/src/actions/latte/acceptChanges.ts @@ -6,15 +6,14 @@ import { clearLatteThreadCheckpoints } from '@latitude-data/core/services/copilo import { evaluateLatteThreadChanges } from '@latitude-data/core/services/copilot/latte/threads/evaluateChanges' export const acceptLatteChangesAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ threadUuid: z.string(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace } = ctx - const { threadUuid } = input + const { threadUuid } = parsedInput const checkpoints = await clearLatteThreadCheckpoints({ threadUuid, diff --git a/apps/web/src/actions/latte/addFeedbackToLatteChange.ts b/apps/web/src/actions/latte/addFeedbackToLatteChange.ts index 9f528270d9..fb6b8acad3 100644 --- a/apps/web/src/actions/latte/addFeedbackToLatteChange.ts +++ b/apps/web/src/actions/latte/addFeedbackToLatteChange.ts @@ -5,15 +5,14 @@ import { authProcedure } from '$/actions/procedures' import { addFeedbackToEvaluationResult } from '@latitude-data/core/services/copilot/latte/threads/addFeedbackToEvaluation' export const addFeedbackToLatteChangeAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ evaluationResultUuid: z.string(), content: z.string(), }), ) - .handler(async ({ input }) => { - const { evaluationResultUuid, content } = input + .action(async ({ parsedInput }) => { + const { evaluationResultUuid, content } = parsedInput await addFeedbackToEvaluationResult({ evaluationResultUuid, diff --git a/apps/web/src/actions/latte/addMessage.ts b/apps/web/src/actions/latte/addMessage.ts index 4523b24953..d72580e66c 100644 --- a/apps/web/src/actions/latte/addMessage.ts +++ b/apps/web/src/actions/latte/addMessage.ts @@ -4,26 +4,21 @@ import { NotFoundError } from '@latitude-data/constants/errors' import { LatteThreadsRepository } from '@latitude-data/core/repositories' import { createLatteJob } from '@latitude-data/core/services/copilot/latte/createLatteJob' import { z } from 'zod' -import { withProject, withRateLimit } from '../procedures' +import { withProject, withProjectSchema, withRateLimit } from '../procedures' -export const addMessageToLatteAction = ( - await withRateLimit(withProject, { - limit: 10, - period: 60, - }) -) - .createServerAction() - .input( - z.object({ +export const addMessageToLatteAction = withProject + .use(withRateLimit({ limit: 10, period: 60 })) + .inputSchema( + withProjectSchema.extend({ threadUuid: z.string(), message: z.string(), context: z.string(), debugVersionUuid: z.string().optional(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace, user } = ctx - const { message, threadUuid, context, debugVersionUuid } = input + const { message, threadUuid, context, debugVersionUuid } = parsedInput const threadScope = new LatteThreadsRepository(workspace.id) const thread = await threadScope.findByUuidAndUser({ threadUuid, diff --git a/apps/web/src/actions/latte/discardChanges.ts b/apps/web/src/actions/latte/discardChanges.ts index dec2aaa176..0bc510465b 100644 --- a/apps/web/src/actions/latte/discardChanges.ts +++ b/apps/web/src/actions/latte/discardChanges.ts @@ -6,15 +6,14 @@ import { undoLatteThreadChanges } from '@latitude-data/core/services/copilot/lat import { evaluateLatteThreadChanges } from '@latitude-data/core/services/copilot/latte/threads/evaluateChanges' export const discardLatteChangesActions = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ threadUuid: z.string(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace } = ctx - const { threadUuid } = input + const { threadUuid } = parsedInput const checkpoints = await undoLatteThreadChanges({ workspace, diff --git a/apps/web/src/actions/latte/new.ts b/apps/web/src/actions/latte/new.ts index 6845a1a897..c4e8bd3ccd 100644 --- a/apps/web/src/actions/latte/new.ts +++ b/apps/web/src/actions/latte/new.ts @@ -1,27 +1,22 @@ 'use server' +import { z } from 'zod' import { createLatteJob } from '@latitude-data/core/services/copilot/latte/createLatteJob' import { createLatteThread } from '@latitude-data/core/services/copilot/latte/threads/createThread' -import { z } from 'zod' -import { withProject, withRateLimit } from '../procedures' +import { withProject, withProjectSchema, withRateLimit } from '../procedures' -export const createNewLatteAction = ( - await withRateLimit(withProject, { - limit: 10, - period: 60, - }) -) - .createServerAction() - .input( - z.object({ +export const createNewLatteAction = withProject + .use(withRateLimit({ limit: 10, period: 60 })) + .inputSchema( + withProjectSchema.extend({ message: z.string(), context: z.string(), debugVersionUuid: z.string().optional(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace, user, project } = ctx - const { message, context, debugVersionUuid } = input + const { message, context, debugVersionUuid } = parsedInput const thread = await createLatteThread({ user, workspace, diff --git a/apps/web/src/actions/latte/partialAcceptChanges.ts b/apps/web/src/actions/latte/partialAcceptChanges.ts index a6e8193ff0..ff2553a13d 100644 --- a/apps/web/src/actions/latte/partialAcceptChanges.ts +++ b/apps/web/src/actions/latte/partialAcceptChanges.ts @@ -6,16 +6,15 @@ import { partialAcceptLatteChanges } from '@latitude-data/core/services/copilot/ import { evaluateLatteThreadChanges } from '@latitude-data/core/services/copilot/latte/threads/evaluateChanges' export const partialAcceptLatteChangesAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ threadUuid: z.string(), documentUuidsToAccept: z.array(z.string()), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace } = ctx - const { threadUuid, documentUuidsToAccept } = input + const { threadUuid, documentUuidsToAccept } = parsedInput const checkpoints = await partialAcceptLatteChanges({ workspace, diff --git a/apps/web/src/actions/latte/partialRejectChanges.ts b/apps/web/src/actions/latte/partialRejectChanges.ts index 39575c1dcf..2e04b12323 100644 --- a/apps/web/src/actions/latte/partialRejectChanges.ts +++ b/apps/web/src/actions/latte/partialRejectChanges.ts @@ -6,16 +6,15 @@ import { partialRejectLatteChanges } from '@latitude-data/core/services/copilot/ import { evaluateLatteThreadChanges } from '@latitude-data/core/services/copilot/latte/threads/evaluateChanges' export const partialRejectLatteChangesAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ threadUuid: z.string(), documentUuidsToReject: z.array(z.string()), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace } = ctx - const { threadUuid, documentUuidsToReject } = input + const { threadUuid, documentUuidsToReject } = parsedInput const checkpoints = await partialRejectLatteChanges({ workspace, diff --git a/apps/web/src/actions/latte/stopChat.ts b/apps/web/src/actions/latte/stopChat.ts index f507c3983b..63d17ef7b9 100644 --- a/apps/web/src/actions/latte/stopChat.ts +++ b/apps/web/src/actions/latte/stopChat.ts @@ -7,15 +7,10 @@ import { queues } from '@latitude-data/core/queues' import { NotFoundError } from '@latitude-data/constants/errors' export const stopChatLatteAction = authProcedure - .createServerAction() - .input( - z.object({ - jobId: z.string(), - }), - ) - .handler(async ({ input }) => { + .inputSchema(z.object({ jobId: z.string() })) + .action(async ({ parsedInput }) => { const { latteQueue } = await queues() - const { jobId } = input + const { jobId } = parsedInput const job = await latteQueue.getJob(jobId) if (!job) { throw new NotFoundError('No job found to stop Latte chat') diff --git a/apps/web/src/actions/magicLinkTokens/confirm.test.ts b/apps/web/src/actions/magicLinkTokens/confirm.test.ts index 77a6e59c4f..82e68dc1c9 100644 --- a/apps/web/src/actions/magicLinkTokens/confirm.test.ts +++ b/apps/web/src/actions/magicLinkTokens/confirm.test.ts @@ -4,12 +4,12 @@ import { createProject } from '@latitude-data/core/factories' import { generateUUIDIdentifier } from '@latitude-data/core/lib/generateUUID' import { confirmMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/confirm' import { createMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/create' -import { redirect } from 'next/navigation' +import { frontendRedirect } from '$/lib/frontendRedirect' import { beforeEach, describe, expect, it, vi } from 'vitest' import { confirmMagicLinkTokenAction } from './confirm' -vi.mock('next/navigation', () => ({ - redirect: vi.fn(), +vi.mock('$/lib/frontendRedirect', () => ({ + frontendRedirect: vi.fn(), })) vi.mock('$/services/auth/setSession', () => ({ @@ -34,7 +34,7 @@ describe('confirmMagicLinkTokenAction', () => { token: token.token, }) - expect(redirect).toHaveBeenCalledWith(ROUTES.auth.login) + expect(frontendRedirect).toHaveBeenCalledWith(ROUTES.auth.login) }) it('should redirect to login if magic link token does not exist', async () => { @@ -42,7 +42,7 @@ describe('confirmMagicLinkTokenAction', () => { token: generateUUIDIdentifier(), }) - expect(redirect).toHaveBeenCalledWith(ROUTES.auth.login) + expect(frontendRedirect).toHaveBeenCalledWith(ROUTES.auth.login) }) it('redirects to dashboard when magic link exists and is not expired', async () => { @@ -52,7 +52,7 @@ describe('confirmMagicLinkTokenAction', () => { token: token.token, }) - expect(redirect).toHaveBeenCalledWith(ROUTES.dashboard.root) + expect(frontendRedirect).toHaveBeenCalledWith(ROUTES.dashboard.root) }) it('redirects to returnTo when magic link exists and is not expired and has returnTo', async () => { @@ -66,6 +66,6 @@ describe('confirmMagicLinkTokenAction', () => { returnTo: ROUTES.projects.root, }) - expect(redirect).toHaveBeenCalledWith(ROUTES.projects.root) + expect(frontendRedirect).toHaveBeenCalledWith(ROUTES.projects.root) }) }) diff --git a/apps/web/src/actions/magicLinkTokens/confirm.ts b/apps/web/src/actions/magicLinkTokens/confirm.ts index ff1019fd53..da9eb5ce96 100644 --- a/apps/web/src/actions/magicLinkTokens/confirm.ts +++ b/apps/web/src/actions/magicLinkTokens/confirm.ts @@ -10,35 +10,35 @@ import { unsafelyGetUser, } from '@latitude-data/core/data-access' import { confirmMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/confirm' -import { redirect } from 'next/navigation' import { z } from 'zod' -import { createServerAction } from 'zsa' +import { errorHandlingProcedure } from '../procedures' +import { frontendRedirect } from '$/lib/frontendRedirect' -export const confirmMagicLinkTokenAction = createServerAction() - .input( +export const confirmMagicLinkTokenAction = errorHandlingProcedure + .inputSchema( z.object({ token: z.string(), returnTo: z.string().optional(), }), ) - .handler(async ({ input }) => { - const magicLinkToken = await unsafelyFindMagicLinkByToken(input.token).then( - (r) => r[0], - ) + .action(async ({ parsedInput }) => { + const magicLinkToken = await unsafelyFindMagicLinkByToken( + parsedInput.token, + ).then((r) => r[0]) if (!magicLinkToken || !!magicLinkToken.expiredAt) { - if (!input.returnTo) { - return redirect(ROUTES.auth.login) + if (!parsedInput.returnTo) { + return frontendRedirect(ROUTES.auth.login) } - redirect( - `${ROUTES.auth.login}?returnTo=${encodeURIComponent(input.returnTo)}`, + return frontendRedirect( + `${ROUTES.auth.login}?returnTo=${encodeURIComponent(parsedInput.returnTo)}`, ) } const user = await unsafelyGetUser(magicLinkToken.userId) if (!user) throw new NotFoundError('User not found') - await confirmMagicLinkToken(input.token).then((r) => r.unwrap()) + await confirmMagicLinkToken(parsedInput.token).then((r) => r.unwrap()) const workspace = await getFirstWorkspace({ userId: user.id }).then((r) => r.unwrap(), @@ -53,9 +53,9 @@ export const confirmMagicLinkTokenAction = createServerAction() }, }) - if (!input.returnTo || !isLatitudeUrl(input.returnTo)) { - return redirect(ROUTES.dashboard.root) + if (!parsedInput.returnTo || !isLatitudeUrl(parsedInput.returnTo)) { + return frontendRedirect(ROUTES.dashboard.root) } - return redirect(input.returnTo) + return frontendRedirect(parsedInput.returnTo) }) diff --git a/apps/web/src/actions/memberships/destroy.ts b/apps/web/src/actions/memberships/destroy.ts index 67d996cc2b..4c4c4cac22 100644 --- a/apps/web/src/actions/memberships/destroy.ts +++ b/apps/web/src/actions/memberships/destroy.ts @@ -7,20 +7,13 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const destroyMembershipAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.string(), - }), - ) - .handler(async ({ input, ctx }) => { - const { id } = input - const { user } = ctx + .inputSchema(z.object({ id: z.string() })) + .action(async ({ parsedInput: { id }, ctx: { workspace, user } }) => { if (user.id === id) { throw new Error('You cannot remove yourself from the workspace.') } - const membershipsScope = new MembershipsRepository(ctx.workspace.id) + const membershipsScope = new MembershipsRepository(workspace.id) const membership = await membershipsScope .findByUserId(id) .then((r) => r.unwrap()) diff --git a/apps/web/src/actions/procedures/index.ts b/apps/web/src/actions/procedures/index.ts index d8639cac6d..bd5a8c1d68 100644 --- a/apps/web/src/actions/procedures/index.ts +++ b/apps/web/src/actions/procedures/index.ts @@ -1,162 +1,288 @@ -import { getDataFromSession } from '$/data-access' -import { captureException } from '$/helpers/captureException' +import { RateLimiterRedis, RateLimiterRes } from 'rate-limiter-flexible' +import { z } from 'zod' +import { + createMiddleware, + createSafeActionClient, + DEFAULT_SERVER_ERROR_MESSAGE, +} from 'next-safe-action' +import { ReplyError } from 'ioredis' +import { headers } from 'next/headers' + import { getUnsafeIp } from '$/helpers/ip' +import { DatasetsRepository } from '@latitude-data/core/repositories' +import { captureException } from '$/helpers/captureException' +import { Dataset } from '@latitude-data/core/browser' import { getCurrentUserOrRedirect } from '$/services/auth/getCurrentUser' +import { cache } from '@latitude-data/core/cache' import { LatitudeError, + LatitudeErrorDetails, RateLimitError, UnauthorizedError, + UnprocessableEntityError, } from '@latitude-data/constants/errors' -import { Dataset } from '@latitude-data/core/browser' -import { cache } from '@latitude-data/core/cache' import { CommitsRepository, - DatasetsRepository, DocumentVersionsRepository, EvaluationsV2Repository, ProjectsRepository, } from '@latitude-data/core/repositories' -import { ReplyError } from 'ioredis' -import { headers } from 'next/headers' -import { RateLimiterRedis, RateLimiterRes } from 'rate-limiter-flexible' -import { z } from 'zod' -import { createServerActionProcedure, TAnyCompleteProcedure } from 'zsa' +import { getDataFromSession } from '$/data-access' +import { flattenErrors } from '@latitude-data/core/lib/zodUtils' const DEFAULT_RATE_LIMIT_POINTS = 1000 const DEFAULT_RATE_LIMIT_DURATION = 60 -export const errorHandlingProcedure = createServerActionProcedure() - .onError(async (error) => { - if (error instanceof LatitudeError) return +/** + * FIXME: + * `next-safe-action` needs an abstraction. + * It has a type safety issue when using middlewares like `withProject`. + * + * At runtime it works because we validate that `projectId` is passed + * inside `withProject` middleware, but types are not inferred correctly. + * + * When using in frontend a next.js action that is using a middleware like `withProject` + * the types in their schema are not inferred correctly. + * + * Current workaround is to extend the `inputSchema` in the action itself. + * + * withProject.inputSchema(withProjectSchema.extend({ ... })) + * + * This is not ideal because it force the developer to remember to do this + * If they forget, types are wrong. and code can be merged without passing `projectId` + * + * My proposal is to add a factory function to create these middlewares + * + * ```ts + * createServerAction({ + * scope: 'secure:withProject', + * schema: z.object({ projectId: z.number().or(z.string()) }), + * }).action(...) + * + * This under the hood is doing the `withProject.extend({...})` automatically + */ + +/** + * Base action client with error handling. + */ +export const errorHandlingProcedure = createSafeActionClient({ + defaultValidationErrorsShape: 'flattened', + handleServerError: async (error) => { + if (error instanceof UnprocessableEntityError) { + return `${error.message}: ${JSON.stringify(error.details)}` + } + + if (error instanceof LatitudeError) return error.message try { const data = await getCurrentUserOrRedirect() - captureException(error as Error, { component: 'serverAction', userId: data.user.id, userName: data.user.name, userEmail: data.user.email, }) - } catch (_) { + } catch { captureException(error as Error, { component: 'serverAction' }) } - }) - .handler((ctx) => ({ ...ctx })) - -export const maybeAuthProcedure = createServerActionProcedure( - errorHandlingProcedure, -).handler(async () => { - const data = await getDataFromSession() - if (!data.user || !data.workspace) { - return {} - } - return { - session: data.session, - workspace: data.workspace, - user: data.user, - } + return DEFAULT_SERVER_ERROR_MESSAGE + }, }) -export const authProcedure = createServerActionProcedure( - errorHandlingProcedure, -).handler(async () => { - const data = await getDataFromSession() - if (!data.user || !data.workspace) { - throw new UnauthorizedError('Unauthorized') - } +/** + * Auth procedure. + */ +export const authProcedure = errorHandlingProcedure.use( + async ({ next, ctx }) => { + const data = await getDataFromSession() + + if (!data.user || !data.workspace) { + throw new UnauthorizedError('Unauthorized') + } + return next({ + ctx: { + ...ctx, + session: data.session, + workspace: data.workspace, + user: data.user, + }, + }) + }, +) + +/** + * Admin procedure. + */ +export const withAdmin = authProcedure.use(async ({ next, ctx }) => { + if (!ctx.user?.admin) throw new UnauthorizedError('Unauthorized') + + return next({ ctx }) +}) + +/** + * Maybe-auth procedure. + */ +export const maybeAuthProcedure = errorHandlingProcedure.use( + async ({ next, ctx }) => { + const data = await getDataFromSession() + if (!data.user || !data.workspace) { + return next({ ctx }) + } + return next({ + ctx: { + ...ctx, + session: data.session, + workspace: data.workspace, + user: data.user, + }, + }) + }, +) - return { - session: data.session, - workspace: data.workspace, - user: data.user, +function validateSchema(schema: T, data: unknown) { + const parsed = schema.safeParse(data) + if (!parsed.success) { + const errors = flattenErrors(parsed) as LatitudeErrorDetails + throw new UnprocessableEntityError('Invalid data', errors) } + + return parsed.data +} + +export const withProjectSchema = z.object({ + projectId: z.number().or(z.string()), +}) + +/** + * With project procedure. + */ +export const withProject = authProcedure.use( + async ({ next, ctx, clientInput }) => { + const { projectId } = validateSchema(withProjectSchema, clientInput) + const projectScope = new ProjectsRepository(ctx.workspace.id) + const project = await projectScope + .getProjectById(Number(projectId)) + .then((r) => r.unwrap()) + + return next({ ctx: { ...ctx, project } }) + }, +) + +export const withCommitSchema = withProjectSchema.extend({ + commitUuid: z.string(), }) -export const withProject = createServerActionProcedure(authProcedure) - .input(z.object({ projectId: z.number().or(z.string()) })) - .handler(async ({ input, ctx }) => { - const { workspace } = ctx - const projectScope = new ProjectsRepository(workspace.id) - const project = ( - await projectScope.getProjectById(Number(input.projectId)) - ).unwrap() - - return { ...ctx, project } - }) - -export const withCommit = createServerActionProcedure(withProject) - .input(z.object({ commitUuid: z.string() })) - .handler(async ({ input, ctx }) => { +/** + * With commit procedure. + */ +export const withCommit = withProject.use( + async ({ next, ctx, clientInput }) => { + const { commitUuid } = validateSchema(withCommitSchema, clientInput) const repository = new CommitsRepository(ctx.workspace.id) const commit = await repository .getCommitByUuid({ projectId: ctx.project.id, - uuid: input.commitUuid, + uuid: commitUuid, }) .then((r) => r.unwrap()) - return { ...ctx, commit } - }) + return next({ ctx: { ...ctx, commit } }) + }, +) + +export const withDocumentSchema = withCommitSchema.extend({ + documentUuid: z.string(), +}) + +/** + * With document procedure. + */ +export const withDocument = withCommit.use( + async ({ next, ctx, clientInput }) => { + const { documentUuid } = validateSchema(withDocumentSchema, clientInput) -export const withDocument = createServerActionProcedure(withCommit) - .input(z.object({ documentUuid: z.string() })) - .handler(async ({ input, ctx }) => { const repo = new DocumentVersionsRepository(ctx.workspace.id) const document = await repo .getDocumentAtCommit({ projectId: ctx.project.id, commitUuid: ctx.commit.uuid, - documentUuid: input.documentUuid, + documentUuid, }) .then((r) => r.unwrap()) - return { ...ctx, document, currentCommitUuid: input.commitUuid } - }) + return next({ + ctx: { ...ctx, document, currentCommitUuid: ctx.commit.uuid }, + }) + }, +) + +export const withEvaluationSchema = withDocumentSchema.extend({ + evaluationUuid: z.string(), +}) -export const withEvaluation = createServerActionProcedure(withDocument) - .input(z.object({ evaluationUuid: z.string() })) - .handler(async ({ input, ctx }) => { +/** + * With evaluation procedure. + */ +export const withEvaluation = withDocument.use( + async ({ next, ctx, clientInput }) => { + const { evaluationUuid } = validateSchema(withEvaluationSchema, clientInput) const repository = new EvaluationsV2Repository(ctx.workspace.id) const evaluation = await repository .getAtCommitByDocument({ projectId: ctx.project.id, commitUuid: ctx.commit.uuid, documentUuid: ctx.document.documentUuid, - evaluationUuid: input.evaluationUuid, + evaluationUuid, }) .then((r) => r.unwrap()) - return { ...ctx, evaluation } - }) + return next({ ctx: { ...ctx, evaluation } }) + }, +) -export const withAdmin = createServerActionProcedure(authProcedure).handler( - async ({ ctx }) => { - if (!ctx.user.admin) throw new UnauthorizedError('Unauthorized') +export const withDatasetSchema = withDocumentSchema.extend({ + datasetId: z.number(), +}) - return ctx +/** + * With dataset procedure. + */ +export const withDataset = withDocument.use( + async ({ next, ctx, clientInput }) => { + const { datasetId } = validateSchema(withDatasetSchema, clientInput) + const repo = new DatasetsRepository(ctx.workspace.id) + const dataset = await repo.find(datasetId).then((r) => r.unwrap()) + return next({ ctx: { ...ctx, dataset: dataset as Dataset } }) }, ) -export async function withRateLimit( - procedure: T, - { - limit = DEFAULT_RATE_LIMIT_POINTS, - period = DEFAULT_RATE_LIMIT_DURATION, - }: { - limit?: number - period?: number - }, -): Promise { - const rateLimiter = new RateLimiterRedis({ - storeClient: await cache(), - points: limit, - duration: period, - }) - - return createServerActionProcedure(procedure).handler( - async ({ ctx, ...rest }) => { +type DataFromSession = Awaited> + +type RateLimitCtx = { + session?: DataFromSession['session'] + user?: DataFromSession['user'] + workspace?: DataFromSession['workspace'] +} + +/** + * With rate limit wrapper. + */ +export function withRateLimit({ + limit = DEFAULT_RATE_LIMIT_POINTS, + period = DEFAULT_RATE_LIMIT_DURATION, +}: { + limit?: number + period?: number +}) { + return createMiddleware<{ ctx: RateLimitCtx }>().define( + async ({ ctx, next }) => { + const rateLimiter = new RateLimiterRedis({ + storeClient: await cache(), + points: limit, + duration: period, + }) + const key = ctx.user?.id || getUnsafeIp(await headers()) || 'unknown' try { @@ -165,26 +291,12 @@ export async function withRateLimit( if (error instanceof RateLimiterRes) { throw new RateLimitError('Too many requests') } - if (!(error instanceof ReplyError)) { throw error } } - return { ...ctx, ...rest } + return next({ ctx }) }, - ) as T -} - -export const withDataset = createServerActionProcedure(withDocument) - .input( - z.object({ - datasetId: z.number(), - }), ) - .handler(async ({ input, ctx }) => { - const repo = new DatasetsRepository(ctx.workspace.id) - const dataset = await repo.find(input.datasetId).then((r) => r.unwrap()) - - return { ...ctx, dataset: dataset as Dataset } - }) +} diff --git a/apps/web/src/actions/projects/create.ts b/apps/web/src/actions/projects/create.ts index d84195f52d..bd66c2ad87 100644 --- a/apps/web/src/actions/projects/create.ts +++ b/apps/web/src/actions/projects/create.ts @@ -8,12 +8,15 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const createProjectAction = authProcedure - .createServerAction() - .input(z.object({ name: z.string() })) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ name: z.string() })) + .action(async ({ parsedInput, ctx }) => { const workspace = ctx.workspace const user = ctx.user - const result = await createProject({ name: input.name, workspace, user }) + const result = await createProject({ + name: parsedInput.name, + workspace, + user, + }) const { project } = result.unwrap() revalidatePath(ROUTES.dashboard.root) diff --git a/apps/web/src/actions/projects/destroy.ts b/apps/web/src/actions/projects/destroy.ts index 1f9fd6f973..92a67c7e8c 100644 --- a/apps/web/src/actions/projects/destroy.ts +++ b/apps/web/src/actions/projects/destroy.ts @@ -9,11 +9,12 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const destroyProjectAction = authProcedure - .createServerAction() - .input(z.object({ id: z.string() })) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.string() })) + .action(async ({ parsedInput, ctx }) => { const scope = new ProjectsRepository(ctx.workspace.id) - const project = await scope.find(Number(input.id)).then((r) => r.unwrap()) + const project = await scope + .find(Number(parsedInput.id)) + .then((r) => r.unwrap()) const result = await destroyProject({ project }) revalidatePath(ROUTES.dashboard.root) diff --git a/apps/web/src/actions/projects/update.ts b/apps/web/src/actions/projects/update.ts index ae378177cd..8469faf1d7 100644 --- a/apps/web/src/actions/projects/update.ts +++ b/apps/web/src/actions/projects/update.ts @@ -8,13 +8,12 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const updateProjectAction = authProcedure - .createServerAction() - .input(z.object({ id: z.number().or(z.string()), name: z.string() })) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.number().or(z.string()), name: z.string() })) + .action(async ({ parsedInput, ctx }) => { const workspace = ctx.workspace const scope = new ProjectsRepository(workspace.id) - const project = await scope.find(input.id).then((r) => r.unwrap()) - const result = await updateProject(project, { name: input.name }) + const project = await scope.find(parsedInput.id).then((r) => r.unwrap()) + const result = await updateProject(project, { name: parsedInput.name }) revalidatePath('/dashboard') diff --git a/apps/web/src/actions/promocodes/claim.ts b/apps/web/src/actions/promocodes/claim.ts index 9bcea17dd3..f9444cfaea 100644 --- a/apps/web/src/actions/promocodes/claim.ts +++ b/apps/web/src/actions/promocodes/claim.ts @@ -1,24 +1,19 @@ 'use server' import { z } from 'zod' -import { authProcedure, withRateLimit } from '$/actions/procedures' +import { errorHandlingProcedure, withRateLimit } from '$/actions/procedures' import { claimPromocode } from '@latitude-data/core/services/promocodes/claimPromocode' -export const claimPromocodeAction = ( - await withRateLimit(authProcedure, { - limit: 10, - period: 60, - }) -) - .createServerAction() - .input( +export const claimPromocodeAction = errorHandlingProcedure + .use(withRateLimit({ limit: 10, period: 60 })) + .inputSchema( z.object({ code: z.string(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace } = ctx - const { code } = input - const result = await claimPromocode({ code, workspace }) + const { code } = parsedInput + const result = await claimPromocode({ code, workspace: workspace! }) return result.unwrap() }) diff --git a/apps/web/src/actions/providerApiKeys/create.test.ts b/apps/web/src/actions/providerApiKeys/create.test.ts index b5b57d5aed..0058e93f6a 100644 --- a/apps/web/src/actions/providerApiKeys/create.test.ts +++ b/apps/web/src/actions/providerApiKeys/create.test.ts @@ -20,14 +20,14 @@ describe('createProviderApiKeyAction', () => { it('errors when the user is not authenticated', async () => { mocks.getSession.mockResolvedValue(null) - const [_, error] = await createProviderApiKeyAction({ + const { serverError } = await createProviderApiKeyAction({ provider: Providers.OpenAI, token: 'test-token', url: 'https://api.openai.com', name: 'Test API Key', }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -48,17 +48,19 @@ describe('createProviderApiKeyAction', () => { }) it('successfully creates a provider API key', async () => { - const [result, error] = await createProviderApiKeyAction({ + const { + data: provider, + serverError, + validationErrors, + } = await createProviderApiKeyAction({ provider: Providers.OpenAI, token: 'test-token', name: 'Test API Key', configuration: { endpoint: 'chat_completions' }, }) - expect(error).toBeNull() - - const provider = result! - + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(provider.provider).toEqual(Providers.OpenAI) expect(provider.name).toEqual('Test API Key') expect(provider.token).toEqual('tes********oken') @@ -69,20 +71,24 @@ describe('createProviderApiKeyAction', () => { }) it('successfully creates an OpenAI provider for responses endpoint', async () => { - const [result] = await createProviderApiKeyAction({ + const { data } = await createProviderApiKeyAction({ provider: Providers.OpenAI, token: 'test-token', name: 'Test API Key', configuration: { endpoint: 'responses' }, }) - expect(result!.configuration).toEqual({ + expect(data!.configuration).toEqual({ endpoint: 'responses', }) }) it('successfully creates a provider API key with a default model', async () => { - const [result, error] = await createProviderApiKeyAction({ + const { + data: provider, + serverError, + validationErrors, + } = await createProviderApiKeyAction({ provider: Providers.OpenAI, token: 'test-token', name: 'Test API Key', @@ -90,37 +96,37 @@ describe('createProviderApiKeyAction', () => { configuration: { endpoint: 'chat_completions' }, }) - expect(error).toBeNull() - - const provider = result! - - expect(provider.provider).toEqual(Providers.OpenAI) - expect(provider.name).toEqual('Test API Key') - expect(provider.token).toEqual('tes********oken') - expect(provider.defaultModel).toEqual('gpt-4o') + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() + expect(provider?.provider).toEqual(Providers.OpenAI) + expect(provider?.name).toEqual('Test API Key') + expect(provider?.token).toEqual('tes********oken') + expect(provider?.defaultModel).toEqual('gpt-4o') }) it('handles errors when creating a provider API key fails', async () => { - const [data, error] = await createProviderApiKeyAction({ + const { data, validationErrors } = await createProviderApiKeyAction({ provider: Providers.OpenAI, - // @ts-expect-error - Testing invalid input + // @ts-expect-error - testing invalid input token: null, name: 'Test API Key', }) - expect(data).toBeNull() - expect(error).toBeDefined() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors?.token).toContain( + 'Invalid input: expected string, received null', + ) }) it('fails creating a custom provider without url', async () => { - const [data, error] = await createProviderApiKeyAction({ + const { data, serverError } = await createProviderApiKeyAction({ provider: Providers.Custom, token: 'test-token', name: 'Test API Key', }) - expect(data).toBeNull() - expect(error).toBeDefined() + expect(data).toBeUndefined() + expect(serverError).toEqual('Custom provider requires a URL') }) it('returns proper error when duplicate name', async () => { @@ -128,20 +134,22 @@ describe('createProviderApiKeyAction', () => { workspace, type: Providers.OpenAI, name: 'foo', - // @ts-expect-error - Mock + // @ts-expect-error - testing user, configuration: { endpoint: 'chat_completions' }, }) - const [data, error] = await createProviderApiKeyAction({ + const { data, validationErrors } = await createProviderApiKeyAction({ provider: Providers.OpenAI, token: 'test-token', name: 'foo', url: 'https://api.openai.com', }) - expect(data).toBeNull() - expect(error).toBeDefined() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors.configuration).toContain( + 'Invalid input: expected object, received undefined', + ) }) it('allows creating two providers with same name if one is deleted', async () => { @@ -149,21 +157,23 @@ describe('createProviderApiKeyAction', () => { workspace, type: Providers.OpenAI, name: 'foo', - // @ts-expect-error - Mock + // @ts-expect-error - testing user, deletedAt: new Date(), configuration: { endpoint: 'chat_completions' }, }) - const [_, error] = await createProviderApiKeyAction({ - provider: Providers.OpenAI, - token: 'test-token', - name: 'foo', - url: 'https://api.openai.com', - configuration: { endpoint: 'chat_completions' }, - }) - - expect(error).toBeNull() + const { serverError, validationErrors } = + await createProviderApiKeyAction({ + provider: Providers.OpenAI, + token: 'test-token', + name: 'foo', + url: 'https://api.openai.com', + configuration: { endpoint: 'chat_completions' }, + }) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() }) }) }) diff --git a/apps/web/src/actions/providerApiKeys/create.ts b/apps/web/src/actions/providerApiKeys/create.ts index 3cc3b13b0b..850406785f 100644 --- a/apps/web/src/actions/providerApiKeys/create.ts +++ b/apps/web/src/actions/providerApiKeys/create.ts @@ -6,18 +6,17 @@ import { authProcedure } from '../procedures' import { inputSchema } from './inputSchema' export const createProviderApiKeyAction = authProcedure - .createServerAction() - .input(inputSchema) - .handler(async ({ input, ctx }) => { + .inputSchema(inputSchema) + .action(async ({ parsedInput, ctx }) => { return await createProviderApiKey({ workspace: ctx.workspace, - provider: input.provider, - token: input.token, - url: input.url, - name: input.name, - defaultModel: input.defaultModel, + provider: parsedInput.provider, + token: parsedInput.token, + url: parsedInput.url, + name: parsedInput.name, + defaultModel: parsedInput.defaultModel, author: ctx.user, - configuration: input.configuration, + configuration: parsedInput.configuration, }) .then((r) => r.unwrap()) .then(providerApiKeyPresenter) diff --git a/apps/web/src/actions/providerApiKeys/destroy.ts b/apps/web/src/actions/providerApiKeys/destroy.ts index 48fc045cc6..84c4220868 100644 --- a/apps/web/src/actions/providerApiKeys/destroy.ts +++ b/apps/web/src/actions/providerApiKeys/destroy.ts @@ -8,18 +8,13 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const destroyProviderApiKeyAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.number().or(z.string()), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.number().or(z.string()) })) + .action(async ({ parsedInput, ctx }) => { const providerApiKeysRepository = new ProviderApiKeysRepository( ctx.workspace.id, ) const apiKeyProvider = await providerApiKeysRepository - .find(Number(input.id)) + .find(Number(parsedInput.id)) .then((r) => r.unwrap()) return await destroyProviderApiKey(apiKeyProvider) diff --git a/apps/web/src/actions/providerApiKeys/update.ts b/apps/web/src/actions/providerApiKeys/update.ts index a8776a2787..8ad3d49901 100644 --- a/apps/web/src/actions/providerApiKeys/update.ts +++ b/apps/web/src/actions/providerApiKeys/update.ts @@ -7,19 +7,18 @@ import providerApiKeyPresenter from '$/presenters/providerApiKeyPresenter' import { updateProviderApiKeyName } from '@latitude-data/core/services/providerApiKeys/updateName' export const updateProviderApiKeyAction = authProcedure - .createServerAction() - .input(z.object({ id: z.coerce.number(), name: z.string() })) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ id: z.coerce.number(), name: z.string() })) + .action(async ({ parsedInput, ctx }) => { const providerApiKeysRepository = new ProviderApiKeysRepository( ctx.workspace.id, ) const providerApiKey = await providerApiKeysRepository - .find(input.id) + .find(parsedInput.id) .then((r) => r.unwrap()) return await updateProviderApiKeyName({ providerApiKey, - name: input.name, + name: parsedInput.name, workspaceId: ctx.workspace.id, }) .then((r) => r.unwrap()) diff --git a/apps/web/src/actions/rewards/claimRewardAction.ts b/apps/web/src/actions/rewards/claimRewardAction.ts index 3401b8d711..f27fc42c3a 100644 --- a/apps/web/src/actions/rewards/claimRewardAction.ts +++ b/apps/web/src/actions/rewards/claimRewardAction.ts @@ -7,21 +7,20 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const claimRewardAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ type: z.enum(Object.values(RewardType) as [string, ...string[]]), reference: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const workspace = ctx.workspace const user = ctx.user const result = await claimReward({ workspace, user, - type: input.type as RewardType, - reference: input.reference, + type: parsedInput.type as RewardType, + reference: parsedInput.reference, }) return result.unwrap() as ClaimedReward | undefined }) diff --git a/apps/web/src/actions/rewards/fetchPendingRewardClaimsAction.ts b/apps/web/src/actions/rewards/fetchPendingRewardClaimsAction.ts deleted file mode 100644 index 65c206b704..0000000000 --- a/apps/web/src/actions/rewards/fetchPendingRewardClaimsAction.ts +++ /dev/null @@ -1,11 +0,0 @@ -'use server' - -import { findAllRewardClaimsPendingToValidate } from '@latitude-data/core/data-access' -import { withAdmin } from '../procedures' - -export const fetchPendingRewardClaimsAction = withAdmin - .createServerAction() - .handler(async () => { - const result = await findAllRewardClaimsPendingToValidate() - return result.unwrap() - }) diff --git a/apps/web/src/actions/rewards/updateRewardClaimValidityAction.ts b/apps/web/src/actions/rewards/updateRewardClaimValidityAction.ts index d001a6ab20..5752ab8581 100644 --- a/apps/web/src/actions/rewards/updateRewardClaimValidityAction.ts +++ b/apps/web/src/actions/rewards/updateRewardClaimValidityAction.ts @@ -6,17 +6,16 @@ import { z } from 'zod' import { withAdmin } from '../procedures' export const updateRewardClaimValidityAction = withAdmin - .createServerAction() - .input( + .inputSchema( z.object({ claimId: z.number(), isValid: z.boolean().nullable(), }), ) - .handler(async ({ input }) => { + .action(async ({ parsedInput }) => { const result = await updateRewardClaim({ - claimId: input.claimId, - isValid: input.isValid, + claimId: parsedInput.claimId, + isValid: parsedInput.isValid, }) return result.unwrap() as ClaimedReward | undefined diff --git a/apps/web/src/actions/runs/stop.ts b/apps/web/src/actions/runs/stop.ts index 4f0053049e..9260b874ba 100644 --- a/apps/web/src/actions/runs/stop.ts +++ b/apps/web/src/actions/runs/stop.ts @@ -3,15 +3,14 @@ import { RunsRepository } from '@latitude-data/core/repositories' import { stopRun } from '@latitude-data/core/services/runs/stop' import { z } from 'zod' -import { withProject } from '../procedures' +import { withProject, withProjectSchema } from '../procedures' export const stopRunAction = withProject - .createServerAction() - .input(z.object({ runUuid: z.string() })) - .handler(async ({ input, ctx }) => { + .inputSchema(withProjectSchema.extend({ runUuid: z.string() })) + .action(async ({ parsedInput, ctx }) => { const repository = new RunsRepository(ctx.workspace.id, ctx.project.id) const run = await repository - .get({ runUuid: input.runUuid }) + .get({ runUuid: parsedInput.runUuid }) .then((r) => r.unwrap()) const result = await stopRun({ diff --git a/apps/web/src/actions/sdk/addMessagesAction.ts b/apps/web/src/actions/sdk/addMessagesAction.ts index 9808227195..dac7548faf 100644 --- a/apps/web/src/actions/sdk/addMessagesAction.ts +++ b/apps/web/src/actions/sdk/addMessagesAction.ts @@ -5,7 +5,7 @@ import { publisher } from '@latitude-data/core/events/publisher' import { type ChainEventDto, type Message } from '@latitude-data/sdk' import { createSdk } from '$/app/(private)/_lib/createSdk' import { getCurrentUserOrRedirect } from '$/services/auth/getCurrentUser' -import { createStreamableValue } from 'ai/rsc' +import { createStreamableValue } from '@ai-sdk/rsc' type AddMessagesActionProps = { documentLogUuid: string diff --git a/apps/web/src/actions/sdk/addSharedMessagesAction.ts b/apps/web/src/actions/sdk/addSharedMessagesAction.ts index 48c9dd3d2a..0e21e4849d 100644 --- a/apps/web/src/actions/sdk/addSharedMessagesAction.ts +++ b/apps/web/src/actions/sdk/addSharedMessagesAction.ts @@ -4,7 +4,7 @@ import { LogSources, StreamEventTypes } from '@latitude-data/core/browser' import { publisher } from '@latitude-data/core/events/publisher' import { type ChainEventDto, type Message } from '@latitude-data/sdk' import { createSdk } from '$/app/(private)/_lib/createSdk' -import { createStreamableValue } from 'ai/rsc' +import { createStreamableValue } from '@ai-sdk/rsc' import { findSharedDocumentCached } from '$/app/(public)/_data_access' type AddMessagesActionProps = { diff --git a/apps/web/src/actions/sdk/generateDatasetPreviewAction.ts b/apps/web/src/actions/sdk/generateDatasetPreviewAction.ts index ba821f6e1c..fe5b46b0fa 100644 --- a/apps/web/src/actions/sdk/generateDatasetPreviewAction.ts +++ b/apps/web/src/actions/sdk/generateDatasetPreviewAction.ts @@ -9,14 +9,13 @@ import { authProcedure } from '$/actions/procedures' import { z } from 'zod' export const generateDatasetPreviewAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ description: z.string(), parameters: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { if (!env.LATITUDE_CLOUD) { throw new BadRequestError(CLOUD_MESSAGES.generateDatasets) } @@ -45,8 +44,8 @@ export const generateDatasetPreviewAction = authProcedure stream: false, parameters: { row_count: 10, - parameters: input.parameters, - user_message: input.description, + parameters: parsedInput.parameters, + user_message: parsedInput.description, }, }) if (!result) { diff --git a/apps/web/src/actions/sdk/runDocumentAction.ts b/apps/web/src/actions/sdk/runDocumentAction.ts index 2b7448b18a..36673a7edb 100644 --- a/apps/web/src/actions/sdk/runDocumentAction.ts +++ b/apps/web/src/actions/sdk/runDocumentAction.ts @@ -7,7 +7,7 @@ import { ChainEvent } from '@latitude-data/constants' import { LogSources, StreamEventTypes } from '@latitude-data/core/browser' import { publisher } from '@latitude-data/core/events/publisher' import { Latitude, type ChainEventDto } from '@latitude-data/sdk' -import { createStreamableValue, StreamableValue } from 'ai/rsc' +import { createStreamableValue, StreamableValue } from '@ai-sdk/rsc' type RunDocumentActionProps = { documentPath: string diff --git a/apps/web/src/actions/sdk/runSharedPromptAction.ts b/apps/web/src/actions/sdk/runSharedPromptAction.ts index 89442f93b0..4288ef98ff 100644 --- a/apps/web/src/actions/sdk/runSharedPromptAction.ts +++ b/apps/web/src/actions/sdk/runSharedPromptAction.ts @@ -6,7 +6,7 @@ import { type ChainEventDto } from '@latitude-data/sdk' import { RunDocumentResponse } from '$/actions/sdk/runDocumentAction' import { createSdk } from '$/app/(private)/_lib/createSdk' import { findSharedDocumentCached } from '$/app/(public)/_data_access' -import { createStreamableValue } from 'ai/rsc' +import { createStreamableValue } from '@ai-sdk/rsc' type RunSharedPromptActionProps = { publishedDocumentUuid: string diff --git a/apps/web/src/actions/tools/results/submit.ts b/apps/web/src/actions/tools/results/submit.ts index 22e1029f44..f12e247752 100644 --- a/apps/web/src/actions/tools/results/submit.ts +++ b/apps/web/src/actions/tools/results/submit.ts @@ -1,21 +1,20 @@ 'use server' +import { z } from 'zod' import { authProcedure } from '$/actions/procedures' import { NotFoundError } from '@latitude-data/constants/errors' import { ApiKeysRepository } from '@latitude-data/core/repositories/apiKeysRepository' import { env } from '@latitude-data/env' -import { z } from 'zod' export const submitToolResultAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ toolCallId: z.string(), result: z.string(), isError: z.boolean().optional().default(false), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const { workspace } = ctx const gatewayUrl = buildGatewayUrl() @@ -25,7 +24,7 @@ export const submitToolResultAction = authProcedure const response = await fetch(`${gatewayUrl}/api/v3/tools/results`, { method: 'POST', - body: JSON.stringify(input), + body: JSON.stringify(parsedInput), headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, diff --git a/apps/web/src/actions/user/invite.test.ts b/apps/web/src/actions/user/invite.test.ts index 7ef047892b..3eff0772ae 100644 --- a/apps/web/src/actions/user/invite.test.ts +++ b/apps/web/src/actions/user/invite.test.ts @@ -1,5 +1,8 @@ import * as factories from '@latitude-data/core/factories' -import { PaymentRequiredError } from '@latitude-data/constants/errors' +import { + LatitudeError, + PaymentRequiredError, +} from '@latitude-data/constants/errors' import { beforeEach, describe, expect, it, vi } from 'vitest' import { inviteUserAction } from './invite' @@ -32,12 +35,11 @@ describe('inviteUserAction', () => { it('errors when the user is not authenticated', async () => { mocks.getSession.mockResolvedValue(null) - const [_, error] = await inviteUserAction({ + const { serverError } = await inviteUserAction({ email: 'test@example.com', name: 'Test User', }) - - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') }) }) @@ -71,12 +73,13 @@ describe('inviteUserAction', () => { mocks.applyUserPlanLimit.mockResolvedValue({ unwrap: () => {} }) mocks.inviteUser.mockResolvedValue({ unwrap: () => mockInvitedUser }) - const [data, error] = await inviteUserAction({ + const { data, serverError, validationErrors } = await inviteUserAction({ email: 'test@example.com', name: 'Test User', }) - expect(error).toBeNull() + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toEqual(mockInvitedUser) expect(mocks.applyUserPlanLimit).toHaveBeenCalledWith({ workspace }) expect(mocks.inviteUser).toHaveBeenCalledWith({ @@ -98,13 +101,15 @@ describe('inviteUserAction', () => { }, }) - const [data, error] = await inviteUserAction({ + const { data, serverError } = await inviteUserAction({ email: 'test@example.com', name: 'Test User', }) - expect(data).toBeNull() - expect(error).toBeDefined() + expect(data).toBeUndefined() + expect(serverError).toBe( + 'You have reached the maximum number of users allowed for this plan. Upgrade now.', + ) expect(mocks.applyUserPlanLimit).toHaveBeenCalledWith({ workspace }) expect(mocks.inviteUser).not.toHaveBeenCalled() }) @@ -119,13 +124,13 @@ describe('inviteUserAction', () => { }) mocks.inviteUser.mockResolvedValue({ unwrap: () => ({ id: 1 }) }) - const [data, error] = await inviteUserAction({ + const { data, serverError } = await inviteUserAction({ email: 'test@example.com', name: 'Test User', }) - expect(data).toBeNull() - expect(error).toBeDefined() + expect(data).toBeUndefined() + expect(serverError).toEqual('Plan limit exceeded') // Verify order of operations: plan limit check happens first expect(mocks.applyUserPlanLimit).toHaveBeenCalled() @@ -140,12 +145,13 @@ describe('inviteUserAction', () => { }) it('successfully invites user with valid input', async () => { - const [data, error] = await inviteUserAction({ + const { data, serverError, validationErrors } = await inviteUserAction({ email: 'valid@example.com', name: 'Valid User', }) - expect(error).toBeNull() + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(mocks.inviteUser).toHaveBeenCalledWith({ email: 'valid@example.com', @@ -156,20 +162,20 @@ describe('inviteUserAction', () => { }) it('passes through invitation service errors', async () => { - const inviteError = new Error('User already exists') + const inviteError = new LatitudeError('User already exists') mocks.inviteUser.mockResolvedValue({ unwrap: () => { throw inviteError }, }) - const [data, error] = await inviteUserAction({ + const { data, serverError } = await inviteUserAction({ email: 'test@example.com', name: 'Test User', }) - expect(data).toBeNull() - expect(error).toBeDefined() + expect(data).toBeUndefined() + expect(serverError).toBe('User already exists') }) }) diff --git a/apps/web/src/actions/user/invite.ts b/apps/web/src/actions/user/invite.ts index 79836fdaa1..c997985086 100644 --- a/apps/web/src/actions/user/invite.ts +++ b/apps/web/src/actions/user/invite.ts @@ -6,21 +6,15 @@ import { applyUserPlanLimit } from '@latitude-data/core/services/subscriptions/l import { authProcedure } from '../procedures' export const inviteUserAction = authProcedure - .createServerAction() - .input( - z.object({ - email: z.string(), - name: z.string(), - }), - ) - .handler(async ({ input, ctx }) => { + .inputSchema(z.object({ email: z.email(), name: z.string() })) + .action(async ({ parsedInput, ctx }) => { await applyUserPlanLimit({ workspace: ctx.workspace }).then((r) => r.unwrap(), ) return await inviteUser({ - email: input.email, - name: input.name, + email: parsedInput.email, + name: parsedInput.name, workspace: ctx.workspace, author: ctx.user, }).then((r) => r.unwrap()) diff --git a/apps/web/src/actions/user/loginAction.ts b/apps/web/src/actions/user/loginAction.ts index ebef265d29..33c1da6a0a 100644 --- a/apps/web/src/actions/user/loginAction.ts +++ b/apps/web/src/actions/user/loginAction.ts @@ -3,7 +3,6 @@ import { getFirstWorkspace, getUserFromCredentials } from '$/data-access' import { ROUTES } from '$/services/routes' import { createMagicLinkToken } from '@latitude-data/core/services/magicLinkTokens/create' -import { redirect } from 'next/navigation' import { z } from 'zod' import { setSession } from '$/services/auth/setSession' @@ -11,18 +10,19 @@ import { isLatitudeUrl } from '@latitude-data/constants' import { NotFoundError } from '@latitude-data/constants/errors' import { env } from '@latitude-data/env' import { errorHandlingProcedure } from '../procedures' +import { frontendRedirect } from '$/lib/frontendRedirect' export const loginAction = errorHandlingProcedure - .createServerAction() - .input( + .inputSchema( z.object({ - email: z.string().email(), + email: z.email(), returnTo: z.string().optional(), }), - { type: 'formData' }, ) - .handler(async ({ input }) => { - const { user } = await getUserFromCredentials(input).then((r) => r.unwrap()) + .action(async ({ parsedInput }) => { + const { user } = await getUserFromCredentials(parsedInput).then((r) => + r.unwrap(), + ) if (env.DISABLE_EMAIL_AUTHENTICATION) { if (!user) throw new NotFoundError('User not found') @@ -40,16 +40,15 @@ export const loginAction = errorHandlingProcedure }, }) - if (!input.returnTo || !isLatitudeUrl(input.returnTo)) { - return redirect(ROUTES.dashboard.root) + if (!parsedInput.returnTo || !isLatitudeUrl(parsedInput.returnTo)) { + return frontendRedirect(ROUTES.dashboard.root) } - return redirect(input.returnTo) + return frontendRedirect(parsedInput.returnTo) } else { - await createMagicLinkToken({ user, returnTo: input.returnTo }).then((r) => - r.unwrap(), + await createMagicLinkToken({ user, returnTo: parsedInput.returnTo }).then( + (r) => r.unwrap(), ) - - redirect(ROUTES.auth.magicLinkSent(user.email)) + return frontendRedirect(ROUTES.auth.magicLinkSent(user.email)) } }) diff --git a/apps/web/src/actions/user/logoutAction.ts b/apps/web/src/actions/user/logoutAction.ts index 27bc1e9a1a..29d0c06ff7 100644 --- a/apps/web/src/actions/user/logoutAction.ts +++ b/apps/web/src/actions/user/logoutAction.ts @@ -3,12 +3,10 @@ import { authProcedure } from '$/actions/procedures' import { removeSession } from '$/services/auth/removeSession' import { ROUTES } from '$/services/routes' -import { redirect } from 'next/navigation' +import { frontendRedirect } from '$/lib/frontendRedirect' -export const logoutAction = authProcedure - .createServerAction() - .handler(async ({ ctx }) => { - await removeSession({ session: ctx.session }) +export const logoutAction = authProcedure.action(async ({ ctx }) => { + await removeSession({ session: ctx.session }) - redirect(ROUTES.auth.login) - }) + return frontendRedirect(ROUTES.auth.login) +}) diff --git a/apps/web/src/actions/user/refreshWebsocketTokenAction.ts b/apps/web/src/actions/user/refreshWebsocketTokenAction.ts index 809f6a2b8e..2d4151f589 100644 --- a/apps/web/src/actions/user/refreshWebsocketTokenAction.ts +++ b/apps/web/src/actions/user/refreshWebsocketTokenAction.ts @@ -6,9 +6,8 @@ import { cookies } from 'next/headers' import { authProcedure } from '../procedures' -export const refreshWebesocketTokenAction = authProcedure - .createServerAction() - .handler(async ({ ctx: { user, workspace } }) => { +export const refreshWebesocketTokenAction = authProcedure.action( + async ({ ctx: { user, workspace } }) => { const cks = await cookies() const refreshWebsocketCookie = cks.get('websocketRefresh') const refreshToken = refreshWebsocketCookie?.value @@ -29,4 +28,5 @@ export const refreshWebesocketTokenAction = authProcedure }) return { success: true } - }) + }, +) diff --git a/apps/web/src/actions/user/setupAction.test.ts b/apps/web/src/actions/user/setupAction.test.ts index 32fca82786..01b6d3daea 100644 --- a/apps/web/src/actions/user/setupAction.test.ts +++ b/apps/web/src/actions/user/setupAction.test.ts @@ -6,7 +6,6 @@ import { setupAction } from './setupAction' const mocks = vi.hoisted(() => { return { createMagicLinkToken: vi.fn(), - redirect: vi.fn(), } }) @@ -14,120 +13,115 @@ vi.mock('@latitude-data/core/services/magicLinkTokens/create', () => ({ createMagicLinkToken: mocks.createMagicLinkToken, })) -vi.mock('next/navigation', () => ({ - redirect: mocks.redirect, -})) - describe('setupAction', () => { beforeEach(() => { mocks.createMagicLinkToken.mockResolvedValue({ unwrap: () => ({}) }) - mocks.redirect.mockImplementation(() => {}) }) // TODO: unskip once we have email signup again it.skip('accepts a valid email', async () => { - const [_, error] = await setupAction({ - // @ts-expect-error - testing + const { data, serverError, validationErrors } = await setupAction({ name: 'John Doe', email: 'john@example.com', companyName: 'Acme Inc', }) - expect(error).toBeNull() + expect(validationErrors).toBeUndefined() + expect(serverError).toBeUndefined() expect(mocks.createMagicLinkToken).toHaveBeenCalled() - expect(mocks.redirect).toHaveBeenCalled() + expect(data.frontendRedirect).toBeDefined() }) it('rejects an email that is already in use', async () => { await factories.createUser({ email: 'existing@example.com' }) - const [data, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors } = await setupAction({ name: 'John Doe', email: 'existing@example.com', companyName: 'Acme Inc', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Email is already in use') - expect(data).toBeNull() + expect(data).toBeUndefined() + expect(validationErrors).toBeDefined() + expect(validationErrors?.fieldErrors.email).toContain( + 'Email is already in use', + ) }) it('rejects an email with a plus sign and number', async () => { - const [data, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors } = await setupAction({ name: 'John Doe', email: 'john+123@example.com', companyName: 'Acme Inc', }) - expect(error).not.toBeNull() - expect(error!.message).toContain('Email is not valid') - expect(data).toBeNull() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors.email).toContain('Email is not valid') }) // TODO: unskip once we have email signup again it.skip('accepts an email with a subdomain', async () => { - const [_, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors, serverError } = await setupAction({ name: 'John Doe', email: 'john@subdomain.example.com', companyName: 'Acme Inc', }) - expect(error).toBeNull() + expect(validationErrors).toBeUndefined() + expect(serverError).toBeUndefined() expect(mocks.createMagicLinkToken).toHaveBeenCalled() - expect(mocks.redirect).toHaveBeenCalled() + expect(data.frontendRedirect).toBeDefined() }) it('rejects an email with invalid characters', async () => { - const [data, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors } = await setupAction({ name: 'John Doe', email: 'john@exa!mple.com', companyName: 'Acme Inc', }) - expect(error).toBeDefined() - expect(data).toBeNull() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors.email).toContain( + 'Invalid email address', + ) }) it('rejects an email without a domain', async () => { - const [data, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors } = await setupAction({ name: 'John Doe', email: 'johndoe', companyName: 'Acme Inc', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Invalid email') - expect(data).toBeNull() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors.email).toContain( + 'Invalid email address', + ) }) it('rejects an empty name', async () => { - const [data, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors } = await setupAction({ name: '', email: 'john@example.com', companyName: 'Acme Inc', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Name is a required field') - expect(data).toBeNull() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors.name).toContain( + 'Name is a required field', + ) }) it('rejects an empty company name', async () => { - const [data, error] = await setupAction({ - // @ts-expect-error - testing + const { data, validationErrors } = await setupAction({ name: 'John Doe', email: 'john@example.com', companyName: '', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Workspace name is a required field') - expect(data).toBeNull() + expect(data).toBeUndefined() + expect(validationErrors?.fieldErrors.companyName).toContain( + 'Workspace name is a required field', + ) }) }) diff --git a/apps/web/src/actions/user/setupAction.ts b/apps/web/src/actions/user/setupAction.ts index 8ab8a89b55..5b5e4514aa 100644 --- a/apps/web/src/actions/user/setupAction.ts +++ b/apps/web/src/actions/user/setupAction.ts @@ -1,48 +1,44 @@ 'use server' +import { z } from 'zod' import { setSession } from '$/services/auth/setSession' import { ROUTES } from '$/services/routes' import setupService from '$/services/user/setupService' import { isLatitudeUrl } from '@latitude-data/constants' import { unsafelyFindUserByEmail } from '@latitude-data/core/data-access' -import { redirect } from 'next/navigation' -import { z } from 'zod' import { errorHandlingProcedure } from '../procedures' +import { frontendRedirect } from '$/lib/frontendRedirect' export const setupAction = errorHandlingProcedure - .createServerAction() - .input( - async () => { - return z.object({ - returnTo: z.string().optional(), - source: z.string().optional(), - name: z.string().min(1, { message: 'Name is a required field' }), - email: z - .string() - .email() - .refine( - async (email) => { - const existingUser = await unsafelyFindUserByEmail(email) - return !existingUser - }, - { message: 'Email is already in use' }, - ) - .refine( - async (email) => - !email.match(/^[A-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Z0-9.-]+$/) && - !email.match(/^[^+]+\+\d+@[A-Z0-9.-]+$/i), - { message: 'Email is not valid' }, - ), - companyName: z - .string() - .min(1, { message: 'Workspace name is a required field' }), - }) - }, - { type: 'formData' }, + .inputSchema( + z.object({ + returnTo: z.string().optional(), + source: z.string().optional(), + name: z.string().min(1, { error: 'Name is a required field' }), + email: z + .string() + .pipe(z.email()) + .refine( + async (email) => { + const existingUser = await unsafelyFindUserByEmail(email) + return !existingUser + }, + { error: 'Email is already in use' }, + ) + .refine( + async (email) => + !email.match(/^[A-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Z0-9.-]+$/) && + !email.match(/^[^+]+\+\d+@[A-Z0-9.-]+$/i), + { error: 'Email is not valid' }, + ), + companyName: z + .string() + .min(1, { error: 'Workspace name is a required field' }), + }), ) - .handler(async ({ input }) => { - const result = await setupService(input) + .action(async ({ parsedInput }) => { + const result = await setupService(parsedInput) const { workspace, user } = result.unwrap() await setSession({ @@ -55,9 +51,9 @@ export const setupAction = errorHandlingProcedure }, }) - if (!input.returnTo || !isLatitudeUrl(input.returnTo)) { - return redirect(ROUTES.dashboard.root) + if (!parsedInput.returnTo || !isLatitudeUrl(parsedInput.returnTo)) { + return frontendRedirect(ROUTES.dashboard.root) } - return redirect(input.returnTo) + return frontendRedirect(parsedInput.returnTo) }) diff --git a/apps/web/src/actions/user/update.ts b/apps/web/src/actions/user/update.ts index 4e2a8ef600..bccc91dddd 100644 --- a/apps/web/src/actions/user/update.ts +++ b/apps/web/src/actions/user/update.ts @@ -5,15 +5,14 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const updateUserAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ devMode: z.boolean(), }), ) - .handler(async ({ ctx, input }) => { + .action(async ({ ctx, parsedInput }) => { const result = await updateUser(ctx.user, { - devMode: input.devMode, + devMode: parsedInput.devMode, }).then((r) => r.unwrap()) return result diff --git a/apps/web/src/actions/webhooks/create.test.ts b/apps/web/src/actions/webhooks/create.test.ts index b6ae8a64f5..108d34b5cf 100644 --- a/apps/web/src/actions/webhooks/create.test.ts +++ b/apps/web/src/actions/webhooks/create.test.ts @@ -46,12 +46,12 @@ describe('createWebhookAction', () => { it('errors when the user is not authenticated', async () => { mocks.getSession.mockResolvedValue(null) - const [_, error] = await createWebhookAction({ + const { serverError } = await createWebhookAction({ name: 'Test Webhook', url: 'https://test.com', }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) }) @@ -65,12 +65,15 @@ describe('createWebhookAction', () => { }) it('successfully creates a webhook', async () => { - const [data, error] = await createWebhookAction({ - name: 'Test Webhook', - url: 'https://test.com', - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await createWebhookAction( + { + name: 'Test Webhook', + url: 'https://test.com', + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.name).toBe('Test Webhook') expect(data!.url).toBe('https://test.com') @@ -81,63 +84,65 @@ describe('createWebhookAction', () => { }) it('successfully creates a webhook with project IDs', async () => { - const [data, error] = await createWebhookAction({ - name: 'Test Webhook', - url: 'https://test.com', - projectIds: JSON.stringify([1, 2, 3]), - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await createWebhookAction( + { + name: 'Test Webhook', + url: 'https://test.com', + projectIds: JSON.stringify([1, 2, 3]), + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.projectIds).toEqual([1, 2, 3]) expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('successfully creates an inactive webhook', async () => { - const [data, error] = await createWebhookAction({ - name: 'Test Webhook', - url: 'https://test.com', - isActive: 'false', - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await createWebhookAction( + { + name: 'Test Webhook', + url: 'https://test.com', + isActive: 'false', + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.isActive).toBe(false) expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('rejects invalid project IDs format', async () => { - const [_, error] = await createWebhookAction({ + const { serverError } = await createWebhookAction({ name: 'Test Webhook', url: 'https://test.com', projectIds: 'invalid-json', }) - expect(error).toBeDefined() - expect(error!.name).toEqual('BadRequestError') - expect(error!.message).toContain('Invalid project IDs') + expect(serverError).toEqual('Invalid project IDs') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('rejects invalid URL format', async () => { - const [_, error] = await createWebhookAction({ + const { validationErrors } = await createWebhookAction({ name: 'Test Webhook', url: 'not-a-url', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Invalid URL format') + expect(validationErrors?.fieldErrors.url).toContain('Invalid URL format') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('rejects empty name', async () => { - const [_, error] = await createWebhookAction({ + const { validationErrors } = await createWebhookAction({ name: '', url: 'https://test.com', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Name is required') + expect(validationErrors?.fieldErrors.name).toContain('Name is required') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) }) diff --git a/apps/web/src/actions/webhooks/create.ts b/apps/web/src/actions/webhooks/create.ts index 6cd1cff28d..59a9dc1f6e 100644 --- a/apps/web/src/actions/webhooks/create.ts +++ b/apps/web/src/actions/webhooks/create.ts @@ -6,29 +6,30 @@ import { createWebhook } from '@latitude-data/core/services/webhooks/createWebho import { BadRequestError } from '@latitude-data/constants/errors' export const createWebhookAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ - name: z.string().min(1, { message: 'Name is required' }), - url: z.string().url({ message: 'Invalid URL format' }), + name: z.string().min(1, { error: 'Name is required' }), + url: z.string().pipe(z.url({ error: 'Invalid URL format' })), projectIds: z.string().optional(), isActive: z.string().optional().default('true'), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { let projectIds: number[] | undefined try { - projectIds = input.projectIds ? JSON.parse(input.projectIds) : undefined + projectIds = parsedInput.projectIds + ? JSON.parse(parsedInput.projectIds) + : undefined } catch (error) { throw new BadRequestError('Invalid project IDs') } const result = await createWebhook({ workspaceId: ctx.workspace.id, - name: input.name, - url: input.url, + name: parsedInput.name, + url: parsedInput.url, projectIds: projectIds || [], - isActive: input.isActive === 'true', + isActive: parsedInput.isActive === 'true', }) return result.unwrap() diff --git a/apps/web/src/actions/webhooks/delete.test.ts b/apps/web/src/actions/webhooks/delete.test.ts index a99ac4ed97..b3383f9fdc 100644 --- a/apps/web/src/actions/webhooks/delete.test.ts +++ b/apps/web/src/actions/webhooks/delete.test.ts @@ -53,11 +53,11 @@ describe('deleteWebhookAction', () => { it('errors when the user is not authenticated', async () => { mocks.getSession.mockResolvedValue(null) - const [_, error] = await deleteWebhookAction({ + const { serverError } = await deleteWebhookAction({ id: webhook.id, }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) }) @@ -71,23 +71,25 @@ describe('deleteWebhookAction', () => { }) it('successfully deletes a webhook', async () => { - const [data, error] = await deleteWebhookAction({ - id: webhook.id, - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await deleteWebhookAction( + { + id: webhook.id, + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.id).toEqual(webhook.id) expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('returns error when webhook is not found', async () => { - const [_, error] = await deleteWebhookAction({ + const { serverError } = await deleteWebhookAction({ id: 999999, }) - expect(error).toBeDefined() - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Webhook not found') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) }) diff --git a/apps/web/src/actions/webhooks/delete.ts b/apps/web/src/actions/webhooks/delete.ts index 41e30f2607..0981fdc21d 100644 --- a/apps/web/src/actions/webhooks/delete.ts +++ b/apps/web/src/actions/webhooks/delete.ts @@ -1,20 +1,14 @@ 'use server' import { z } from 'zod' -import { authProcedure } from '../procedures' import { deleteWebhook } from '@latitude-data/core/services/webhooks/deleteWebhook' import { getWebhook } from '@latitude-data/core/services/webhooks/getWebhook' +import { authProcedure } from '../procedures' export const deleteWebhookAction = authProcedure - .createServerAction() - .input( - z.object({ - id: z.number(), - }), - ) - .handler(async ({ input, ctx }) => { - // First get the webhook instance - const webhook = await getWebhook(input.id, ctx.workspace).then((r) => + .inputSchema(z.object({ id: z.number() })) + .action(async ({ parsedInput, ctx }) => { + const webhook = await getWebhook(parsedInput.id, ctx.workspace).then((r) => r.unwrap(), ) diff --git a/apps/web/src/actions/webhooks/testWebhook.ts b/apps/web/src/actions/webhooks/testWebhook.ts index dadbb0cf4a..633b6e660b 100644 --- a/apps/web/src/actions/webhooks/testWebhook.ts +++ b/apps/web/src/actions/webhooks/testWebhook.ts @@ -5,15 +5,14 @@ import { authProcedure } from '../procedures' import { testWebhookEndpoint } from '@latitude-data/core/services/webhooks/testWebhook' export const testWebhookAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ - url: z.string().url({ message: 'Invalid URL format' }), + url: z.string().pipe(z.url({ error: 'Invalid URL format' })), }), ) - .handler(async ({ input }) => { + .action(async ({ parsedInput }) => { const result = await testWebhookEndpoint({ - url: input.url, + url: parsedInput.url, }) return result.unwrap() diff --git a/apps/web/src/actions/webhooks/update.test.ts b/apps/web/src/actions/webhooks/update.test.ts index 872609bd9a..01bd5020be 100644 --- a/apps/web/src/actions/webhooks/update.test.ts +++ b/apps/web/src/actions/webhooks/update.test.ts @@ -53,12 +53,12 @@ describe('updateWebhookAction', () => { it('errors when the user is not authenticated', async () => { mocks.getSession.mockResolvedValue(null) - const [_, error] = await updateWebhookAction({ + const { serverError } = await updateWebhookAction({ id: webhook.id, name: 'Updated Webhook', }) - expect(error!.name).toEqual('UnauthorizedError') + expect(serverError).toEqual('Unauthorized') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) }) @@ -72,12 +72,15 @@ describe('updateWebhookAction', () => { }) it('successfully updates a webhook name', async () => { - const [data, error] = await updateWebhookAction({ - id: webhook.id, - name: 'Updated Webhook', - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await updateWebhookAction( + { + id: webhook.id, + name: 'Updated Webhook', + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.name).toEqual('Updated Webhook') expect(data!.url).toEqual(webhook.url) @@ -85,83 +88,87 @@ describe('updateWebhookAction', () => { }) it('successfully updates a webhook URL', async () => { - const [data, error] = await updateWebhookAction({ - id: webhook.id, - url: 'https://new-example.com/webhook', - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await updateWebhookAction( + { + id: webhook.id, + url: 'https://new-example.com/webhook', + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.url).toEqual('https://new-example.com/webhook') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('successfully updates project IDs', async () => { - const [data, error] = await updateWebhookAction({ - id: webhook.id, - projectIds: JSON.stringify([1, 2, 3]), - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await updateWebhookAction( + { + id: webhook.id, + projectIds: JSON.stringify([1, 2, 3]), + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.projectIds).toEqual([1, 2, 3]) expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('successfully updates active status', async () => { - const [data, error] = await updateWebhookAction({ - id: webhook.id, - isActive: 'false', - }) - - expect(error).toBeNull() + const { data, serverError, validationErrors } = await updateWebhookAction( + { + id: webhook.id, + isActive: 'false', + }, + ) + + expect(serverError).toBeUndefined() + expect(validationErrors).toBeUndefined() expect(data).toBeDefined() expect(data!.isActive).toBe(false) expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('rejects invalid project IDs format', async () => { - const [_, error] = await updateWebhookAction({ + const { serverError } = await updateWebhookAction({ id: webhook.id, projectIds: 'invalid-json', }) - expect(error).toBeDefined() - expect(error!.name).toEqual('BadRequestError') - expect(error!.message).toContain('Invalid project IDs') + expect(serverError).toEqual('Invalid project IDs') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('rejects invalid URL format', async () => { - const [_, error] = await updateWebhookAction({ + const { validationErrors } = await updateWebhookAction({ id: webhook.id, url: 'not-a-url', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Invalid URL format') + expect(validationErrors?.fieldErrors.url).toContain('Invalid URL format') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('rejects empty name', async () => { - const [_, error] = await updateWebhookAction({ + const { validationErrors } = await updateWebhookAction({ id: webhook.id, name: '', }) - expect(error).toBeDefined() - expect(error!.message).toContain('Name is required') + expect(validationErrors?.fieldErrors.name).toContain('Name is required') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) it('returns error when webhook is not found', async () => { - const [_, error] = await updateWebhookAction({ + const { serverError } = await updateWebhookAction({ id: -1, name: 'Updated Webhook', }) - expect(error).toBeDefined() - expect(error!.name).toEqual('NotFoundError') + expect(serverError).toEqual('Webhook not found') expect(mocks.publisher.publishLater).not.toHaveBeenCalled() }) }) diff --git a/apps/web/src/actions/webhooks/update.ts b/apps/web/src/actions/webhooks/update.ts index ee3589e158..8256e6407a 100644 --- a/apps/web/src/actions/webhooks/update.ts +++ b/apps/web/src/actions/webhooks/update.ts @@ -7,35 +7,39 @@ import { getWebhook } from '@latitude-data/core/services/webhooks/getWebhook' import { BadRequestError } from '@latitude-data/constants/errors' export const updateWebhookAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ id: z.coerce.number(), - name: z.string().min(1, { message: 'Name is required' }).optional(), - url: z.string().url({ message: 'Invalid URL format' }).optional(), + name: z.string().min(1, { error: 'Name is required' }).optional(), + url: z + .string() + .pipe(z.url({ error: 'Invalid URL format' })) + .optional(), projectIds: z.string().optional(), isActive: z.string().optional(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { let projectIds: number[] | undefined try { - projectIds = input.projectIds ? JSON.parse(input.projectIds) : undefined + projectIds = parsedInput.projectIds + ? JSON.parse(parsedInput.projectIds) + : undefined } catch (error) { throw new BadRequestError('Invalid project IDs') } // First get the webhook instance - const webhook = await getWebhook(input.id, ctx.workspace).then((r) => + const webhook = await getWebhook(parsedInput.id, ctx.workspace).then((r) => r.unwrap(), ) const result = await updateWebhook({ webhook, - name: input.name, - url: input.url, + name: parsedInput.name, + url: parsedInput.url, projectIds, - isActive: input.isActive === 'true', + isActive: parsedInput.isActive === 'true', }) return result.unwrap() diff --git a/apps/web/src/actions/workspaceOnboarding/complete.ts b/apps/web/src/actions/workspaceOnboarding/complete.ts index 575a2822d9..996e136f87 100644 --- a/apps/web/src/actions/workspaceOnboarding/complete.ts +++ b/apps/web/src/actions/workspaceOnboarding/complete.ts @@ -7,9 +7,8 @@ import { authProcedure } from '../procedures' /** * Mark onboarding as complete */ -export const completeOnboardingAction = authProcedure - .createServerAction() - .handler(async ({ ctx }) => { +export const completeOnboardingAction = authProcedure.action( + async ({ ctx }) => { const onboarding = await getWorkspaceOnboarding({ workspace: ctx.workspace, }).then((r) => r.unwrap()) @@ -17,4 +16,5 @@ export const completeOnboardingAction = authProcedure return await markWorkspaceOnboardingComplete({ onboarding, }).then((r) => r.unwrap()) - }) + }, +) diff --git a/apps/web/src/actions/workspaceOnboarding/moveNextStep.ts b/apps/web/src/actions/workspaceOnboarding/moveNextStep.ts index beb85c45dd..bc91cb0ed6 100644 --- a/apps/web/src/actions/workspaceOnboarding/moveNextStep.ts +++ b/apps/web/src/actions/workspaceOnboarding/moveNextStep.ts @@ -7,9 +7,8 @@ import { moveNextOnboardingStep } from '@latitude-data/core/services/workspaceOn /** * Move to the next onboarding step */ -export const moveNextOnboardingStepAction = authProcedure - .createServerAction() - .handler(async ({ ctx }) => { +export const moveNextOnboardingStepAction = authProcedure.action( + async ({ ctx }) => { const onboarding = await getWorkspaceOnboarding({ workspace: ctx.workspace, }).then((r) => r.unwrap()) @@ -19,4 +18,5 @@ export const moveNextOnboardingStepAction = authProcedure }).then((r) => r.unwrap()) return nextOnboardingStep - }) + }, +) diff --git a/apps/web/src/actions/workspaces/setDefaultProvider.ts b/apps/web/src/actions/workspaces/setDefaultProvider.ts index 563684bd74..c140250eba 100644 --- a/apps/web/src/actions/workspaces/setDefaultProvider.ts +++ b/apps/web/src/actions/workspaces/setDefaultProvider.ts @@ -7,22 +7,21 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const setDefaultProviderAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ workspaceId: z.number(), defaultProviderId: z.number().nullable(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const userId = ctx.session.userId const workspacesScope = new WorkspacesRepository(userId) const workspace = await workspacesScope - .find(input.workspaceId) + .find(parsedInput.workspaceId) .then((r) => r.unwrap()) const updatedWorkspace = await updateWorkspace(workspace, { - defaultProviderId: input.defaultProviderId, + defaultProviderId: parsedInput.defaultProviderId, }).then((r) => r.unwrap()) return updatedWorkspace diff --git a/apps/web/src/actions/workspaces/switch.ts b/apps/web/src/actions/workspaces/switch.ts index 234e7b0c79..2e4ccc37f4 100644 --- a/apps/web/src/actions/workspaces/switch.ts +++ b/apps/web/src/actions/workspaces/switch.ts @@ -9,19 +9,18 @@ import { cookies } from 'next/headers' import { removeSession, Session } from '$/services/auth/removeSession' export const switchWorkspaceAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ workspaceId: z.number(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const userId = ctx.session.userId const workspacesScope = new WorkspacesRepository(userId) // Verify the user has access to this workspace const workspace = await workspacesScope - .find(input.workspaceId) + .find(parsedInput.workspaceId) .then((r) => r.unwrap()) // Get the current user diff --git a/apps/web/src/actions/workspaces/update.ts b/apps/web/src/actions/workspaces/update.ts index 2c35e0df1a..06f29653e7 100644 --- a/apps/web/src/actions/workspaces/update.ts +++ b/apps/web/src/actions/workspaces/update.ts @@ -7,22 +7,21 @@ import { z } from 'zod' import { authProcedure } from '../procedures' export const updateWorkspaceAction = authProcedure - .createServerAction() - .input( + .inputSchema( z.object({ workspaceId: z.number(), name: z.string(), }), ) - .handler(async ({ input, ctx }) => { + .action(async ({ parsedInput, ctx }) => { const userId = ctx.session.userId const workspacesScope = new WorkspacesRepository(userId) const workspace = await workspacesScope - .find(input.workspaceId) + .find(parsedInput.workspaceId) .then((r) => r.unwrap()) const updatedWorkspace = await updateWorkspace(workspace, { - name: input.name, + name: parsedInput.name, }).then((r) => r.unwrap()) return updatedWorkspace diff --git a/apps/web/src/app/(actions)/actions/[actionType]/_lib/index.tsx b/apps/web/src/app/(actions)/actions/[actionType]/_lib/index.tsx index dba9de336a..d3817b811c 100644 --- a/apps/web/src/app/(actions)/actions/[actionType]/_lib/index.tsx +++ b/apps/web/src/app/(actions)/actions/[actionType]/_lib/index.tsx @@ -42,8 +42,15 @@ export function ClientPage({ setTimeout(async () => { try { const [result, error] = await executeBackendAction({ type, parameters }) - if (error) setError(error) - if (result) await executeFrontendAction({ type, parameters: result }) + + if (error) { + setError(error) + } else if (result) { + await executeFrontendAction({ + type, + parameters: result as ActionFrontendParameters, + }) + } setTimeout(() => setEnded(true), 5000) } catch (error) { setError(error as Error) diff --git a/apps/web/src/app/(actions)/actions/[actionType]/_lib/shared.ts b/apps/web/src/app/(actions)/actions/[actionType]/_lib/shared.ts index 63debeb1b4..ff184ee14d 100644 --- a/apps/web/src/app/(actions)/actions/[actionType]/_lib/shared.ts +++ b/apps/web/src/app/(actions)/actions/[actionType]/_lib/shared.ts @@ -9,7 +9,7 @@ import { useRouter } from 'next/navigation' import { z } from 'zod' // prettier-ignore -type ZodSchema = z.ZodObject +type ZodSchema<_T = any> = z.ZodObject export type ActionExecuteArgs = { parameters: ActionFrontendParameters diff --git a/apps/web/src/app/(admin)/backoffice/rewards/page.tsx b/apps/web/src/app/(admin)/backoffice/rewards/page.tsx index af4da30005..bcbc600246 100644 --- a/apps/web/src/app/(admin)/backoffice/rewards/page.tsx +++ b/apps/web/src/app/(admin)/backoffice/rewards/page.tsx @@ -1,6 +1,6 @@ 'use client' import { REWARD_CONFIGS } from '$/components/layouts/AppLayout/Header/Rewards/Content/RewardMenu/RewardConfigs' -import usePendingRewardClaims from '$/stores/pendingRewardClaims' +import { usePendingRewardClaims } from '$/stores/admin/pendingRewardClaims' import { RewardType } from '@latitude-data/core/browser' import { Button } from '@latitude-data/web-ui/atoms/Button' import { Icon, IconName } from '@latitude-data/web-ui/atoms/Icons' diff --git a/apps/web/src/app/(private)/dashboard/_components/ProjectsTable/RenameProjectModal.tsx b/apps/web/src/app/(private)/dashboard/_components/ProjectsTable/RenameProjectModal.tsx index b99ca98d87..4a0cc6211a 100644 --- a/apps/web/src/app/(private)/dashboard/_components/ProjectsTable/RenameProjectModal.tsx +++ b/apps/web/src/app/(private)/dashboard/_components/ProjectsTable/RenameProjectModal.tsx @@ -1,4 +1,3 @@ -'use client' import { Project } from '@latitude-data/core/browser' import { Button } from '@latitude-data/web-ui/atoms/Button' import { FormWrapper } from '@latitude-data/web-ui/atoms/FormWrapper' diff --git a/apps/web/src/app/(private)/datasets/_components/DeleteDatasetModal/index.tsx b/apps/web/src/app/(private)/datasets/_components/DeleteDatasetModal/index.tsx index 4f4b202711..dc90ef033a 100644 --- a/apps/web/src/app/(private)/datasets/_components/DeleteDatasetModal/index.tsx +++ b/apps/web/src/app/(private)/datasets/_components/DeleteDatasetModal/index.tsx @@ -1,6 +1,5 @@ import { Dataset } from '@latitude-data/core/browser' import { ReactStateDispatch } from '@latitude-data/web-ui/commonTypes' -import { destroyDatasetAction } from '$/actions/datasets/destroy' import DestroyModal from '$/components/modals/DestroyModal' import useDatasets from '$/stores/datasets' import { useRouter } from 'next/navigation' @@ -21,7 +20,7 @@ export default function DeleteDatasetModal({ if (!dataset) return null return ( - + !open && setDataset(null)} diff --git a/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/index.tsx b/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/index.tsx index ef4d992a61..62942c2216 100644 --- a/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/index.tsx +++ b/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/index.tsx @@ -1,6 +1,5 @@ import { FormEvent } from 'react' -import { useServerAction } from 'zsa-react' import { useToast } from '@latitude-data/web-ui/atoms/Toast' import { generateDatasetAction } from '$/actions/datasets/generateDataset' import { useNavigate } from '$/hooks/useNavigate' @@ -8,6 +7,7 @@ import { ROUTES } from '$/services/routes' import useDatasets from '$/stores/datasets' import { GenerateDatasetModalComponent } from './GenerateDatasetModalComponent' import { useDatasetPreviewModal } from './useDatasetPreviewModal' +import useLatitudeAction from '$/hooks/useLatitudeAction' export function GenerateDatasetModal({ open, @@ -29,11 +29,11 @@ export function GenerateDatasetModal({ execute: runGenerateAction, isPending: generateIsLoading, error: generateError, - } = useServerAction(generateDatasetAction, { + } = useLatitudeAction(generateDatasetAction, { onError: (error) => { toast({ title: 'Failed to generate dataset', - description: error.err.message, + description: error.message, variant: 'destructive', }) }, diff --git a/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/useDatasetPreviewModal.ts b/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/useDatasetPreviewModal.ts index 4c94d04564..bbb77f80df 100644 --- a/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/useDatasetPreviewModal.ts +++ b/apps/web/src/app/(private)/datasets/_components/RootHeader/GenerateDatasetModal/GenerateDatasetModal/useDatasetPreviewModal.ts @@ -1,24 +1,25 @@ import { ChangeEvent, useCallback, useEffect, useState } from 'react' -import { useServerAction } from 'zsa-react' import { useToast } from '@latitude-data/web-ui/atoms/Toast' import { generateDatasetPreviewAction } from '$/actions/sdk/generateDatasetPreviewAction' +import useLatitudeAction from '$/hooks/useLatitudeAction' function usePreviewData() { const { toast } = useToast() const { - data, + result, execute: runPreviewAction, isPending: previewIsLoading, error: previewError, - } = useServerAction(generateDatasetPreviewAction, { + } = useLatitudeAction(generateDatasetPreviewAction, { onError: (error) => { toast({ title: 'Failed to generate dataset', - description: error.err.message, + description: error.message, variant: 'destructive', }) }, }) + const data = result.data return { explanation: data?.explanation, diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx index a3434742d2..ffd693af09 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/_components/Sidebar/CreateDraftCommitModal/index.tsx @@ -45,7 +45,7 @@ export default function DraftCommitModal({ const { error, data: input, action } = useFormAction(createDraft) const { project } = useCurrentProject() const router = useRouter() - const formattedErrors = error?.fieldErrors as Record + const formattedErrors = error?.fieldErrors return ( >(evaluation) const [options, setOptions] = useState(evaluation) - const [errors, setErrors] = - useState>() + const [errors, setErrors] = useState() const onUpdate = useCallback(async () => { if (isUpdatingEvaluation) return @@ -218,8 +218,9 @@ function EditEvaluation< settings: settings, options: options, }) - if (errors) setErrors(errors) - else { + if (errors) { + setErrors(errors) + } else { setErrors(undefined) setOpenUpdateModal(false) } diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsActions.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsActions.tsx index 17b4d7f0e6..501a6877ac 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsActions.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsActions.tsx @@ -1,6 +1,7 @@ import { useCurrentDocument } from '$/app/providers/DocumentProvider' -import EvaluationV2Form from '$/components/evaluations/EvaluationV2Form' -import { ActionErrors } from '$/hooks/useLatitudeAction' +import EvaluationV2Form, { + EvaluationV2FormErrors, +} from '$/components/evaluations/EvaluationV2Form' import { useNavigate } from '$/hooks/useNavigate' import { ROUTES } from '$/services/routes' import { useEvaluationsV2 } from '$/stores/evaluationsV2' @@ -143,14 +144,15 @@ function AddEvaluation({ const [options, setOptions] = useState>( DEFAULT_EVALUATION_OPTIONS, ) - const [errors, setErrors] = - useState>() + const [errors, setErrors] = useState() const onCreate = useCallback(async () => { if (isCreatingEvaluation) return const [result, errors] = await createEvaluation({ settings, options }) - if (errors) setErrors(errors) - else { + + if (errors) { + setErrors(errors) + } else if (result?.evaluation) { setSettings(DEFAULT_EVALUATION_SETTINGS) setOptions(DEFAULT_EVALUATION_OPTIONS) setErrors(undefined) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsGenerator.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsGenerator.tsx index ab232a6ddd..fb988a0788 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsGenerator.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsGenerator.tsx @@ -1,6 +1,7 @@ import { useCurrentDocument } from '$/app/providers/DocumentProvider' -import EvaluationV2Form from '$/components/evaluations/EvaluationV2Form' -import { ActionErrors } from '$/hooks/useLatitudeAction' +import EvaluationV2Form, { + EvaluationV2FormErrors, +} from '$/components/evaluations/EvaluationV2Form' import { useNavigate } from '$/hooks/useNavigate' import { ROUTES } from '$/services/routes' import { useEvaluationsV2 } from '$/stores/evaluationsV2' @@ -54,8 +55,7 @@ export function EvaluationsGenerator({ const [options, setOptions] = useState>( DEFAULT_EVALUATION_OPTIONS, ) - const [errors, setErrors] = - useState>() + const [errors, setErrors] = useState() const onGenerate = useCallback(async () => { if (isGeneratingEvaluation) return @@ -78,8 +78,9 @@ export function EvaluationsGenerator({ const onCreate = useCallback(async () => { if (isCreatingEvaluation || !settings) return const [result, errors] = await createEvaluation({ settings, options }) - if (errors) setErrors(errors) - else { + if (errors) { + setErrors(errors) + } else if (result?.evaluation) { setOpen(false) setInstructions(undefined) setSettings(undefined) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsTemplates.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsTemplates.tsx index 2832e742b2..b15d26b4ba 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsTemplates.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/evaluations/_components/EvaluationsTemplates.tsx @@ -86,6 +86,8 @@ export function EvaluationsTemplates({ }, }) if (errors) return + if (!result?.evaluation) return + setOpenUseModal(false) const { evaluation } = result diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/experiments/_components/ExperimentsComparison/ExperimentItem/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/experiments/_components/ExperimentsComparison/ExperimentItem/index.tsx index 573ea147fe..3e8b4ee232 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/experiments/_components/ExperimentsComparison/ExperimentItem/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/experiments/_components/ExperimentsComparison/ExperimentItem/index.tsx @@ -78,8 +78,8 @@ export function ExperimentItem({ const stopExperiment = useCallback(() => { if (!experiment) return execute({ - experimentUuid: experiment.uuid, projectId: project.id, + experimentUuid: experiment.uuid, commitUuid: commit.uuid, documentUuid: document.documentUuid, }) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/_components/DocumentLogs/SaveLogsAsDatasetModal/useSelectedLogs.ts b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/_components/DocumentLogs/SaveLogsAsDatasetModal/useSelectedLogs.ts index 996cdbaf5d..693b4cdd5d 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/_components/DocumentLogs/SaveLogsAsDatasetModal/useSelectedLogs.ts +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/_components/DocumentLogs/SaveLogsAsDatasetModal/useSelectedLogs.ts @@ -152,10 +152,10 @@ export function useSelectedLogs({ selectableState.clearSelections() previewModalState.onClose() }, - onError: ({ err }) => { + onError: (error) => { toast({ title: 'Error', - description: err.message, + description: error.message, variant: 'destructive', }) }, diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/upload/UploadLogModal.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/upload/UploadLogModal.tsx index 2593bec392..eb7aa987a8 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/upload/UploadLogModal.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/(withTabs)/logs/upload/UploadLogModal.tsx @@ -44,7 +44,7 @@ export default function UploadLogModal({ }, }) const { action, error } = useFormAction(execute) - const errors = error?.fieldErrors as Record + const errors = error?.fieldErrors return ( { setChanges(changes) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) @@ -38,7 +38,7 @@ export function useDocumentActions({ onSuccess: ({ data: changes }) => { setChanges(changes) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) @@ -54,7 +54,7 @@ export function useDocumentActions({ : commitBaseRoute.root router.push(route) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) @@ -70,7 +70,7 @@ export function useDocumentActions({ : commitBaseRoute.root router.push(route) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/history/_components/CommitsList/commitActions.ts b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/history/_components/CommitsList/commitActions.ts index 52dd0ac1a3..0f4efc445f 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/history/_components/CommitsList/commitActions.ts +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/history/_components/CommitsList/commitActions.ts @@ -21,7 +21,7 @@ export function useCommitActions({ commit }: { commit: Commit }) { onSuccess: ({ data: changes }) => { setChanges(changes) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) @@ -31,7 +31,7 @@ export function useCommitActions({ commit }: { commit: Commit }) { onSuccess: ({ data: changes }) => { setChanges(changes) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) @@ -45,7 +45,7 @@ export function useCommitActions({ commit }: { commit: Commit }) { .commits.detail({ uuid: commitUuid }).root, ) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) @@ -59,7 +59,7 @@ export function useCommitActions({ commit }: { commit: Commit }) { .commits.detail({ uuid: commitUuid }).documents.root, ) }, - onError: ({ err: error }) => setError(error.message), + onError: (error) => setError(error.message), }, ) diff --git a/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx b/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx index cf381a6395..e6e6c56e66 100644 --- a/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx +++ b/apps/web/src/app/(private)/settings/_components/Memberships/New/index.tsx @@ -14,7 +14,7 @@ export default function NewUser({ setOpen: (open: boolean) => void }) { const { invite } = useUsers() - const { data, action } = useFormAction(invite, { + const { isPending, error, data, action } = useFormAction(invite, { onSuccess: () => setOpen(false), }) return ( @@ -27,8 +27,13 @@ export default function NewUser({ footer={ <> - } @@ -40,14 +45,16 @@ export default function NewUser({ type='text' label='Name' name='name' - defaultValue={data?.name} + errors={error?.fieldErrors?.name} + defaultValue={data?.name ?? ''} placeholder='Jon Snow' /> diff --git a/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx b/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx index 827e96949d..dc6765f1c9 100644 --- a/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx +++ b/apps/web/src/app/(public)/invitations/[token]/InvitationForm/index.tsx @@ -7,7 +7,8 @@ import { FormWrapper } from '@latitude-data/web-ui/atoms/FormWrapper' import { Input } from '@latitude-data/web-ui/atoms/Input' import { useToast } from '@latitude-data/web-ui/atoms/Toast' import { acceptInvitationAction } from '$/actions/invitations/accept' -import { useServerAction } from 'zsa-react' +import { useFormAction } from '$/hooks/useFormAction' +import useLatitudeAction from '$/hooks/useLatitudeAction' export default function InvitationForm({ user, @@ -19,24 +20,20 @@ export default function InvitationForm({ footer: ReactNode }) { const { toast } = useToast() - const { isPending, error, executeFormAction } = useServerAction( - acceptInvitationAction, - { - onError: ({ err }) => { - if (err.code === 'ERROR') { - toast({ - title: 'Saving failed', - description: err.message, - variant: 'destructive', - }) - } - }, + const { execute, isPending } = useLatitudeAction(acceptInvitationAction) + const { error, action } = useFormAction(execute, { + onError: (error) => { + toast({ + title: 'Saving failed', + description: error.message, + variant: 'destructive', + }) }, - ) + }) const errors = error?.fieldErrors return ( -
+ { - if (err.code === 'ERROR') { + const { isPending, execute } = useLatitudeAction(loginAction) + const { error, action } = useFormAction(execute, { + onError: (error) => { + if (error.code === 'ERROR') { toast({ title: 'Error', - description: err.message, + description: error.message, variant: 'destructive', }) } @@ -33,7 +35,7 @@ export default function LoginForm({ }) const errors = error?.fieldErrors return ( - + {}, // We don't want the default toast message in this case }) - useEffect(() => { + useOnce(() => { setTimeout(() => execute({ token, returnTo }), 1000) - }, [execute, returnTo, token]) + }) return ( { if (err.code === 'ERROR') { toast({ @@ -54,7 +54,6 @@ export default function SetupForm({ autoComplete='name' label='Name' placeholder='Jon Snow' - // @ts-expect-error errors={errors?.name} defaultValue={data?.name || name} /> @@ -64,7 +63,6 @@ export default function SetupForm({ autoComplete='email' label='Email' placeholder='jon@winterfell.com' - // @ts-expect-error errors={errors?.email} defaultValue={data?.email || email} /> @@ -73,7 +71,6 @@ export default function SetupForm({ name='companyName' label='Workspace Name' placeholder='Acme Inc.' - // @ts-expect-error errors={errors?.companyName} defaultValue={data?.companyName || companyName} /> diff --git a/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/useChat.ts b/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/useChat.ts index db52a3df62..fa8e835425 100644 --- a/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/useChat.ts +++ b/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/useChat.ts @@ -9,7 +9,7 @@ import { } from '@latitude-data/core/browser' import { ReactStateDispatch } from '@latitude-data/web-ui/commonTypes' import { useCallback } from 'react' -import { readStreamableValue } from 'ai/rsc' +import { readStreamableValue } from '@ai-sdk/rsc' import { ChainEvent, ChainEventTypes } from '@latitude-data/constants' export function useChat({ diff --git a/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/usePrompt.ts b/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/usePrompt.ts index 1752fb4df5..156411a222 100644 --- a/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/usePrompt.ts +++ b/apps/web/src/app/(public)/share/d/[publishedDocumentUuid]/_components/SharedDocument/RunPrompt/usePrompt.ts @@ -8,7 +8,7 @@ import { StreamEventTypes, } from '@latitude-data/core/browser' import { runSharedPromptAction } from '$/actions/sdk/runSharedPromptAction' -import { readStreamableValue } from 'ai/rsc' +import { readStreamableValue } from '@ai-sdk/rsc' import { SetStateAction } from '@latitude-data/web-ui/commonTypes' import { ChainEvent, ChainEventTypes } from '@latitude-data/constants' diff --git a/apps/web/src/app/api/admin/rewards/pending/route.ts b/apps/web/src/app/api/admin/rewards/pending/route.ts new file mode 100644 index 0000000000..73ec8d8e41 --- /dev/null +++ b/apps/web/src/app/api/admin/rewards/pending/route.ts @@ -0,0 +1,13 @@ +import { NextRequest, NextResponse } from 'next/server' +import { errorHandler } from '$/middlewares/errorHandler' +import { adminHandler } from '$/middlewares/adminHandler' +import { findAllRewardClaimsPendingToValidate } from '@latitude-data/core/data-access' + +export const POST = errorHandler( + adminHandler(async (_req: NextRequest) => { + const pendingRewards = await findAllRewardClaimsPendingToValidate().then( + (r) => r.unwrap(), + ) + return NextResponse.json({ pendingRewards }, { status: 200 }) + }), +) diff --git a/apps/web/src/app/api/datasets/create/route.test.ts b/apps/web/src/app/api/datasets/create/route.test.ts index 05e293d0c6..a543368366 100644 --- a/apps/web/src/app/api/datasets/create/route.test.ts +++ b/apps/web/src/app/api/datasets/create/route.test.ts @@ -144,7 +144,9 @@ describe('POST handler for datasets/create', () => { expect(response.status).toBe(400) const data = await response.json() expect(data.success).toBe(false) - expect(data.errors.name).toContain('Expected string, received null') + expect(data.errors.name).toContain( + 'Invalid input: expected string, received null', + ) }) it('should return 400 if csvDelimiter is invalid', async () => { diff --git a/apps/web/src/app/api/datasets/create/route.ts b/apps/web/src/app/api/datasets/create/route.ts index 63a4db8f27..72de43b23e 100644 --- a/apps/web/src/app/api/datasets/create/route.ts +++ b/apps/web/src/app/api/datasets/create/route.ts @@ -12,6 +12,7 @@ import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { DatasetsRepository } from '@latitude-data/core/repositories' import { z } from 'zod' +import { flattenErrors } from '@latitude-data/core/lib/zodUtils' const MAX_SIZE_MESSAGE = `Your dataset must be less than ${MAX_SIZE}MB in size.` @@ -20,7 +21,7 @@ const createDatasetSchema = (workspaceId: number) => .object({ name: z .string() - .min(1, { message: 'Name is required' }) + .min(1, { error: 'Name is required' }) .refine( async (name) => { const scope = new DatasetsRepository(workspaceId) @@ -112,19 +113,10 @@ export const POST = errorHandler( const validation = await schema.safeParseAsync(data) if (!validation.success) { - const errors: Record = {} - validation.error.issues.forEach((issue) => { - const path = issue.path.join('.') - if (!errors[path]) { - errors[path] = [] - } - errors[path].push(issue.message) - }) - return NextResponse.json( { success: false, - errors, + errors: flattenErrors(validation), }, { status: 400 }, ) diff --git a/apps/web/src/app/api/documents/[documentUuid]/run/route.ts b/apps/web/src/app/api/documents/[documentUuid]/run/route.ts index 427e0acb48..49ea4e0c86 100644 --- a/apps/web/src/app/api/documents/[documentUuid]/run/route.ts +++ b/apps/web/src/app/api/documents/[documentUuid]/run/route.ts @@ -30,7 +30,7 @@ import { z } from 'zod' const inputSchema = z.object({ path: z.string(), commitUuid: z.string(), - parameters: z.record(z.any()), + parameters: z.record(z.string(), z.any()), stream: z.boolean().default(true), userMessage: z.string().optional(), aiParameters: z.boolean().optional(), @@ -234,7 +234,10 @@ export const POST = errorHandler( } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( - { message: 'Invalid input', details: error.errors }, + { + message: 'Invalid input', + details: z.treeifyError(error), + }, { status: 400 }, ) } diff --git a/apps/web/src/app/api/documents/logs/[documentLogUuid]/chat/route.ts b/apps/web/src/app/api/documents/logs/[documentLogUuid]/chat/route.ts index eb67b44229..1f1efc3dbb 100644 --- a/apps/web/src/app/api/documents/logs/[documentLogUuid]/chat/route.ts +++ b/apps/web/src/app/api/documents/logs/[documentLogUuid]/chat/route.ts @@ -112,7 +112,7 @@ export const POST = errorHandler( } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( - { message: 'Invalid input', details: error.errors }, + { message: 'Invalid input', details: z.treeifyError(error) }, { status: 400 }, ) } diff --git a/apps/web/src/app/api/integrations/[integrationName]/listTools/route.ts b/apps/web/src/app/api/integrations/[integrationName]/listTools/route.ts index a83dba31aa..138d91cc51 100644 --- a/apps/web/src/app/api/integrations/[integrationName]/listTools/route.ts +++ b/apps/web/src/app/api/integrations/[integrationName]/listTools/route.ts @@ -1,11 +1,11 @@ +import { z } from 'zod' import { Workspace } from '@latitude-data/core/browser' import { authHandler } from '$/middlewares/authHandler' import { errorHandler } from '$/middlewares/errorHandler' import { NextRequest, NextResponse } from 'next/server' import { IntegrationsRepository } from '@latitude-data/core/repositories' import { listTools } from '@latitude-data/core/services/integrations/index' -import { LatitudeTool, McpTool } from '@latitude-data/constants' -import { getLatitudeToolDefinition } from '@latitude-data/core/services/latitudeTools/helpers' +import { LATITUDE_TOOLS } from '@latitude-data/core/services/latitudeTools/tools' export const GET = errorHandler( authHandler( @@ -22,16 +22,16 @@ export const GET = errorHandler( }, ) => { if (params.integrationName === 'latitude') { - const latitudeTools: McpTool[] = Object.values(LatitudeTool).map( - (latitudeTool) => { - const toolDefinition = getLatitudeToolDefinition(latitudeTool)! - return { - name: latitudeTool, - description: toolDefinition.description, - inputSchema: toolDefinition.parameters, - } - }, - ) + const latitudeTools = LATITUDE_TOOLS.map((tool) => ({ + name: tool.name, + description: tool.definition().description, + inputSchema: z.toJSONSchema( + tool.definition().inputSchema as z.ZodType, + { + target: 'openapi-3.0', + }, + ), + })) return NextResponse.json( { ok: true, diff --git a/apps/web/src/app/api/integrations/[integrationName]/references/route.ts b/apps/web/src/app/api/integrations/[integrationName]/references/route.ts index e150462a3b..06df4107dc 100644 --- a/apps/web/src/app/api/integrations/[integrationName]/references/route.ts +++ b/apps/web/src/app/api/integrations/[integrationName]/references/route.ts @@ -4,8 +4,6 @@ import { errorHandler } from '$/middlewares/errorHandler' import { NextRequest, NextResponse } from 'next/server' import { IntegrationsRepository } from '@latitude-data/core/repositories' import { listIntegrationReferences } from '@latitude-data/core/services/integrations/references' -import { LatitudeTool, McpTool } from '@latitude-data/constants' -import { getLatitudeToolDefinition } from '@latitude-data/core/services/latitudeTools/helpers' export const GET = errorHandler( authHandler( @@ -22,23 +20,7 @@ export const GET = errorHandler( }, ) => { if (params.integrationName === 'latitude') { - const latitudeTools: McpTool[] = Object.values(LatitudeTool).map( - (latitudeTool) => { - const toolDefinition = getLatitudeToolDefinition(latitudeTool)! - return { - name: latitudeTool, - description: toolDefinition.description, - inputSchema: toolDefinition.parameters, - } - }, - ) - return NextResponse.json( - { - ok: true, - data: latitudeTools, - }, - { status: 200 }, - ) + return NextResponse.json({ ok: true, data: [] }, { status: 200 }) } const integrationsScope = new IntegrationsRepository(workspace.id) diff --git a/apps/web/src/app/api/projects/[projectId]/commits/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationUuid]/run-llm/route.ts b/apps/web/src/app/api/projects/[projectId]/commits/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationUuid]/run-llm/route.ts index 9d46a85ece..6c8787a1b6 100644 --- a/apps/web/src/app/api/projects/[projectId]/commits/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationUuid]/run-llm/route.ts +++ b/apps/web/src/app/api/projects/[projectId]/commits/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationUuid]/run-llm/route.ts @@ -85,7 +85,7 @@ const buildWriteErrorToStream = } const inputSchema = z.object({ - parameters: z.record(z.any()), + parameters: z.record(z.string(), z.any()), }) export const POST = errorHandler( diff --git a/apps/web/src/components/ChatWrapper/Message/index.tsx b/apps/web/src/components/ChatWrapper/Message/index.tsx index 4fe87b1c85..4e18c86eaa 100644 --- a/apps/web/src/components/ChatWrapper/Message/index.tsx +++ b/apps/web/src/components/ChatWrapper/Message/index.tsx @@ -1,5 +1,3 @@ -'use client' - import { memo, ReactNode, useEffect, useMemo, useState } from 'react' import { @@ -256,8 +254,9 @@ const Content = ({ } } - if (value.type === 'reasoning') + if (value.type === 'reasoning') { return + } if (value.type === 'image') { return ( diff --git a/apps/web/src/components/PlaygroundCommon/StatusIndicator/index.tsx b/apps/web/src/components/PlaygroundCommon/StatusIndicator/index.tsx index 863deefd8e..d1a4258e9c 100644 --- a/apps/web/src/components/PlaygroundCommon/StatusIndicator/index.tsx +++ b/apps/web/src/components/PlaygroundCommon/StatusIndicator/index.tsx @@ -7,7 +7,7 @@ import { Separator } from '@latitude-data/web-ui/atoms/Separator' import { Text } from '@latitude-data/web-ui/atoms/Text' import { Tooltip } from '@latitude-data/web-ui/atoms/Tooltip' import { cn } from '@latitude-data/web-ui/utils' -import { LanguageModelUsage } from 'ai' +import { LegacyVercelSDKVersion4Usage as LanguageModelUsage } from '@latitude-data/constants' type StatusIndicatorProps = { playground: ReturnType @@ -123,7 +123,13 @@ function InnerIndicator({ } function StatusInfo({ - usage: { promptTokens, completionTokens, totalTokens }, + usage: { + promptTokens, + completionTokens, + totalTokens, + reasoningTokens, + cachedInputTokens, + }, cost, duration, }: { @@ -145,7 +151,14 @@ function StatusInfo({ - {formatCount(totalTokens || promptTokens || completionTokens || 0)}{' '} + {formatCount( + totalTokens || + promptTokens || + completionTokens || + cachedInputTokens || + reasoningTokens || + 0, + )}{' '} tokens @@ -155,8 +168,8 @@ function StatusInfo({
- - + +
diff --git a/apps/web/src/components/Providers/WebsocketsProvider/index.tsx b/apps/web/src/components/Providers/WebsocketsProvider/index.tsx index e2530ecdba..d43fb2d00d 100644 --- a/apps/web/src/components/Providers/WebsocketsProvider/index.tsx +++ b/apps/web/src/components/Providers/WebsocketsProvider/index.tsx @@ -17,6 +17,7 @@ import { IoProvider, useSocket } from '@latitude-data/socket.io-react-hook' import { useSession } from '@latitude-data/web-ui/providers' import { useToast } from '@latitude-data/web-ui/atoms/Toast' import { captureClientError } from '$/instrumentation-client' +import useLatitudeAction from '$/hooks/useLatitudeAction' export const SocketIOProvider = ({ children }: { children: ReactNode }) => { return {children} @@ -54,10 +55,13 @@ export function useSocketConnection({ }, ) + const { execute: refreshToken } = useLatitudeAction( + refreshWebesocketTokenAction, + ) connection.socket.on('connect_error', async (error) => { if (error.message.startsWith('AUTH_ERROR')) { try { - const [data] = await refreshWebesocketTokenAction() + const [data] = await refreshToken() if (data && data.success) { connection.socket.connect() diff --git a/apps/web/src/components/evaluations/EvaluationV2Form.tsx b/apps/web/src/components/evaluations/EvaluationV2Form.tsx index b509ef7df4..78e63e2999 100644 --- a/apps/web/src/components/evaluations/EvaluationV2Form.tsx +++ b/apps/web/src/components/evaluations/EvaluationV2Form.tsx @@ -1,5 +1,4 @@ -import { ActionErrors, parseActionErrors } from '$/hooks/useLatitudeAction' -import { useEvaluationsV2 } from '$/stores/evaluationsV2' +import { ActionErrors } from '$/hooks/useLatitudeAction' import { EvaluationMetric, EvaluationOptions, @@ -25,6 +24,7 @@ import { ConfigurationSimpleForm, } from './ConfigurationForm' import { EVALUATION_SPECIFICATIONS } from './index' +import { StandardSchemaV1 } from '@standard-schema/spec' const EVALUATION_TYPE_OPTIONS = Object.values(EvaluationType).map((type) => { const specification = EVALUATION_SPECIFICATIONS[type] @@ -35,6 +35,31 @@ const EVALUATION_TYPE_OPTIONS = Object.values(EvaluationType).map((type) => { } }) +/** + * This can be improved by passing specific schemas per type/metric + * But I'm fed up of fixing zod errors sorry + */ +type EvaluationV2FormSchema = StandardSchemaV1<{ + type: string + name: string + description: string + metric: string + options: string + settings: string + evaluateLiveLogs: string + enableSuggestions: string +}> + +export type EvaluationV2FormErrors = ActionErrors + +/** + * Helper: normalize validation errors into a flat map + */ +export function parseActionErrors(errors?: EvaluationV2FormErrors) { + if (!errors) return {} + return errors.fieldErrors +} + const EVALUATION_METRIC_OPTIONS = < T extends EvaluationType = EvaluationType, M extends EvaluationMetric = EvaluationMetric, @@ -88,10 +113,7 @@ export default function EvaluationV2Form< setSettings: (settings: EvaluationSettings) => void options: Partial setOptions: (options: Partial) => void - errors?: ActionErrors< - typeof useEvaluationsV2, - 'createEvaluation' | 'updateEvaluation' - > + errors?: EvaluationV2FormErrors commit: ICommitContextType['commit'] disabled?: boolean }) { @@ -137,7 +159,7 @@ export default function EvaluationV2Form< description={typeSpecification.description} options={EVALUATION_TYPE_OPTIONS} onChange={(value) => setSettings({ ...settings, type: value as T })} - errors={errors?.['type']} + errors={errors?.type} fancy disabled={disabled || commitMerged} required @@ -149,7 +171,7 @@ export default function EvaluationV2Form< label='Name' placeholder='Give your evaluation a name' onChange={(e) => setSettings({ ...settings, name: e.target.value })} - errors={errors?.['name']} + errors={errors?.name} className='w-full' disabled={disabled || commitMerged} required @@ -164,7 +186,7 @@ export default function EvaluationV2Form< onChange={(e) => setSettings({ ...settings, description: e.target.value }) } - errors={errors?.['description']} + errors={errors?.description} className='w-full' disabled={disabled || commitMerged} required @@ -185,7 +207,7 @@ export default function EvaluationV2Form< onChange={(value) => setSettings({ ...settings, metric: value as M }) } - errors={errors?.['metric']} + errors={errors?.metric} disabled={disabled || commitMerged} required /> @@ -248,7 +270,7 @@ export default function EvaluationV2Form< onCheckedChange={(value) => setOptions({ ...options, evaluateLiveLogs: value }) } - errors={errors?.['evaluateLiveLogs']} + errors={errors?.evaluateLiveLogs} disabled={ disabled || !metricSpecification?.supportsLiveEvaluation } @@ -262,21 +284,9 @@ export default function EvaluationV2Form< onCheckedChange={(value) => setOptions({ ...options, enableSuggestions: value }) } - errors={errors?.['enableSuggestions']} + errors={errors?.enableSuggestions} disabled={disabled} /> - {/* TODO(exps): Uncomment when experiments are implemented */} - {/* - setOptions({ ...options, autoApplySuggestions: value }) - } - errors={errors?.['autoApplySuggestions']} - disabled={disabled} - /> */} } diff --git a/apps/web/src/components/evaluations/rule/SemanticSimilarity.tsx b/apps/web/src/components/evaluations/rule/SemanticSimilarity.tsx index 6e26a1aa2d..0fde8de7a0 100644 --- a/apps/web/src/components/evaluations/rule/SemanticSimilarity.tsx +++ b/apps/web/src/components/evaluations/rule/SemanticSimilarity.tsx @@ -24,13 +24,6 @@ export default { chartConfiguration: chartConfiguration, } -// TODO: Uncomment when more algorithms are implemented -// const ALGORITHM_OPTIONS = -// specification.configuration.shape.algorithm.options.map((option) => ({ -// label: option.toUpperCase().split('_').join(' '), -// value: option, -// })) - function ConfigurationSimpleForm({ configuration, setConfiguration, @@ -48,21 +41,6 @@ function ConfigurationSimpleForm({ return ( <> - {/* TODO: Uncomment when more algorithms are implemented */} - {/* - ) - - if (props.hidden) return inputComp - return ( (function Input( errors={errors} errorStyle={errorStyle} > - {inputComp} + ) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d006709461..9e709b10db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,6 +39,9 @@ catalogs: '@types/react-dom': specifier: 19.1.1 version: 19.1.1 + ai: + specifier: 5.0.44 + version: 5.0.44 dd-trace: specifier: 5.43.0 version: 5.43.0 @@ -52,8 +55,8 @@ catalogs: specifier: 8.16.0 version: 8.16.0 promptl-ai: - specifier: 0.7.6 - version: 0.7.6 + specifier: 0.8.0 + version: 0.8.0 react: specifier: 19.1.1 version: 19.1.1 @@ -61,8 +64,8 @@ catalogs: specifier: 19.1.1 version: 19.1.1 zod: - specifier: 3.24.2 - version: 3.24.2 + specifier: 4.1.8 + version: 4.1.8 importers: @@ -141,8 +144,8 @@ importers: specifier: 0.4.1 version: 0.4.1(hono@4.9.7) '@hono/zod-openapi': - specifier: 0.16.4 - version: 0.16.4(hono@4.9.7)(zod@3.24.2) + specifier: 1.1.1 + version: 1.1.1(hono@4.9.7)(zod@4.1.8) '@latitude-data/constants': specifier: workspace:^ version: link:../../packages/constants @@ -160,7 +163,7 @@ importers: version: link:../../packages/telemetry/typescript '@t3-oss/env-core': specifier: 0.13.8 - version: 0.13.8(typescript@5.8.3)(zod@3.24.2) + version: 0.13.8(typescript@5.8.3)(zod@4.1.8) dd-trace: specifier: 'catalog:' version: 5.43.0 @@ -178,13 +181,13 @@ importers: version: 4.17.21 promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) rate-limiter-flexible: specifier: 5.0.3 version: 5.0.3 zod: specifier: 'catalog:' - version: 3.24.2 + version: 4.1.8 devDependencies: '@latitude-data/eslint-config': specifier: workspace:^ @@ -213,6 +216,9 @@ importers: apps/web: dependencies: + '@ai-sdk/rsc': + specifier: ^1.0.44 + version: 1.0.44(react@19.1.1)(zod@4.1.8) '@aws-sdk/client-s3': specifier: 3.850.0 version: 3.850.0 @@ -277,8 +283,8 @@ importers: specifier: 2.2.1 version: 2.2.1 '@t3-oss/env-nextjs': - specifier: 0.10.1 - version: 0.10.1(typescript@5.8.3)(zod@3.24.2) + specifier: 0.13.8 + version: 0.13.8(typescript@5.8.3)(zod@4.1.8) '@types/diff-match-patch': specifier: 1.0.36 version: 1.0.36 @@ -286,8 +292,8 @@ importers: specifier: 1.1.3 version: 1.1.3 ai: - specifier: 4.2.1 - version: 4.2.1(react@19.1.1)(zod@3.24.2) + specifier: 'catalog:' + version: 5.0.44(zod@4.1.8) arctic: specifier: 3.6.0 version: 3.6.0 @@ -339,6 +345,9 @@ importers: next: specifier: 15.5.4 version: 15.5.4(@opentelemetry/api@1.8.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next-safe-action: + specifier: ^8.0.11 + version: 8.0.11(next@15.5.4(@opentelemetry/api@1.8.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) next-themes: specifier: 0.3.0 version: 0.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -365,7 +374,7 @@ importers: version: 1.252.1 promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) rate-limiter-flexible: specifier: 5.0.3 version: 5.0.3 @@ -395,16 +404,7 @@ importers: version: 2.4.5 zod: specifier: 'catalog:' - version: 3.24.2 - zod-to-json-schema: - specifier: 3.24.5 - version: 3.24.5(zod@3.24.2) - zsa: - specifier: 0.5.1 - version: 0.5.1(zod@3.24.2) - zsa-react: - specifier: 0.2.3 - version: 0.2.3(react@19.1.1)(zod@3.24.2) + version: 4.1.8 zustand: specifier: 4.5.6 version: 4.5.6(@types/react@19.1.15)(immer@9.0.21)(react@19.1.1) @@ -436,6 +436,9 @@ importers: '@rollup/plugin-typescript': specifier: ^11.1.6 version: 11.1.6(rollup@4.43.0)(tslib@2.8.1)(typescript@5.8.3) + '@standard-schema/spec': + specifier: ^1.0.0 + version: 1.0.0 '@testing-library/dom': specifier: ^10.3.2 version: 10.4.0 @@ -571,7 +574,7 @@ importers: version: 9.9.0 '@t3-oss/env-core': specifier: '*' - version: 0.13.8(typescript@5.8.3)(zod@3.25.76) + version: 0.13.8(typescript@5.8.3)(zod@4.1.8) bullmq: specifier: 5.44.4 version: 5.44.4 @@ -685,7 +688,7 @@ importers: version: 2.4.5 zod: specifier: 'catalog:' - version: 3.24.2 + version: 4.1.8 devDependencies: '@latitude-data/eslint-config': specifier: workspace:* @@ -721,17 +724,17 @@ importers: packages/constants: dependencies: ai: - specifier: ^4.2.9 - version: 4.3.16(react@19.1.1)(zod@3.24.2) + specifier: 'catalog:' + version: 5.0.44(zod@4.1.8) json-schema: specifier: ^0.4.0 version: 0.4.0 promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) zod: specifier: 'catalog:' - version: 3.24.2 + version: 4.1.8 devDependencies: '@latitude-data/eslint-config': specifier: workspace:* @@ -746,41 +749,41 @@ importers: packages/core: dependencies: '@ai-sdk/amazon-bedrock': - specifier: 2.2.10 - version: 2.2.10(zod@3.24.2) + specifier: 3.0.21 + version: 3.0.21(zod@4.1.8) '@ai-sdk/anthropic': - specifier: 1.1.0 - version: 1.1.0(zod@3.24.2) + specifier: 2.0.17 + version: 2.0.17(zod@4.1.8) '@ai-sdk/azure': - specifier: 1.1.0 - version: 1.1.0(zod@3.24.2) + specifier: 2.0.30 + version: 2.0.30(zod@4.1.8) '@ai-sdk/deepseek': - specifier: 0.2.5 - version: 0.2.5(zod@3.24.2) + specifier: 1.0.17 + version: 1.0.17(zod@4.1.8) '@ai-sdk/google': - specifier: 1.2.18 - version: 1.2.18(zod@3.24.2) + specifier: 2.0.14 + version: 2.0.14(zod@4.1.8) '@ai-sdk/google-vertex': - specifier: 2.2.27 - version: 2.2.27(zod@3.24.2) + specifier: 3.0.27 + version: 3.0.27(zod@4.1.8) '@ai-sdk/mistral': - specifier: 1.1.0 - version: 1.1.0(zod@3.24.2) + specifier: 2.0.14 + version: 2.0.14(zod@4.1.8) '@ai-sdk/openai': - specifier: 1.3.22 - version: 1.3.22(zod@3.24.2) + specifier: 2.0.30 + version: 2.0.30(zod@4.1.8) '@ai-sdk/perplexity': - specifier: 1.1.3 - version: 1.1.3(zod@3.24.2) + specifier: 2.0.9 + version: 2.0.9(zod@4.1.8) '@ai-sdk/provider-utils': - specifier: 2.2.8 - version: 2.2.8(zod@3.24.2) + specifier: 3.0.9 + version: 3.0.9(zod@4.1.8) '@ai-sdk/xai': - specifier: 1.2.6 - version: 1.2.6(zod@3.24.2) + specifier: 2.0.18 + version: 2.0.18(zod@4.1.8) '@anthropic-ai/sdk': - specifier: 0.32.1 - version: 0.32.1 + specifier: 0.62.0 + version: 0.62.0 '@aws-sdk/client-s3': specifier: 3.850.0 version: 3.850.0 @@ -852,13 +855,13 @@ importers: version: 2.2.1 '@t3-oss/env-core': specifier: 0.13.8 - version: 0.13.8(typescript@5.8.3)(zod@3.24.2) + version: 0.13.8(typescript@5.8.3)(zod@4.1.8) '@tavily/core': specifier: 0.3.1 version: 0.3.1 ai: - specifier: 4.2.1 - version: 4.2.1(react@19.1.1)(zod@3.24.2) + specifier: 'catalog:' + version: 5.0.44(zod@4.1.8) ajv: specifier: 8.17.1 version: 8.17.1 @@ -951,7 +954,7 @@ importers: version: 4.2.0 promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) rate-limiter-flexible: specifier: 5.0.3 version: 5.0.3 @@ -978,10 +981,7 @@ importers: version: 11.1.0 zod: specifier: 'catalog:' - version: 3.24.2 - zod-to-json-schema: - specifier: 3.24.5 - version: 3.24.5(zod@3.24.2) + version: 4.1.8 devDependencies: '@faker-js/faker': specifier: ^8.4.1 @@ -1060,13 +1060,13 @@ importers: dependencies: '@t3-oss/env-core': specifier: ^0.13.8 - version: 0.13.8(typescript@5.8.3)(zod@3.24.2) + version: 0.13.8(typescript@5.8.3)(zod@4.1.8) dotenv: specifier: ^16.4.5 version: 16.5.0 zod: specifier: 'catalog:' - version: 3.24.2 + version: 4.1.8 devDependencies: '@latitude-data/eslint-config': specifier: workspace:* @@ -1085,13 +1085,13 @@ importers: version: 2.7.0 promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) typescript: specifier: ^5.5.4 version: 5.8.3 zod: specifier: 'catalog:' - version: 3.24.2 + version: 4.1.8 devDependencies: '@latitude-data/constants': specifier: workspace:* @@ -1197,11 +1197,11 @@ importers: version: 11.1.0 zod: specifier: 'catalog:' - version: 3.24.2 + version: 4.1.8 devDependencies: '@anthropic-ai/sdk': - specifier: ^0.32.1 - version: 0.32.1 + specifier: 0.62.0 + version: 0.62.0 '@aws-sdk/client-bedrock-runtime': specifier: ^3.830.0 version: 3.830.0 @@ -1216,7 +1216,7 @@ importers: version: 1.10.0 '@langchain/core': specifier: ^0.3.57 - version: 0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) + version: 0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) '@latitude-data/constants': specifier: workspace:* version: link:../../constants @@ -1245,26 +1245,26 @@ importers: specifier: ^10.0.0 version: 10.0.0 ai: - specifier: ^4.3.16 - version: 4.3.16(react@19.1.1)(zod@3.24.2) + specifier: 'catalog:' + version: 5.0.44(zod@4.1.8) cohere-ai: specifier: ^7.17.1 version: 7.17.1 langchain: specifier: ^0.3.27 - version: 0.3.27(@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.3.27(@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)))(axios@1.10.0)(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) llamaindex: specifier: ^0.8.37 - version: 0.8.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(bufferutil@4.0.9)(cohere-ai@7.17.1)(js-tiktoken@1.0.20)(jsdom@24.1.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))(pathe@1.1.2)(pg-query-stream@4.8.1(pg@8.14.1))(pg@8.14.1)(socks@2.8.5)(terser@5.44.0)(tree-sitter@0.22.4)(typescript@5.8.3)(utf-8-validate@6.0.5)(web-tree-sitter@0.24.7)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + version: 0.8.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(bufferutil@4.0.9)(cohere-ai@7.17.1)(js-tiktoken@1.0.20)(jsdom@24.1.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))(pathe@1.1.2)(pg-query-stream@4.8.1(pg@8.14.1))(pg@8.14.1)(socks@2.8.5)(terser@5.44.0)(tree-sitter@0.22.4)(typescript@5.8.3)(utf-8-validate@6.0.5)(web-tree-sitter@0.24.7)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) msw: specifier: ^2.3.5 version: 2.10.2(@types/node@22.15.31)(typescript@5.8.3) openai: - specifier: ^4.102.0 - version: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + specifier: ^5.22.0 + version: 5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) rollup: specifier: ^4.41.0 version: 4.43.0 @@ -1412,9 +1412,6 @@ importers: use-debounce: specifier: ^10.0.5 version: 10.0.5(react@19.1.1) - zod: - specifier: 'catalog:' - version: 3.24.2 zustand: specifier: ^4.5.6 version: 4.5.6(@types/react@19.1.15)(immer@9.0.21)(react@19.1.1) @@ -1481,7 +1478,7 @@ importers: version: 16.1.0(postcss@8.5.4) promptl-ai: specifier: 'catalog:' - version: 0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + version: 0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) recast: specifier: ^0.23.4 version: 0.23.11 @@ -1526,160 +1523,97 @@ importers: packages: - '@ai-sdk/amazon-bedrock@2.2.10': - resolution: {integrity: sha512-icLGO7Q0NinnHIPgT+y1QjHVwH4HwV+brWbvM+FfCG2Afpa89PyKa3Ret91kGjZpBgM/xnj1B7K5eM+rRlsXQA==} + '@ai-sdk/amazon-bedrock@3.0.21': + resolution: {integrity: sha512-oYzjPiuzMnOZV28BPmoGtgAFuPtvnS4NVomXTd+pJCkM87f5MiX6hIgcBAVQj5FfaFBRd7iYQId41cFqp4LK2Q==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/anthropic@1.1.0': - resolution: {integrity: sha512-dunguWbV6D2CdFqGgtAXLp64ZqUet8W6AUkheb8C8oxXcaGium2BSqopO17kx9pCWFaBYWBFZZb2jCFok6zOUg==} + '@ai-sdk/anthropic@2.0.17': + resolution: {integrity: sha512-fEmGD3H3cI4ahcrtU/ekA6xvUq9kk/IpOh2TI3wOSxqvKqpo+ztwiem5/x5R92Yenl9KRooYIefr0LNlFUR5Ow==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/anthropic@1.2.12': - resolution: {integrity: sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==} + '@ai-sdk/azure@2.0.30': + resolution: {integrity: sha512-bBa8WVYpjmfAznzf9Ba6TcGsaVcxeSNs+mEByk1k6mT/xtt3kxRpPixssSpKP/fc0Gl1us+O51OYzqc7G9/B+w==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/azure@1.1.0': - resolution: {integrity: sha512-h9ICuSGHHJ/I0D1JYBLxdn1yP2OlHqGNNZ99tA9EsOWsrTDMHEbsJNcDdnshyL+sogaXzSguB5jVFKxf5s+enw==} + '@ai-sdk/deepseek@1.0.17': + resolution: {integrity: sha512-vhP6R04GAD521Mji95R/zCpKWFqLgsOwkzgBQrG36tIsANy5NSrukQSrs7s76OnzV76sGwn4rLWEvCdqnvYeaA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/deepseek@0.2.5': - resolution: {integrity: sha512-iYB3CSX6kT2Ow9Zc2zHOC4jFPy3isliEyh2CLlZWPBcGIWVikp2Q7zACPOZrZYN58IFTWGC1l++s1kWXxPtuiw==} + '@ai-sdk/gateway@1.0.23': + resolution: {integrity: sha512-ynV7WxpRK2zWLGkdOtrU2hW22mBVkEYVS3iMg1+ZGmAYSgzCqzC74bfOJZ2GU1UdcrFWUsFI9qAYjsPkd+AebA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/google-vertex@2.2.27': - resolution: {integrity: sha512-iDGX/2yrU4OOL1p/ENpfl3MWxuqp9/bE22Z8Ip4DtLCUx6ismUNtrKO357igM1/3jrM6t9C6egCPniHqBsHOJA==} + '@ai-sdk/google-vertex@3.0.27': + resolution: {integrity: sha512-qR4+gLBNJPbnqaSBhvWktK0h3MfQryTLpKj9OWTYVhesNPqpkcchmbHmWBBXhI9kGN5niIknzHmCYdrtMqzF5w==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/google@1.2.18': - resolution: {integrity: sha512-8B70+i+uB12Ae6Sn6B9Oc6W0W/XorGgc88Nx0pyUrcxFOdytHBaAVhTPqYsO3LLClfjYN8pQ9GMxd5cpGEnUcA==} + '@ai-sdk/google@2.0.14': + resolution: {integrity: sha512-OCBBkEUq1RNLkbJuD+ejqGsWDD0M5nRyuFWDchwylxy0J4HSsAiGNhutNYVTdnqmNw+r9LyZlkyZ1P4YfAfLdg==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/google@1.2.22': - resolution: {integrity: sha512-Ppxu3DIieF1G9pyQ5O1Z646GYR0gkC57YdBqXJ82qvCdhEhZHu0TWhmnOoeIWe2olSbuDeoOY+MfJrW8dzS3Hw==} + '@ai-sdk/mistral@2.0.14': + resolution: {integrity: sha512-HPj/tz03k8IeW9SabHqhHmgdeKq1H89MltU5zUaxU/7JVV3n96MYIG3aQUtKbPMdtHw6LngJf6r/A+hampGLOA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/mistral@1.1.0': - resolution: {integrity: sha512-RPOAzk6baQ8R/Cw/N9189tX6CzSOHBNf+r4I5x2H08JCsEx8nycMgwqvVzZ5lhWhaKBvHaD3KoEQSyqyqW82Gg==} + '@ai-sdk/openai-compatible@1.0.17': + resolution: {integrity: sha512-5AWWS8sbT7VJmi2gQZKXRJsnPg+3+yN2BLms3x1sjRA/ZUq2TfmVzv6PSMObvbkn27jpvyIX5/e6JYgafhYJkA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/openai-compatible@0.2.5': - resolution: {integrity: sha512-Mi83WLIbrmcrQ5b4LQSl8DSs/QHLGTtRu+5+Kb+4lzxCHAGiqzgEGrFww3S6bl+GVfhGtyTCONqU1Nok2r+k5Q==} + '@ai-sdk/openai@2.0.30': + resolution: {integrity: sha512-a9Vf64OT2dWEFyEGv+OxtCs69B18BsuzInvuyUxVPczbIiBLqUCt3zcD/8EwqbTPJwsFNsL8/9nbVZFmwA1+2A==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/openai@1.1.0': - resolution: {integrity: sha512-D2DaGMK89yYgO32n4Gr7gBJeJGGGS27gdfzYFMRDXlZmKh7VW1WXBp3FXxDwpmt0CgLoVI4qV8lf+gslah+kWw==} + '@ai-sdk/perplexity@2.0.9': + resolution: {integrity: sha512-OuwPKWQkZKeynUNOOstMY2nktZNatpbTvDSq4INgpx0jsSzE9UfdPdm8BNU6d1lGRf0v03FlQ/cX09R2jMI3mA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/openai@1.3.22': - resolution: {integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==} + '@ai-sdk/provider-utils@3.0.9': + resolution: {integrity: sha512-Pm571x5efqaI4hf9yW4KsVlDBDme8++UepZRnq+kqVBWWjgvGhQlzU8glaFq0YJEB9kkxZHbRRyVeHoV2sRYaQ==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 - '@ai-sdk/perplexity@1.1.3': - resolution: {integrity: sha512-CeWCjYi+z+yxMoodqXXlh9MgtKgQi4/zBgvkDaAGEZXQRyz0lsIKa8oKNXX7znkxeSj+74t9HESwelOv5jqGnA==} + '@ai-sdk/provider@2.0.0': + resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 - - '@ai-sdk/provider-utils@2.1.0': - resolution: {integrity: sha512-rBUabNoyB25PBUjaiMSk86fHNSCqTngNZVvXxv8+6mvw47JX5OexW+ZHRsEw8XKTE8+hqvNFVzctaOrRZ2i9Zw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 - peerDependenciesMeta: - zod: - optional: true - - '@ai-sdk/provider-utils@2.2.0': - resolution: {integrity: sha512-RX5BnDSqudjvZjwwpROcxVQElyX7rUn/xImBgaZLXekSGqq8f7/tefqDcQiRbDZjuCd4CVIfhrK8y/Pta8cPfQ==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.23.8 - '@ai-sdk/provider-utils@2.2.3': - resolution: {integrity: sha512-o3fWTzkxzI5Af7U7y794MZkYNEsxbjLam2nxyoUZSScqkacb7vZ3EYHLh21+xCcSSzEC161C7pZAGHtC0hTUMw==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.23.8 - - '@ai-sdk/provider-utils@2.2.8': - resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.23.8 - - '@ai-sdk/provider@1.0.4': - resolution: {integrity: sha512-lJi5zwDosvvZER3e/pB8lj1MN3o3S7zJliQq56BRr4e9V3fcRyFtwP0JRxaRS5vHYX3OJ154VezVoQNrk0eaKw==} - engines: {node: '>=18'} - - '@ai-sdk/provider@1.1.0': - resolution: {integrity: sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew==} - engines: {node: '>=18'} - - '@ai-sdk/provider@1.1.3': - resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} - engines: {node: '>=18'} - - '@ai-sdk/react@1.2.0': - resolution: {integrity: sha512-fUTZkAsxOMz8ijjWf87E/GfYkgsH4V5MH2yuj7EXh5ShjWe/oayn2ZJkyoqFMr4Jf8m5kptDaivmbIenDq5OXA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - zod: - optional: true - - '@ai-sdk/react@1.2.12': - resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + '@ai-sdk/rsc@1.0.44': + resolution: {integrity: sha512-WOnh8JCEJv0j8xZVmOnSHdMjxsLM+LAQ3lwJJqy7xAYhrhPDvlRTZ9c2xJORqDFeYTAGZe0NjnvP8TsOiJQ6Zg==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 + zod: ^3.25.76 || ^4 peerDependenciesMeta: zod: optional: true - '@ai-sdk/ui-utils@1.2.0': - resolution: {integrity: sha512-0IZwCqe7E+GkCASTDPAbzMr+POm9GDzWvFd37FvzpOeKNeibmge/LZEkTDbGSa+3b928H8wPwOLsOXBWPLUPDQ==} + '@ai-sdk/xai@2.0.18': + resolution: {integrity: sha512-QmrtRDY76R084qBgeLmWR4WvRgKIGq9tHD/Wk8l9LbpqqSa3ez/oESUzFIbZx9fJJGca1xkXyK3jLeCB9FzNwA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.23.8 - - '@ai-sdk/ui-utils@1.2.11': - resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.23.8 - - '@ai-sdk/xai@1.2.6': - resolution: {integrity: sha512-I5D1uH8kCAx+SDsYdkNQETWMOLMoNGE4BUSqV2qzxnc3+/0RHa0W5pkhyGAgArs+3GDWA3UyRO5OFFdTTCNeVg==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 '@alcalzone/ansi-tokenize@0.2.0': resolution: {integrity: sha512-qI/5TaaaCZE4yeSZ83lu0+xi1r88JSxUjnH4OP/iZF7+KKZ75u3ee5isd0LxX+6N8U0npL61YrpbthILHB6BnA==} @@ -1696,13 +1630,17 @@ packages: '@anthropic-ai/sdk@0.32.1': resolution: {integrity: sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==} + '@anthropic-ai/sdk@0.62.0': + resolution: {integrity: sha512-gT2VFKX0gSp7KJNlav/vzRFjJOPYDZxCJRx7MYUc+fqURc5aS6OI/UJeD2KytJkjsIWv0OOwH1ePc1S5QE2GZw==} + hasBin: true + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@asteasolutions/zod-to-openapi@7.3.3': - resolution: {integrity: sha512-ioiw+R+gBGAUwmDp+/gJA16tedBivzDaji5wOvWej0ZYDE0CXTSSfJfXbrBIuWKh6JQhuXgNDniJdeDueKUZTA==} + '@asteasolutions/zod-to-openapi@8.1.0': + resolution: {integrity: sha512-tQFxVs05J/6QXXqIzj6rTRk3nj1HFs4pe+uThwE95jL5II2JfpVXkK+CqkO7aT0Do5AYqO6LDrKpleLUFXgY+g==} peerDependencies: - zod: ^3.20.2 + zod: ^4.0.0 '@asyncapi/parser@3.4.0': resolution: {integrity: sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ==} @@ -3100,18 +3038,18 @@ packages: peerDependencies: hono: '*' - '@hono/zod-openapi@0.16.4': - resolution: {integrity: sha512-mnF6GthBaKex0D5PsY/4lYNtkaGJNE38bjeUI//EUqq7Ee4TNm2su35IUiFH4HcmJp5fWYMLyOJOpjnkClzEGw==} + '@hono/zod-openapi@1.1.1': + resolution: {integrity: sha512-0IF/IqhO8ZgVHjf4MplkLqWbqctnijTJSh6yaGIcf4tQumDMbXRWHOBwHhZAIbMXlvej64Nst+CxE25qo6Pu8g==} engines: {node: '>=16.0.0'} peerDependencies: hono: '>=4.3.6' - zod: 3.* + zod: ^4.0.0 - '@hono/zod-validator@0.3.0': - resolution: {integrity: sha512-7XcTk3yYyk6ldrO/VuqsroE7stvDZxHJQcpATRAyha8rUxJNBPV3+6waDrARfgEqxOVlzIadm3/6sE/dPseXgQ==} + '@hono/zod-validator@0.7.3': + resolution: {integrity: sha512-uYGdgVib3RlGD698WR5dVM0zB3UuPY5vHKXffGUbUh7r4xY+mFIhF3/v4AcQVLrU5CQdBso8BJr4wuVoCrjTuQ==} peerDependencies: hono: '>=3.9.0' - zod: ^3.19.1 + zod: ^3.25.0 || ^4.0.0 '@huggingface/inference@2.8.1': resolution: {integrity: sha512-EfsNtY9OR6JCNaUa5bZu2mrs48iqeTz0Gutwf+fU0Kypx33xFQB4DKMhp8u4Ee6qVbLbNWvTHuWwlppLQl4p4Q==} @@ -3167,12 +3105,6 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-darwin-arm64@0.34.3': - resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - '@img/sharp-darwin-arm64@0.34.4': resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3185,12 +3117,6 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-darwin-x64@0.34.3': - resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - '@img/sharp-darwin-x64@0.34.4': resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3202,11 +3128,6 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.0': - resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} - cpu: [arm64] - os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.3': resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} cpu: [arm64] @@ -3217,11 +3138,6 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.0': - resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} - cpu: [x64] - os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.3': resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} cpu: [x64] @@ -3232,11 +3148,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm64@1.2.0': - resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} - cpu: [arm64] - os: [linux] - '@img/sharp-libvips-linux-arm64@1.2.3': resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} cpu: [arm64] @@ -3247,21 +3158,11 @@ packages: cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.0': - resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} - cpu: [arm] - os: [linux] - '@img/sharp-libvips-linux-arm@1.2.3': resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.0': - resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} - cpu: [ppc64] - os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.3': resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} cpu: [ppc64] @@ -3272,11 +3173,6 @@ packages: cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.0': - resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} - cpu: [s390x] - os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.3': resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} cpu: [s390x] @@ -3287,11 +3183,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.0': - resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} - cpu: [x64] - os: [linux] - '@img/sharp-libvips-linux-x64@1.2.3': resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} cpu: [x64] @@ -3302,11 +3193,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': - resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} - cpu: [arm64] - os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} cpu: [arm64] @@ -3317,11 +3203,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.0': - resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} - cpu: [x64] - os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.3': resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} cpu: [x64] @@ -3333,12 +3214,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-linux-arm64@0.34.3': - resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - '@img/sharp-linux-arm64@0.34.4': resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3351,24 +3226,12 @@ packages: cpu: [arm] os: [linux] - '@img/sharp-linux-arm@0.34.3': - resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - '@img/sharp-linux-arm@0.34.4': resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.3': - resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - '@img/sharp-linux-ppc64@0.34.4': resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3381,12 +3244,6 @@ packages: cpu: [s390x] os: [linux] - '@img/sharp-linux-s390x@0.34.3': - resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - '@img/sharp-linux-s390x@0.34.4': resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3399,12 +3256,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-linux-x64@0.34.3': - resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - '@img/sharp-linux-x64@0.34.4': resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3417,12 +3268,6 @@ packages: cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.3': - resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.4': resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3435,12 +3280,6 @@ packages: cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.3': - resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - '@img/sharp-linuxmusl-x64@0.34.4': resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3452,22 +3291,11 @@ packages: engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-wasm32@0.34.3': - resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - '@img/sharp-wasm32@0.34.4': resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.3': - resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - '@img/sharp-win32-arm64@0.34.4': resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3480,12 +3308,6 @@ packages: cpu: [ia32] os: [win32] - '@img/sharp-win32-ia32@0.34.3': - resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - '@img/sharp-win32-ia32@0.34.4': resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -3498,12 +3320,6 @@ packages: cpu: [x64] os: [win32] - '@img/sharp-win32-x64@0.34.3': - resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - '@img/sharp-win32-x64@0.34.4': resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -6252,6 +6068,9 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@stoplight/better-ajv-errors@1.0.3': resolution: {integrity: sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==} engines: {node: ^12.20 || >= 14.13} @@ -6331,15 +6150,6 @@ packages: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} - '@t3-oss/env-core@0.10.1': - resolution: {integrity: sha512-GcKZiCfWks5CTxhezn9k5zWX3sMDIYf6Kaxy2Gx9YEQftFcz8hDRN56hcbylyAO3t4jQnQ5ifLawINsNgCDpOg==} - peerDependencies: - typescript: '>=5.0.0' - zod: ^3.0.0 - peerDependenciesMeta: - typescript: - optional: true - '@t3-oss/env-core@0.13.8': resolution: {integrity: sha512-L1inmpzLQyYu4+Q1DyrXsGJYCXbtXjC4cICw1uAKv0ppYPQv656lhZPU91Qd1VS6SO/bou1/q5ufVzBGbNsUpw==} peerDependencies: @@ -6357,14 +6167,22 @@ packages: zod: optional: true - '@t3-oss/env-nextjs@0.10.1': - resolution: {integrity: sha512-iy2qqJLnFh1RjEWno2ZeyTu0ufomkXruUsOZludzDIroUabVvHsrSjtkHqwHp1/pgPUzN3yBRHMILW162X7x2Q==} + '@t3-oss/env-nextjs@0.13.8': + resolution: {integrity: sha512-QmTLnsdQJ8BiQad2W2nvV6oUpH4oMZMqnFEjhVpzU0h3sI9hn8zb8crjWJ1Amq453mGZs6A4v4ihIeBFDOrLeQ==} peerDependencies: + arktype: ^2.1.0 typescript: '>=5.0.0' - zod: ^3.0.0 + valibot: ^1.0.0-beta.7 || ^1.0.0 + zod: ^3.24.0 || ^4.0.0-beta.0 peerDependenciesMeta: + arktype: + optional: true typescript: optional: true + valibot: + optional: true + zod: + optional: true '@tailwindcss/typography@0.5.16': resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} @@ -7196,25 +7014,11 @@ packages: resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} engines: {node: '>=12'} - ai@4.2.1: - resolution: {integrity: sha512-i+/inzhpukqOfx2PvL4+gyXCgF3SahHSHzGRHAyGABORKn1790L8eGNtaSTMn//7ViZnxTMwZFV1pPeUZT4JgQ==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - react: - optional: true - - ai@4.3.16: - resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==} + ai@5.0.44: + resolution: {integrity: sha512-l/rdoM4LcRpsRBVvZQBwSU73oNoFGlWj+PcH86QRzxDGJgZqgGItWO0QcKjBNcLDmUjGN1VYd/8J0TAXHJleRQ==} engines: {node: '>=18'} peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - react: - optional: true + zod: ^3.25.76 || ^4 ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} @@ -8562,10 +8366,6 @@ packages: resolution: {integrity: sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==} engines: {node: '>=12.20'} - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} - engines: {node: '>=8'} - detect-libc@2.1.1: resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==} engines: {node: '>=8'} @@ -9235,10 +9035,6 @@ packages: resolution: {integrity: sha512-gMaRLm5zejEH9mNXC54AnIteFI9YwL/q5JKMdBnoG+lEI1JWVGFVk0Taaj9Xb5SKgzIBDZoQX5IzMe44ILWODg==} engines: {node: '>=18.0.0'} - eventsource-parser@3.0.2: - resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==} - engines: {node: '>=18.0.0'} - eventsource-parser@3.0.6: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} @@ -11376,6 +11172,14 @@ packages: react: '>= 18.3.0 < 19.0.0' react-dom: '>= 18.3.0 < 19.0.0' + next-safe-action@8.0.11: + resolution: {integrity: sha512-gqJLmnQLAoFCq1kRBopN46New+vx1n9J9Y/qDQLXpv/VqU40AWxDakvshwwnWAt8R0kLvlakNYNLX5PqlXWSMg==} + engines: {node: '>=18.17'} + peerDependencies: + next: '>= 14.0.0' + react: '>= 18.2.0' + react-dom: '>= 18.2.0' + next-themes@0.3.0: resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==} peerDependencies: @@ -11691,11 +11495,23 @@ packages: zod: optional: true + openai@5.22.0: + resolution: {integrity: sha512-uSsYZ+vw9JxUwnMTcT9bj5sGM5qY/4du2BIf1KSqDRZF9nhSlJYsBLPRwBZTOW+HNyjwGviR0SsoDPv5lpPrBw==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - openapi3-ts@4.4.0: - resolution: {integrity: sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==} + openapi3-ts@4.5.0: + resolution: {integrity: sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==} openid-client@6.3.4: resolution: {integrity: sha512-CGZGk9Y6Bv9R4bXlrzVoxzD1n4h8iP914UhjVyRSftqzqO4CWaRqKpOmW253Jmpv4EWkz7/Gut/90iiWW8t0ow==} @@ -12234,8 +12050,8 @@ packages: promptl-ai@0.7.5: resolution: {integrity: sha512-qDJggAR34qqxc5/X6xqagMCVNVQK8U3ycohtKJNG1/1Ap5VJ/UzF4ERphWAmLrspl4KYcT2ITKQhq28TAcw3Qg==} - promptl-ai@0.7.6: - resolution: {integrity: sha512-pBSP296CCB25hGZ+BfectGoF8hsVbdkYITi58JJzklZzcEemIcnMLjrcofXX+9Yv0EpfB7W6ORPX/pNLmQAayQ==} + promptl-ai@0.8.0: + resolution: {integrity: sha512-3Qq8XJEFhL/qEieVqajSrb8+tKLEPEQ9h6s9wKbY7iskHpb4oYkJz4eBhBsp0vCimg/sgIKgAims0YKfLw/0OQ==} prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -12825,9 +12641,6 @@ packages: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} - secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - secure-json-parse@4.0.0: resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} @@ -12913,10 +12726,6 @@ packages: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - sharp@0.34.3: - resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - sharp@0.34.4: resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -13446,10 +13255,6 @@ packages: resolution: {integrity: sha512-UpsBhOC45a45TpeHOXE4wwYwL8uD2apbHTbtBvkwtUU4dNwCjC7DpQTjw2Q6eIdfNtw+dKthdwq94uLXTJPfFw==} engines: {node: '>= 4.1.0'} - throttleit@2.1.0: - resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} - engines: {node: '>=18'} - through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -14396,16 +14201,8 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zsa-react@0.2.3: - resolution: {integrity: sha512-THCoIIhtEU3jXTrNBM/PBc0PyHCekizSGEc+fg0LGo1NM1J02LFQN7ICR948X25IjtkZdvxUZmT0+M73GG8eEw==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - zod: ^3.23.5 - - zsa@0.5.1: - resolution: {integrity: sha512-8QI/SQrcdVFTlcFYGvKbB9UroLVZ0LJNEI1aVZUD2ASsRWqrgz1aEGqPAvuMgr+MM9+PQ3AyH8zspAcvbd2FoA==} - peerDependencies: - zod: ^3.23.5 + zod@4.1.8: + resolution: {integrity: sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==} zustand@4.5.6: resolution: {integrity: sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==} @@ -14427,178 +14224,111 @@ packages: snapshots: - '@ai-sdk/amazon-bedrock@2.2.10(zod@3.24.2)': + '@ai-sdk/amazon-bedrock@3.0.21(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + '@ai-sdk/anthropic': 2.0.17(zod@4.1.8) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) '@smithy/eventstream-codec': 4.0.4 '@smithy/util-utf8': 4.0.0 aws4fetch: 1.0.20 - zod: 3.24.2 + zod: 4.1.8 - '@ai-sdk/anthropic@1.1.0(zod@3.24.2)': + '@ai-sdk/anthropic@2.0.17(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.1.0(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/anthropic@1.2.12(zod@3.24.2)': + '@ai-sdk/azure@2.0.30(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/openai': 2.0.30(zod@4.1.8) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/azure@1.1.0(zod@3.24.2)': + '@ai-sdk/deepseek@1.0.17(zod@4.1.8)': dependencies: - '@ai-sdk/openai': 1.1.0(zod@3.24.2) - '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.1.0(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/openai-compatible': 1.0.17(zod@4.1.8) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/deepseek@0.2.5(zod@3.24.2)': + '@ai-sdk/gateway@1.0.23(zod@4.1.8)': dependencies: - '@ai-sdk/openai-compatible': 0.2.5(zod@3.24.2) - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.3(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/google-vertex@2.2.27(zod@3.24.2)': + '@ai-sdk/google-vertex@3.0.27(zod@4.1.8)': dependencies: - '@ai-sdk/anthropic': 1.2.12(zod@3.24.2) - '@ai-sdk/google': 1.2.22(zod@3.24.2) - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) + '@ai-sdk/anthropic': 2.0.17(zod@4.1.8) + '@ai-sdk/google': 2.0.14(zod@4.1.8) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) google-auth-library: 9.15.1 - zod: 3.24.2 + zod: 4.1.8 transitivePeerDependencies: - encoding - supports-color - '@ai-sdk/google@1.2.18(zod@3.24.2)': + '@ai-sdk/google@2.0.14(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/google@1.2.22(zod@3.24.2)': + '@ai-sdk/mistral@2.0.14(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/mistral@1.1.0(zod@3.24.2)': + '@ai-sdk/openai-compatible@1.0.17(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.1.0(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/openai-compatible@0.2.5(zod@3.24.2)': + '@ai-sdk/openai@2.0.30(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.3(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/openai@1.1.0(zod@3.24.2)': + '@ai-sdk/perplexity@2.0.9(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.0.4 - '@ai-sdk/provider-utils': 2.1.0(zod@3.24.2) - zod: 3.24.2 - - '@ai-sdk/openai@1.3.22(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - zod: 3.24.2 - - '@ai-sdk/perplexity@1.1.3(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.3(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 - '@ai-sdk/provider-utils@2.1.0(zod@3.24.2)': + '@ai-sdk/provider-utils@3.0.9(zod@4.1.8)': dependencies: - '@ai-sdk/provider': 1.0.4 + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 eventsource-parser: 3.0.6 - nanoid: 3.3.11 - secure-json-parse: 2.7.0 - optionalDependencies: - zod: 3.24.2 - - '@ai-sdk/provider-utils@2.2.0(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.0 - eventsource-parser: 3.0.2 - nanoid: 3.3.11 - secure-json-parse: 2.7.0 - zod: 3.24.2 - - '@ai-sdk/provider-utils@2.2.3(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.0 - nanoid: 3.3.11 - secure-json-parse: 2.7.0 - zod: 3.24.2 + zod: 4.1.8 - '@ai-sdk/provider-utils@2.2.8(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.3 - nanoid: 3.3.11 - secure-json-parse: 2.7.0 - zod: 3.24.2 - - '@ai-sdk/provider@1.0.4': - dependencies: - json-schema: 0.4.0 - - '@ai-sdk/provider@1.1.0': + '@ai-sdk/provider@2.0.0': dependencies: json-schema: 0.4.0 - '@ai-sdk/provider@1.1.3': - dependencies: - json-schema: 0.4.0 - - '@ai-sdk/react@1.2.0(react@19.1.1)(zod@3.24.2)': - dependencies: - '@ai-sdk/provider-utils': 2.2.0(zod@3.24.2) - '@ai-sdk/ui-utils': 1.2.0(zod@3.24.2) - react: 19.1.1 - swr: 2.2.5(react@19.1.1) - throttleit: 2.1.0 - optionalDependencies: - zod: 3.24.2 - - '@ai-sdk/react@1.2.12(react@19.1.1)(zod@3.24.2)': + '@ai-sdk/rsc@1.0.44(react@19.1.1)(zod@4.1.8)': dependencies: - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - '@ai-sdk/ui-utils': 1.2.11(zod@3.24.2) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + ai: 5.0.44(zod@4.1.8) + jsondiffpatch: 0.6.0 react: 19.1.1 - swr: 2.2.5(react@19.1.1) - throttleit: 2.1.0 optionalDependencies: - zod: 3.24.2 - - '@ai-sdk/ui-utils@1.2.0(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.0(zod@3.24.2) - zod: 3.24.2 - zod-to-json-schema: 3.24.5(zod@3.24.2) - - '@ai-sdk/ui-utils@1.2.11(zod@3.24.2)': - dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - zod: 3.24.2 - zod-to-json-schema: 3.24.5(zod@3.24.2) + zod: 4.1.8 - '@ai-sdk/xai@1.2.6(zod@3.24.2)': + '@ai-sdk/xai@2.0.18(zod@4.1.8)': dependencies: - '@ai-sdk/openai-compatible': 0.2.5(zod@3.24.2) - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.3(zod@3.24.2) - zod: 3.24.2 + '@ai-sdk/openai-compatible': 1.0.17(zod@4.1.8) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) + zod: 4.1.8 '@alcalzone/ansi-tokenize@0.2.0': dependencies: @@ -14624,6 +14354,8 @@ snapshots: transitivePeerDependencies: - encoding + '@anthropic-ai/sdk@0.62.0': {} + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -14632,10 +14364,10 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@asteasolutions/zod-to-openapi@7.3.3(zod@3.24.2)': + '@asteasolutions/zod-to-openapi@8.1.0(zod@4.1.8)': dependencies: - openapi3-ts: 4.4.0 - zod: 3.24.2 + openapi3-ts: 4.5.0 + zod: 4.1.8 '@asyncapi/parser@3.4.0': dependencies: @@ -14668,7 +14400,7 @@ snapshots: '@aws-crypto/crc32@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.821.0 + '@aws-sdk/types': 3.840.0 tslib: 1.14.1 '@aws-crypto/crc32@5.2.0': @@ -14714,7 +14446,7 @@ snapshots: '@aws-crypto/util@3.0.0': dependencies: - '@aws-sdk/types': 3.821.0 + '@aws-sdk/types': 3.840.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -16814,17 +16546,18 @@ snapshots: dependencies: hono: 4.9.7 - '@hono/zod-openapi@0.16.4(hono@4.9.7)(zod@3.24.2)': + '@hono/zod-openapi@1.1.1(hono@4.9.7)(zod@4.1.8)': dependencies: - '@asteasolutions/zod-to-openapi': 7.3.3(zod@3.24.2) - '@hono/zod-validator': 0.3.0(hono@4.9.7)(zod@3.24.2) + '@asteasolutions/zod-to-openapi': 8.1.0(zod@4.1.8) + '@hono/zod-validator': 0.7.3(hono@4.9.7)(zod@4.1.8) hono: 4.9.7 - zod: 3.24.2 + openapi3-ts: 4.5.0 + zod: 4.1.8 - '@hono/zod-validator@0.3.0(hono@4.9.7)(zod@3.24.2)': + '@hono/zod-validator@0.7.3(hono@4.9.7)(zod@4.1.8)': dependencies: hono: 4.9.7 - zod: 3.24.2 + zod: 4.1.8 '@huggingface/inference@2.8.1': dependencies: @@ -16877,11 +16610,6 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true - '@img/sharp-darwin-arm64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.0 - optional: true - '@img/sharp-darwin-arm64@0.34.4': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.2.3 @@ -16892,11 +16620,6 @@ snapshots: '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true - '@img/sharp-darwin-x64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.0 - optional: true - '@img/sharp-darwin-x64@0.34.4': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.2.3 @@ -16905,78 +16628,51 @@ snapshots: '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.0': - optional: true - '@img/sharp-libvips-darwin-arm64@1.2.3': optional: true '@img/sharp-libvips-darwin-x64@1.0.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.0': - optional: true - '@img/sharp-libvips-darwin-x64@1.2.3': optional: true '@img/sharp-libvips-linux-arm64@1.0.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.0': - optional: true - '@img/sharp-libvips-linux-arm64@1.2.3': optional: true '@img/sharp-libvips-linux-arm@1.0.5': optional: true - '@img/sharp-libvips-linux-arm@1.2.0': - optional: true - '@img/sharp-libvips-linux-arm@1.2.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.0': - optional: true - '@img/sharp-libvips-linux-ppc64@1.2.3': optional: true '@img/sharp-libvips-linux-s390x@1.0.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.0': - optional: true - '@img/sharp-libvips-linux-s390x@1.2.3': optional: true '@img/sharp-libvips-linux-x64@1.0.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.0': - optional: true - '@img/sharp-libvips-linux-x64@1.2.3': optional: true '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': - optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': optional: true '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.0': - optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.3': optional: true @@ -16985,11 +16681,6 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true - '@img/sharp-linux-arm64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.0 - optional: true - '@img/sharp-linux-arm64@0.34.4': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.2.3 @@ -17000,21 +16691,11 @@ snapshots: '@img/sharp-libvips-linux-arm': 1.0.5 optional: true - '@img/sharp-linux-arm@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.0 - optional: true - '@img/sharp-linux-arm@0.34.4': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.2.3 optional: true - '@img/sharp-linux-ppc64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.0 - optional: true - '@img/sharp-linux-ppc64@0.34.4': optionalDependencies: '@img/sharp-libvips-linux-ppc64': 1.2.3 @@ -17025,11 +16706,6 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true - '@img/sharp-linux-s390x@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.0 - optional: true - '@img/sharp-linux-s390x@0.34.4': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.2.3 @@ -17040,11 +16716,6 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.0.4 optional: true - '@img/sharp-linux-x64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.0 - optional: true - '@img/sharp-linux-x64@0.34.4': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.2.3 @@ -17055,11 +16726,6 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 - optional: true - '@img/sharp-linuxmusl-arm64@0.34.4': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 @@ -17070,11 +16736,6 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - optional: true - '@img/sharp-linuxmusl-x64@0.34.4': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.2.3 @@ -17085,37 +16746,23 @@ snapshots: '@emnapi/runtime': 1.5.0 optional: true - '@img/sharp-wasm32@0.34.3': - dependencies: - '@emnapi/runtime': 1.5.0 - optional: true - '@img/sharp-wasm32@0.34.4': dependencies: '@emnapi/runtime': 1.5.0 optional: true - '@img/sharp-win32-arm64@0.34.3': - optional: true - '@img/sharp-win32-arm64@0.34.4': optional: true '@img/sharp-win32-ia32@0.33.5': optional: true - '@img/sharp-win32-ia32@0.34.3': - optional: true - '@img/sharp-win32-ia32@0.34.4': optional: true '@img/sharp-win32-x64@0.33.5': optional: true - '@img/sharp-win32-x64@0.34.3': - optional: true - '@img/sharp-win32-x64@0.34.4': optional: true @@ -17383,14 +17030,14 @@ snapshots: - encoding - utf-8-validate - '@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))': + '@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.20 - langsmith: 0.3.31(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) + langsmith: 0.3.31(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -17400,9 +17047,9 @@ snapshots: transitivePeerDependencies: - openai - '@langchain/openai@0.5.12(@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))': + '@langchain/openai@0.5.12(@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))': dependencies: - '@langchain/core': 0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) + '@langchain/core': 0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) js-tiktoken: 1.0.20 openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.25.76) zod: 3.25.76 @@ -17411,9 +17058,9 @@ snapshots: - encoding - ws - '@langchain/textsplitters@0.1.0(@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)))': + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)))': dependencies: - '@langchain/core': 0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) + '@langchain/core': 0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) js-tiktoken: 1.0.20 '@latitude-data/sdk@5.1.0(typescript@5.8.3)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))': @@ -17666,11 +17313,11 @@ snapshots: - supports-color - tiktoken - '@llamaindex/chroma@0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(cohere-ai@7.17.1)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))(pathe@1.1.2)': + '@llamaindex/chroma@0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(cohere-ai@7.17.1)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))(pathe@1.1.2)': dependencies: '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - chromadb: 1.10.3(cohere-ai@7.17.1)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) + chromadb: 1.10.3(cohere-ai@7.17.1)(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) chromadb-default-embed: 2.14.0 transitivePeerDependencies: - '@aws-crypto/sha256-js' @@ -17686,12 +17333,12 @@ snapshots: - tiktoken - voyageai - '@llamaindex/clip@0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)': + '@llamaindex/clip@0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)': dependencies: '@huggingface/transformers': 3.5.2 '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) transitivePeerDependencies: - '@aws-crypto/sha256-js' - encoding @@ -17725,7 +17372,7 @@ snapshots: '@llamaindex/core@0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)': dependencies: '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@types/node': 22.15.31 + '@types/node': 22.18.0 magic-bytes.js: 1.12.1 zod: 3.25.76 zod-to-json-schema: 3.24.5(zod@3.25.76) @@ -17737,11 +17384,11 @@ snapshots: - pathe - tiktoken - '@llamaindex/deepinfra@0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)': + '@llamaindex/deepinfra@0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)': dependencies: '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) transitivePeerDependencies: - '@aws-crypto/sha256-js' - '@huggingface/transformers' @@ -17778,10 +17425,10 @@ snapshots: - supports-color - tiktoken - '@llamaindex/groq@0.0.50(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)': + '@llamaindex/groq@0.0.50(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)': dependencies: '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) groq-sdk: 0.8.0 transitivePeerDependencies: - '@aws-crypto/sha256-js' @@ -17794,13 +17441,13 @@ snapshots: - ws - zod - '@llamaindex/huggingface@0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)': + '@llamaindex/huggingface@0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)': dependencies: '@huggingface/inference': 2.8.1 '@huggingface/transformers': 3.5.2 '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) transitivePeerDependencies: - '@aws-crypto/sha256-js' - encoding @@ -17825,11 +17472,11 @@ snapshots: - pathe - tiktoken - '@llamaindex/mistral@0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(zod@3.24.2)': + '@llamaindex/mistral@0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(zod@4.1.8)': dependencies: '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@mistralai/mistralai': 1.7.2(zod@3.24.2) + '@mistralai/mistralai': 1.7.2(zod@4.1.8) transitivePeerDependencies: - '@aws-crypto/sha256-js' - '@huggingface/transformers' @@ -17895,11 +17542,11 @@ snapshots: - pathe - tiktoken - '@llamaindex/openai@0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)': + '@llamaindex/openai@0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)': dependencies: '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) transitivePeerDependencies: - '@aws-crypto/sha256-js' - '@huggingface/transformers' @@ -18023,9 +17670,9 @@ snapshots: - pathe - tiktoken - '@llamaindex/vllm@0.0.21(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)': + '@llamaindex/vllm@0.0.21(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)': dependencies: - '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) transitivePeerDependencies: - '@aws-crypto/sha256-js' - '@huggingface/transformers' @@ -18059,7 +17706,7 @@ snapshots: '@mapbox/node-pre-gyp@1.0.11': dependencies: - detect-libc: 2.0.4 + detect-libc: 2.1.1 https-proxy-agent: 5.0.1 make-dir: 3.1.0 node-fetch: 2.7.0 @@ -18349,10 +17996,10 @@ snapshots: transitivePeerDependencies: - debug - '@mistralai/mistralai@1.7.2(zod@3.24.2)': + '@mistralai/mistralai@1.7.2(zod@4.1.8)': dependencies: - zod: 3.24.2 - zod-to-json-schema: 3.24.5(zod@3.24.2) + zod: 4.1.8 + zod-to-json-schema: 3.24.5(zod@4.1.8) '@mixedbread-ai/sdk@2.2.11': dependencies: @@ -20730,6 +20377,8 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} + '@standard-schema/spec@1.0.0': {} + '@stoplight/better-ajv-errors@1.0.3(ajv@8.17.1)': dependencies: ajv: 8.17.1 @@ -20738,7 +20387,7 @@ snapshots: '@stoplight/json-ref-readers@1.2.2': dependencies: - node-fetch: 2.6.7 + node-fetch: 2.7.0 tslib: 1.14.1 transitivePeerDependencies: - encoding @@ -20883,28 +20532,17 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@t3-oss/env-core@0.10.1(typescript@5.8.3)(zod@3.24.2)': - dependencies: - zod: 3.24.2 - optionalDependencies: - typescript: 5.8.3 - - '@t3-oss/env-core@0.13.8(typescript@5.8.3)(zod@3.24.2)': - optionalDependencies: - typescript: 5.8.3 - zod: 3.24.2 - - '@t3-oss/env-core@0.13.8(typescript@5.8.3)(zod@3.25.76)': + '@t3-oss/env-core@0.13.8(typescript@5.8.3)(zod@4.1.8)': optionalDependencies: typescript: 5.8.3 - zod: 3.25.76 + zod: 4.1.8 - '@t3-oss/env-nextjs@0.10.1(typescript@5.8.3)(zod@3.24.2)': + '@t3-oss/env-nextjs@0.13.8(typescript@5.8.3)(zod@4.1.8)': dependencies: - '@t3-oss/env-core': 0.10.1(typescript@5.8.3)(zod@3.24.2) - zod: 3.24.2 + '@t3-oss/env-core': 0.13.8(typescript@5.8.3)(zod@4.1.8) optionalDependencies: typescript: 5.8.3 + zod: 4.1.8 '@tailwindcss/typography@0.5.16(tailwindcss@3.4.17)': dependencies: @@ -21304,7 +20942,7 @@ snapshots: '@types/progress-stream@2.0.5': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.18.0 '@types/qs@6.14.0': {} @@ -21358,11 +20996,11 @@ snapshots: '@types/stream-buffers@3.0.7': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.18.0 '@types/tar@6.1.13': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.18.0 minipass: 4.2.8 '@types/tedious@4.0.14': @@ -21371,7 +21009,7 @@ snapshots: '@types/through@0.0.33': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.18.0 '@types/tough-cookie@4.0.5': {} @@ -21395,7 +21033,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 22.15.31 + '@types/node': 22.18.0 '@types/yauzl@2.10.3': dependencies: @@ -21934,30 +21572,13 @@ snapshots: clean-stack: 4.2.0 indent-string: 5.0.0 - ai@4.2.1(react@19.1.1)(zod@3.24.2): - dependencies: - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.0(zod@3.24.2) - '@ai-sdk/react': 1.2.0(react@19.1.1)(zod@3.24.2) - '@ai-sdk/ui-utils': 1.2.0(zod@3.24.2) - '@opentelemetry/api': 1.9.0 - eventsource-parser: 3.0.2 - jsondiffpatch: 0.6.0 - zod: 3.24.2 - optionalDependencies: - react: 19.1.1 - - ai@4.3.16(react@19.1.1)(zod@3.24.2): + ai@5.0.44(zod@4.1.8): dependencies: - '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.24.2) - '@ai-sdk/react': 1.2.12(react@19.1.1)(zod@3.24.2) - '@ai-sdk/ui-utils': 1.2.11(zod@3.24.2) + '@ai-sdk/gateway': 1.0.23(zod@4.1.8) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.9(zod@4.1.8) '@opentelemetry/api': 1.9.0 - jsondiffpatch: 0.6.0 - zod: 3.24.2 - optionalDependencies: - react: 19.1.1 + zod: 4.1.8 ajv-draft-04@1.0.0(ajv@8.17.1): optionalDependencies: @@ -22601,13 +22222,13 @@ snapshots: transitivePeerDependencies: - bare-buffer - chromadb@1.10.3(cohere-ai@7.17.1)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)): + chromadb@1.10.3(cohere-ai@7.17.1)(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)): dependencies: cliui: 8.0.1 isomorphic-fetch: 3.0.0 optionalDependencies: cohere-ai: 7.17.1 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + openai: 5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) transitivePeerDependencies: - encoding @@ -23215,8 +22836,6 @@ snapshots: detect-indent@7.0.1: {} - detect-libc@2.0.4: {} - detect-libc@2.1.1: {} detect-newline@3.1.0: {} @@ -24069,8 +23688,6 @@ snapshots: eventsource-parser@2.0.1: {} - eventsource-parser@3.0.2: {} - eventsource-parser@3.0.6: {} eventsource@3.0.7: @@ -25732,15 +25349,15 @@ snapshots: kuler@2.0.0: {} - langchain@0.3.27(@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)))(axios@1.10.0)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)): + langchain@0.3.27(@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)))(axios@1.10.0)(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)): dependencies: - '@langchain/core': 0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) - '@langchain/openai': 0.5.12(@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.57(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))) + '@langchain/core': 0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) + '@langchain/openai': 0.5.12(@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.57(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))) js-tiktoken: 1.0.20 js-yaml: 4.1.0 jsonpointer: 5.0.1 - langsmith: 0.3.31(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)) + langsmith: 0.3.31(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 @@ -25754,7 +25371,7 @@ snapshots: - openai - ws - langsmith@0.3.31(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2)): + langsmith@0.3.31(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 @@ -25764,7 +25381,7 @@ snapshots: semver: 7.7.2 uuid: 10.0.0 optionalDependencies: - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + openai: 5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) language-subtag-registry@0.3.23: {} @@ -25803,28 +25420,28 @@ snapshots: lines-and-columns@1.2.4: {} - llamaindex@0.8.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(bufferutil@4.0.9)(cohere-ai@7.17.1)(js-tiktoken@1.0.20)(jsdom@24.1.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))(pathe@1.1.2)(pg-query-stream@4.8.1(pg@8.14.1))(pg@8.14.1)(socks@2.8.5)(terser@5.44.0)(tree-sitter@0.22.4)(typescript@5.8.3)(utf-8-validate@6.0.5)(web-tree-sitter@0.24.7)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2): + llamaindex@0.8.37(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(bufferutil@4.0.9)(cohere-ai@7.17.1)(js-tiktoken@1.0.20)(jsdom@24.1.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))(pathe@1.1.2)(pg-query-stream@4.8.1(pg@8.14.1))(pg@8.14.1)(socks@2.8.5)(terser@5.44.0)(tree-sitter@0.22.4)(typescript@5.8.3)(utf-8-validate@6.0.5)(web-tree-sitter@0.24.7)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8): dependencies: '@llamaindex/anthropic': 0.0.33(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(@types/node@22.15.31)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(jsdom@24.1.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@6.0.5))(msw@2.10.2(@types/node@22.15.31)(typescript@5.8.3))(pathe@1.1.2)(terser@5.44.0) '@llamaindex/astra': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/azure': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(socks@2.8.5) - '@llamaindex/chroma': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(cohere-ai@7.17.1)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2))(pathe@1.1.2) - '@llamaindex/clip': 0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/chroma': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(cohere-ai@7.17.1)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8))(pathe@1.1.2) + '@llamaindex/clip': 0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) '@llamaindex/cloud': 2.0.24(@llamaindex/core@0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2))(@llamaindex/env@0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)) '@llamaindex/cohere': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/core': 0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/deepinfra': 0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/deepinfra': 0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) '@llamaindex/env': 0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/google': 0.0.6(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/groq': 0.0.50(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) - '@llamaindex/huggingface': 0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/groq': 0.0.50(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) + '@llamaindex/huggingface': 0.0.35(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) '@llamaindex/milvus': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/mistral': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(zod@3.24.2) + '@llamaindex/mistral': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(zod@4.1.8) '@llamaindex/mixedbread': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/mongodb': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/credential-providers@3.826.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(socks@2.8.5) '@llamaindex/node-parser': 0.0.24(@llamaindex/core@0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2))(@llamaindex/env@0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7) '@llamaindex/ollama': 0.0.39(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/openai': 0.1.51(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) '@llamaindex/pinecone': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/portkey-ai': 0.0.32(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/postgres': 0.0.32(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(pg-query-stream@4.8.1(pg@8.14.1))(pg@8.14.1) @@ -25832,7 +25449,7 @@ snapshots: '@llamaindex/readers': 1.0.25(@aws-sdk/credential-providers@3.826.0)(@llamaindex/core@0.4.23(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2))(@llamaindex/env@0.1.27(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2))(bufferutil@4.0.9)(socks@2.8.5)(utf-8-validate@6.0.5) '@llamaindex/replicate': 0.0.32(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@llamaindex/upstash': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) - '@llamaindex/vllm': 0.0.21(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2) + '@llamaindex/vllm': 0.0.21(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) '@llamaindex/weaviate': 0.0.4(@aws-crypto/sha256-js@5.2.0)(@huggingface/transformers@3.5.2)(gpt-tokenizer@2.9.0)(js-tiktoken@1.0.20)(pathe@1.1.2) '@types/lodash': 4.17.17 '@types/node': 22.15.31 @@ -26855,6 +26472,12 @@ snapshots: - supports-color - unified + next-safe-action@8.0.11(next@15.5.4(@opentelemetry/api@1.8.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + dependencies: + next: 15.5.4(@opentelemetry/api@1.8.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + next-themes@0.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 @@ -26905,7 +26528,7 @@ snapshots: '@next/swc-win32-arm64-msvc': 15.5.4 '@next/swc-win32-x64-msvc': 15.5.4 '@opentelemetry/api': 1.8.0 - sharp: 0.34.3 + sharp: 0.34.4 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -26977,7 +26600,7 @@ snapshots: node-gyp-build-optional-packages@5.2.2: dependencies: - detect-libc: 2.0.4 + detect-libc: 2.1.1 optional: true node-gyp-build@3.9.0: {} @@ -27252,7 +26875,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.104.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.25.76): + openai@4.104.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8): dependencies: '@types/node': 18.19.111 '@types/node-fetch': 2.6.12 @@ -27263,11 +26886,11 @@ snapshots: node-fetch: 2.7.0 optionalDependencies: ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) - zod: 3.25.76 + zod: 4.1.8 transitivePeerDependencies: - encoding - openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.24.2): + openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.25.76): dependencies: '@types/node': 18.19.111 '@types/node-fetch': 2.6.12 @@ -27278,11 +26901,11 @@ snapshots: node-fetch: 2.7.0 optionalDependencies: ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) - zod: 3.24.2 + zod: 3.25.76 transitivePeerDependencies: - encoding - openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.25.76): + openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8): dependencies: '@types/node': 18.19.111 '@types/node-fetch': 2.6.12 @@ -27293,13 +26916,18 @@ snapshots: node-fetch: 2.7.0 optionalDependencies: ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) - zod: 3.25.76 + zod: 4.1.8 transitivePeerDependencies: - encoding + openai@5.22.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8): + optionalDependencies: + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) + zod: 4.1.8 + openapi-types@12.1.3: {} - openapi3-ts@4.4.0: + openapi3-ts@4.5.0: dependencies: yaml: 2.8.1 @@ -27805,7 +27433,7 @@ snapshots: prebuild-install@7.1.3: dependencies: - detect-libc: 2.0.4 + detect-libc: 2.1.1 expand-template: 2.0.3 github-from-package: 0.0.0 minimist: 1.2.8 @@ -27869,28 +27497,28 @@ snapshots: - encoding - ws - promptl-ai@0.7.6(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)): + promptl-ai@0.8.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)): dependencies: acorn: 8.15.0 code-red: 1.0.4 fast-sha256: 1.3.0 locate-character: 3.0.0 - openai: 4.104.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.25.76) + openai: 4.104.0(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) yaml: 2.4.5 - zod: 3.25.76 + zod: 4.1.8 transitivePeerDependencies: - encoding - ws - promptl-ai@0.7.6(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)): + promptl-ai@0.8.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5)): dependencies: acorn: 8.15.0 code-red: 1.0.4 fast-sha256: 1.3.0 locate-character: 3.0.0 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@3.25.76) + openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5))(zod@4.1.8) yaml: 2.4.5 - zod: 3.25.76 + zod: 4.1.8 transitivePeerDependencies: - encoding - ws @@ -28758,8 +28386,6 @@ snapshots: extend-shallow: 2.0.1 kind-of: 6.0.3 - secure-json-parse@2.7.0: {} - secure-json-parse@4.0.0: {} selderee@0.11.0: @@ -28873,7 +28499,7 @@ snapshots: sharp@0.32.6: dependencies: color: 4.2.3 - detect-libc: 2.0.4 + detect-libc: 2.1.1 node-addon-api: 6.1.0 prebuild-install: 7.1.3 semver: 7.7.2 @@ -28886,7 +28512,7 @@ snapshots: sharp@0.33.5: dependencies: color: 4.2.3 - detect-libc: 2.0.4 + detect-libc: 2.1.1 semver: 7.7.2 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 @@ -28909,36 +28535,6 @@ snapshots: '@img/sharp-win32-ia32': 0.33.5 '@img/sharp-win32-x64': 0.33.5 - sharp@0.34.3: - dependencies: - color: 4.2.3 - detect-libc: 2.0.4 - semver: 7.7.2 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.3 - '@img/sharp-darwin-x64': 0.34.3 - '@img/sharp-libvips-darwin-arm64': 1.2.0 - '@img/sharp-libvips-darwin-x64': 1.2.0 - '@img/sharp-libvips-linux-arm': 1.2.0 - '@img/sharp-libvips-linux-arm64': 1.2.0 - '@img/sharp-libvips-linux-ppc64': 1.2.0 - '@img/sharp-libvips-linux-s390x': 1.2.0 - '@img/sharp-libvips-linux-x64': 1.2.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - '@img/sharp-linux-arm': 0.34.3 - '@img/sharp-linux-arm64': 0.34.3 - '@img/sharp-linux-ppc64': 0.34.3 - '@img/sharp-linux-s390x': 0.34.3 - '@img/sharp-linux-x64': 0.34.3 - '@img/sharp-linuxmusl-arm64': 0.34.3 - '@img/sharp-linuxmusl-x64': 0.34.3 - '@img/sharp-wasm32': 0.34.3 - '@img/sharp-win32-arm64': 0.34.3 - '@img/sharp-win32-ia32': 0.34.3 - '@img/sharp-win32-x64': 0.34.3 - optional: true - sharp@0.34.4: dependencies: '@img/colour': 1.0.0 @@ -29653,8 +29249,6 @@ snapshots: - bufferutil - utf-8-validate - throttleit@2.1.0: {} - through2@2.0.5: dependencies: readable-stream: 2.3.8 @@ -30819,29 +30413,21 @@ snapshots: yoga-layout@3.2.1: {} - zod-to-json-schema@3.24.5(zod@3.24.2): - dependencies: - zod: 3.24.2 - zod-to-json-schema@3.24.5(zod@3.25.76): dependencies: zod: 3.25.76 + zod-to-json-schema@3.24.5(zod@4.1.8): + dependencies: + zod: 4.1.8 + zod@3.23.8: {} zod@3.24.2: {} zod@3.25.76: {} - zsa-react@0.2.3(react@19.1.1)(zod@3.24.2): - dependencies: - react: 19.1.1 - zod: 3.24.2 - zsa: 0.5.1(zod@3.24.2) - - zsa@0.5.1(zod@3.24.2): - dependencies: - zod: 3.24.2 + zod@4.1.8: {} zustand@4.5.6(@types/react@19.1.15)(immer@9.0.21)(react@19.1.1): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a68ba84f82..f110ad64d7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,12 +5,13 @@ packages: - 'tools/*' - '!examples/**' catalog: + ai: 5.0.44 pg: 8.16.0 drizzle-orm: 0.44.2 drizzle-kit: 0.31.1 dd-trace: 5.43.0 - promptl-ai: 0.7.6 - zod: 3.24.2 + promptl-ai: 0.8.0 + zod: 4.1.8 react: 19.1.1 react-dom: 19.1.1 '@types/react': 19.1.15