diff --git a/apps/app/package.json b/apps/app/package.json index 4d424e57d..553263619 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -91,7 +91,7 @@ "use-long-press": "^3.3.0", "xml2js": "^0.6.2", "zaraz-ts": "^1.2.0", - "zod": "^4.0.17", + "zod": "^3.25.76", "zustand": "^5.0.3" }, "devDependencies": { diff --git a/apps/app/src/actions/schema.ts b/apps/app/src/actions/schema.ts index d92cad218..8a95aa782 100644 --- a/apps/app/src/actions/schema.ts +++ b/apps/app/src/actions/schema.ts @@ -65,7 +65,7 @@ export const organizationWebsiteSchema = z.object({ export const createRiskSchema = z.object({ title: z .string({ - error: 'Risk name is required', + required_error: 'Risk name is required', }) .min(1, { message: 'Risk name should be at least 1 character', @@ -75,7 +75,7 @@ export const createRiskSchema = z.object({ }), description: z .string({ - error: 'Risk description is required', + required_error: 'Risk description is required', }) .min(1, { message: 'Risk description should be at least 1 character', @@ -84,10 +84,10 @@ export const createRiskSchema = z.object({ message: 'Risk description should be at most 255 characters', }), category: z.nativeEnum(RiskCategory, { - error: 'Risk category is required', + required_error: 'Risk category is required', }), department: z.nativeEnum(Departments, { - error: 'Risk department is required', + required_error: 'Risk department is required', }), assigneeId: z.string().optional().nullable(), }); @@ -103,14 +103,14 @@ export const updateRiskSchema = z.object({ message: 'Risk description is required', }), category: z.nativeEnum(RiskCategory, { - error: 'Risk category is required', + required_error: 'Risk category is required', }), department: z.nativeEnum(Departments, { - error: 'Risk department is required', + required_error: 'Risk department is required', }), assigneeId: z.string().optional().nullable(), status: z.nativeEnum(RiskStatus, { - error: 'Risk status is required', + required_error: 'Risk status is required', }), }); @@ -162,7 +162,7 @@ export const updateTaskSchema = z.object({ description: z.string().optional(), dueDate: z.date().optional(), status: z.nativeEnum(TaskStatus, { - error: 'Task status is required', + required_error: 'Task status is required', }), assigneeId: z.string().optional().nullable(), }); @@ -251,8 +251,10 @@ export const updateResidualRiskEnumSchema = z.object({ // Policies export const createPolicySchema = z.object({ - title: z.string({ error: 'Title is required' }).min(1, 'Title is required'), - description: z.string({ error: 'Description is required' }).min(1, 'Description is required'), + title: z.string({ required_error: 'Title is required' }).min(1, 'Title is required'), + description: z + .string({ required_error: 'Description is required' }) + .min(1, 'Description is required'), frameworkIds: z.array(z.string()).optional(), controlIds: z.array(z.string()).optional(), entityId: z.string().optional(), @@ -279,7 +281,7 @@ export const createEmployeeSchema = z.object({ name: z.string().min(1, 'Name is required'), email: z.string().email('Invalid email address'), department: z.nativeEnum(Departments, { - error: 'Department is required', + required_error: 'Department is required', }), externalEmployeeId: z.string().optional(), isActive: z.boolean().default(true), diff --git a/apps/app/src/app/(app)/[orgId]/vendors/[vendorId]/actions/schema.ts b/apps/app/src/app/(app)/[orgId]/vendors/[vendorId]/actions/schema.ts index 6cb96fa10..fa93708db 100644 --- a/apps/app/src/app/(app)/[orgId]/vendors/[vendorId]/actions/schema.ts +++ b/apps/app/src/app/(app)/[orgId]/vendors/[vendorId]/actions/schema.ts @@ -24,7 +24,7 @@ export const createVendorTaskSchema = z.object({ message: 'Description is required', }), dueDate: z.date({ - error: 'Due date is required', + required_error: 'Due date is required', }), assigneeId: z.string().nullable(), }); @@ -79,7 +79,7 @@ export const updateVendorTaskSchema = z.object({ }), dueDate: z.date().optional(), status: z.nativeEnum(TaskStatus, { - error: 'Task status is required', + required_error: 'Task status is required', }), assigneeId: z.string().nullable(), }); diff --git a/apps/app/src/jobs/tasks/onboarding/onboard-organization-helpers.ts b/apps/app/src/jobs/tasks/onboarding/onboard-organization-helpers.ts index f09d05bfd..c0ae68613 100644 --- a/apps/app/src/jobs/tasks/onboarding/onboard-organization-helpers.ts +++ b/apps/app/src/jobs/tasks/onboarding/onboard-organization-helpers.ts @@ -8,13 +8,13 @@ import { Likelihood, Risk, RiskCategory, + RiskStatus, RiskTreatmentType, VendorCategory, } from '@db'; import { logger, tasks } from '@trigger.dev/sdk'; -import { generateObject, generateText } from 'ai'; +import { generateObject, generateText, jsonSchema } from 'ai'; import axios from 'axios'; -import z from 'zod'; import type { researchVendor } from '../scrape/research'; import { RISK_MITIGATION_PROMPT } from './prompts/risk-mitigation'; import { VENDOR_RISK_ASSESSMENT_PROMPT } from './prompts/vendor-risk-assessment'; @@ -53,6 +53,58 @@ export type RiskData = { department: Departments; }; +// Baseline risks that must always exist for every organization regardless of frameworks +const BASELINE_RISKS: Array<{ + title: string; + description: string; + category: RiskCategory; + department: Departments; + status: RiskStatus; +}> = [ + { + title: 'Intentional Fraud and Misuse', + description: + 'Intentional misrepresentation or deception by an internal actor (employee, contractor) or by the organization as a whole, for the purpose of achieving an unauthorized or improper gain.', + category: RiskCategory.governance, + department: Departments.gov, + status: RiskStatus.closed, + }, +]; + +/** + * Ensures baseline risks are present for the organization. + * Creates them if missing. Returns the list of risks that were created. + */ +export async function ensureBaselineRisks(organizationId: string): Promise { + const created: Risk[] = []; + + for (const base of BASELINE_RISKS) { + const existing = await db.risk.findFirst({ + where: { + organizationId, + title: base.title, + }, + }); + + if (!existing) { + const risk = await db.risk.create({ + data: { + title: base.title, + description: base.description, + category: base.category, + department: base.department, + status: base.status, + organizationId, + }, + }); + created.push(risk); + logger.info(`Created baseline risk: ${risk.id} (${risk.title})`); + } + } + + return created; +} + /** * Revalidates the organization path for cache busting */ @@ -114,28 +166,47 @@ export async function getOrganizationContext(organizationId: string) { export async function extractVendorsFromContext( questionsAndAnswers: ContextItem[], ): Promise { - const result = await generateObject({ + const { object } = await generateObject({ model: openai('gpt-4.1-mini'), - schema: z.object({ - vendors: z.array( - z.object({ - vendor_name: z.string(), - vendor_website: z.string(), - vendor_description: z.string(), - category: z.enum(Object.values(VendorCategory) as [string, ...string[]]), - inherent_probability: z.enum(Object.values(Likelihood) as [string, ...string[]]), - inherent_impact: z.enum(Object.values(Impact) as [string, ...string[]]), - residual_probability: z.enum(Object.values(Likelihood) as [string, ...string[]]), - residual_impact: z.enum(Object.values(Impact) as [string, ...string[]]), - }), - ), + mode: 'json', + schema: jsonSchema({ + type: 'object', + properties: { + vendors: { + type: 'array', + items: { + type: 'object', + properties: { + vendor_name: { type: 'string' }, + vendor_website: { type: 'string' }, + vendor_description: { type: 'string' }, + category: { type: 'string', enum: Object.values(VendorCategory) }, + inherent_probability: { type: 'string', enum: Object.values(Likelihood) }, + inherent_impact: { type: 'string', enum: Object.values(Impact) }, + residual_probability: { type: 'string', enum: Object.values(Likelihood) }, + residual_impact: { type: 'string', enum: Object.values(Impact) }, + }, + required: [ + 'vendor_name', + 'vendor_website', + 'vendor_description', + 'category', + 'inherent_probability', + 'inherent_impact', + 'residual_probability', + 'residual_impact', + ], + }, + }, + }, + required: ['vendors'], }), system: 'Extract vendor names from the following questions and answers. Return their name (grammar-correct), website, description, category, inherent probability, inherent impact, residual probability, and residual impact.', prompt: questionsAndAnswers.map((q) => `${q.question}\n${q.answer}`).join('\n'), }); - return result.object.vendors as VendorData[]; + return (object as { vendors: VendorData[] }).vendors; } /** @@ -335,23 +406,40 @@ export async function extractRisksFromContext( organizationName: string, existingRisks: { title: string }[], ): Promise { - const result = await generateObject({ + const { object } = await generateObject({ model: openai('gpt-4.1-mini'), - schema: z.object({ - risks: z.array( - z.object({ - risk_name: z.string(), - risk_description: z.string(), - risk_treatment_strategy: z.enum( - Object.values(RiskTreatmentType) as [string, ...string[]], - ), - risk_treatment_strategy_description: z.string(), - risk_residual_probability: z.enum(Object.values(Likelihood) as [string, ...string[]]), - risk_residual_impact: z.enum(Object.values(Impact) as [string, ...string[]]), - category: z.enum(Object.values(RiskCategory) as [string, ...string[]]), - department: z.enum(Object.values(Departments) as [string, ...string[]]), - }), - ), + mode: 'json', + schema: jsonSchema({ + type: 'object', + properties: { + risks: { + type: 'array', + items: { + type: 'object', + properties: { + risk_name: { type: 'string' }, + risk_description: { type: 'string' }, + risk_treatment_strategy: { type: 'string', enum: Object.values(RiskTreatmentType) }, + risk_treatment_strategy_description: { type: 'string' }, + risk_residual_probability: { type: 'string', enum: Object.values(Likelihood) }, + risk_residual_impact: { type: 'string', enum: Object.values(Impact) }, + category: { type: 'string', enum: Object.values(RiskCategory) }, + department: { type: 'string', enum: Object.values(Departments) }, + }, + required: [ + 'risk_name', + 'risk_description', + 'risk_treatment_strategy', + 'risk_treatment_strategy_description', + 'risk_residual_probability', + 'risk_residual_impact', + 'category', + 'department', + ], + }, + }, + }, + required: ['risks'], }), system: `Create a list of 8-12 risks that are relevant to the organization. Use action-oriented language, assume reviewers understand basic termilology - skip definitions. Your mandate is to propose risks that satisfy both ISO 27001:2022 clause 6.1 (risk management) and SOC 2 trust services criteria CC3 and CC4. @@ -367,7 +455,7 @@ export async function extractRisksFromContext( `, }); - return result.object.risks as RiskData[]; + return (object as { risks: RiskData[] }).risks; } /** @@ -489,7 +577,10 @@ export async function createRisks( organizationId: string, organizationName: string, ): Promise { - // Get existing risks to avoid duplicates + // Ensure baseline risks exist first so the AI doesn't recreate them + await ensureBaselineRisks(organizationId); + + // Get existing risks to avoid duplicates (includes baseline) const existingRisks = await getExistingRisks(organizationId); // Extract risks using AI diff --git a/bun.lock b/bun.lock index 3745b4faf..4bcb2159b 100644 --- a/bun.lock +++ b/bun.lock @@ -3,6 +3,9 @@ "workspaces": { "": { "name": "comp", + "dependencies": { + "zod": "^3.25.76", + }, "devDependencies": { "@azure/core-http": "^3.0.5", "@azure/core-rest-pipeline": "^1.21.0", @@ -51,7 +54,6 @@ "turbo": "^2.5.4", "typescript": "^5.8.3", "use-debounce": "^10.0.4", - "zod": "^4.0.14", }, }, "apps/api": { @@ -196,7 +198,7 @@ "use-long-press": "^3.3.0", "xml2js": "^0.6.2", "zaraz-ts": "^1.2.0", - "zod": "^4.0.17", + "zod": "^3.25.76", "zustand": "^5.0.3", }, "devDependencies": { @@ -4943,7 +4945,7 @@ "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="], - "zod": ["zod@4.0.17", "", {}, "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "zod-error": ["zod-error@1.5.0", "", { "dependencies": { "zod": "^3.20.2" } }, "sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ=="], @@ -5021,6 +5023,8 @@ "@commitlint/types/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + "@comp/api/zod": ["zod@4.0.17", "", {}, "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ=="], + "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], "@discordjs/rest/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="], @@ -5029,8 +5033,6 @@ "@discordjs/ws/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@dub/better-auth/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@dub/embed-react/vite": ["vite@5.2.9", "", { "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", "rollup": "^4.13.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], @@ -5081,8 +5083,6 @@ "@jest/transform/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "@mendable/firecrawl-js/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@nangohq/types/type-fest": ["type-fest@4.32.0", "", {}, "sha512-rfgpoi08xagF3JSdtJlCwMq9DGNDE0IMh3Mkpc1wUypg9vPi786AiqeBBKcqvIkq42azsBM85N490fyZjeUftw=="], "@nestjs/cli/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], @@ -5185,17 +5185,15 @@ "@trigger.dev/core/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - "@trigger.dev/core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@trigger.dev/sdk/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], "@trigger.dev/sdk/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@trycompai/db/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + "@trycompai/integrations/zod": ["zod@4.0.17", "", {}, "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ=="], - "@vercel/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -5229,8 +5227,6 @@ "chalk-template/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], - "chromium-bidi/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "cli-highlight/parse5": ["parse5@5.1.1", "", {}, "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="], "cli-highlight/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], @@ -5257,8 +5253,6 @@ "dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], - "dub/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "duplexer2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], "ecdsa-sig-formatter/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -5991,8 +5985,6 @@ "yauzl/buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], - "zod-error/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@angular-devkit/core/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@angular-devkit/schematics/ora/cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], diff --git a/package.json b/package.json index 4ed8f37af..a1c44fc40 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,7 @@ "tsup": "^8.5.0", "turbo": "^2.5.4", "typescript": "^5.8.3", - "use-debounce": "^10.0.4", - "zod": "^4.0.14" + "use-debounce": "^10.0.4" }, "engines": { "node": ">=18" @@ -92,5 +91,8 @@ "packages/tsconfig", "packages/ui", "packages/utils" - ] + ], + "dependencies": { + "zod": "^3.25.76" + } } \ No newline at end of file