diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index a15950130..44a00d0b5 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -29,7 +29,7 @@ jobs: - name: Run Claude Code Review id: claude-review if: ${{ !contains(github.event.pull_request.title, '[WIP]') }} - uses: anthropics/claude-code-action@beta + uses: anthropics/claude-code-action@v1 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} @@ -37,7 +37,7 @@ jobs: # model: "claude-opus-4-20250514" # Direct prompt for automated review (no @claude mention needed) - direct_prompt: | + prompt: | Please review this pull request and provide feedback on: - Code quality and best practices - Potential bugs or issues @@ -46,24 +46,3 @@ jobs: - Test coverage Be constructive and helpful in your feedback. - - # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR - # use_sticky_comment: true - - # Optional: Customize review based on file types - # direct_prompt: | - # Review this PR focusing on: - # - For TypeScript files: Type safety and proper interface usage - # - For API endpoints: Security, input validation, and error handling - # - For React components: Performance, accessibility, and best practices - # - For tests: Coverage, edge cases, and test quality - - # Optional: Different prompts for different authors - # direct_prompt: | - # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && - # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' || - # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} - - # Optional: Add specific tools for running tests or linting - # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)" - diff --git a/CLAUDE.md b/CLAUDE.md index c033f44c9..66c279a73 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,7 +58,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **ORM**: Depends on Kysely, Zod, and various utility libraries - **CLI**: Depends on language package, Commander.js, and Prisma (for migrations) - **Language**: Uses Langium for grammar parsing and AST generation -- **Database Support**: SQLite (better-sqlite3) and PostgreSQL (pg) only +- **Database Support**: SQLite (better-sqlite3), PostgreSQL (pg), and MySQL ### Testing Strategy diff --git a/README.md b/README.md index b30a3f1cf..e1631ee63 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ + + + diff --git a/package.json b/package.json index 8456e81df..a4e71f89d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "zenstack-v3", "displayName": "ZenStack", "description": "ZenStack", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", @@ -46,7 +46,7 @@ "prettier": "^3.5.3", "prisma": "catalog:", "tsdown": "^0.21.8", - "tsx": "^4.20.3", + "tsx": "catalog:", "turbo": "^2.5.4", "typescript": "catalog:", "typescript-eslint": "^8.34.1", @@ -62,8 +62,7 @@ ], "overrides": { "cookie@<0.7.0": ">=0.7.0", - "lodash-es@>=4.0.0 <=4.17.22": ">=4.17.23", - "lodash@>=4.0.0 <=4.17.22": ">=4.17.23", + "lodash@>=4.0.0 <=4.17.23": ">=4.18.0", "@better-auth/core": "1.4.19" } }, diff --git a/packages/auth-adapters/better-auth/package.json b/packages/auth-adapters/better-auth/package.json index a97ef4a75..b227490d7 100644 --- a/packages/auth-adapters/better-auth/package.json +++ b/packages/auth-adapters/better-auth/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/better-auth", "displayName": "ZenStack Better Auth Adapter", "description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/auth-adapters/better-auth/src/schema-generator.ts b/packages/auth-adapters/better-auth/src/schema-generator.ts index dee102dc2..bea59baa8 100644 --- a/packages/auth-adapters/better-auth/src/schema-generator.ts +++ b/packages/auth-adapters/better-auth/src/schema-generator.ts @@ -132,7 +132,7 @@ function addDefaultNow(df: DataField) { } as any; const nowExpr: InvocationExpr = { $type: 'InvocationExpr', - function: { $refText: 'now' }, + function: { $refText: 'now', ref: undefined }, args: [], $container: nowArg, }; @@ -221,6 +221,7 @@ function initializeZmodel(config: AdapterConfig) { $type: 'InvocationExpr', function: { $refText: 'env', + ref: undefined, }, args: [], $container: urlField, @@ -391,6 +392,7 @@ function addOrUpdateModel( $type: 'DataFieldType', reference: { $refText: upperCaseFirst(referencedCustomModelName), + ref: undefined, }, array: (field.type as string).endsWith('[]'), optional: !field.required, @@ -408,6 +410,7 @@ function addOrUpdateModel( $type: 'DataFieldAttribute', decl: { $refText: '@relation', + ref: undefined, }, args: [], $container: relationField, @@ -430,6 +433,7 @@ function addOrUpdateModel( $container: fieldsExpr, target: { $refText: fieldName, + ref: undefined, }, }; fieldsExpr.items.push(fkRefExpr); @@ -452,6 +456,7 @@ function addOrUpdateModel( $container: referencesExpr, target: { $refText: field.references.field, + ref: undefined, }, }; referencesExpr.items.push(pkRefExpr); @@ -465,7 +470,7 @@ function addOrUpdateModel( } as any; const onDeleteValueExpr: ReferenceExpr = { $type: 'ReferenceExpr', - target: { $refText: action }, + target: { $refText: action, ref: undefined }, args: [], $container: onDeleteArg, }; @@ -495,6 +500,7 @@ function addOrUpdateModel( $type: 'DataFieldType', reference: { $refText: relatedModel, + ref: undefined, }, array: true, optional: false, @@ -512,7 +518,7 @@ function addOrUpdateModel( function addModelAttribute(dataModel: DataModel, name: string, args: Omit[] = []) { const attr: DataModelAttribute = { $type: 'DataModelAttribute', - decl: { $refText: name }, + decl: { $refText: name, ref: undefined }, $container: dataModel, args: [], }; @@ -527,7 +533,7 @@ function addModelAttribute(dataModel: DataModel, name: string, args: Omit[] = []) { const attr: DataFieldAttribute = { $type: 'DataFieldAttribute', - decl: { $refText: name }, + decl: { $refText: name, ref: undefined }, $container: dataField, args: [], }; diff --git a/packages/cli/package.json b/packages/cli/package.json index 5ce77f6cb..886ece525 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/cli", "displayName": "ZenStack CLI", "description": "FullStack database toolkit with built-in access control and automatic API generation.", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", @@ -45,6 +45,7 @@ "@zenstackhq/orm": "workspace:*", "@zenstackhq/schema": "workspace:*", "@zenstackhq/sdk": "workspace:*", + "@zenstackhq/plugin-policy": "workspace:*", "@zenstackhq/server": "workspace:*", "chokidar": "^5.0.0", "colors": "1.4.0", @@ -98,4 +99,4 @@ "node": ">=20" }, "funding": "https://github.com/sponsors/zenstackhq" -} +} \ No newline at end of file diff --git a/packages/cli/src/actions/check.ts b/packages/cli/src/actions/check.ts index 61f12c57f..c22e65468 100644 --- a/packages/cli/src/actions/check.ts +++ b/packages/cli/src/actions/check.ts @@ -30,7 +30,7 @@ async function checkPluginResolution(schemaFile: string, model: Model) { const provider = getPluginProvider(plugin); if (!provider.startsWith('@core/')) { const pluginSourcePath = - plugin.$cstNode?.parent?.element.$document?.uri?.fsPath ?? schemaFile; + plugin.$cstNode?.container?.astNode.$document?.uri?.fsPath ?? schemaFile; await loadPluginModule(provider, path.dirname(pluginSourcePath)); } } diff --git a/packages/cli/src/actions/db.ts b/packages/cli/src/actions/db.ts index 592f9888d..1a3325d73 100644 --- a/packages/cli/src/actions/db.ts +++ b/packages/cli/src/actions/db.ts @@ -267,7 +267,7 @@ async function runPull(options: PullOptions) { }); // Add/update models and their fields newModel.declarations - .filter((d) => [DataModel, Enum].includes(d.$type)) + .filter((d) => d.$type === DataModel.$type || d.$type === Enum.$type) .forEach((_declaration) => { const newDataModel = _declaration as DataModel | Enum; const declarations = services.shared.workspace.IndexManager.allElements(newDataModel.$type, docsSet).toArray(); diff --git a/packages/cli/src/actions/generate.ts b/packages/cli/src/actions/generate.ts index e65747405..8291d4d9d 100644 --- a/packages/cli/src/actions/generate.ts +++ b/packages/cli/src/actions/generate.ts @@ -63,10 +63,10 @@ export async function run(options: Options) { ( model.declarations.filter( (v) => - v.$cstNode?.parent?.element.$type === 'Model' && - !!v.$cstNode.parent.element.$document?.uri?.fsPath, + v.$cstNode?.container?.astNode.$type === 'Model' && + !!v.$cstNode.container.astNode.$document?.uri?.fsPath, ) as AbstractDeclaration[] - ).map((v) => v.$cstNode!.parent!.element.$document!.uri!.fsPath), + ).map((v) => v.$cstNode!.container!.astNode.$document!.uri!.fsPath), ); const watchedPaths = getRootModelWatchPaths(model); @@ -189,7 +189,7 @@ async function runPlugins(schemaFile: string, model: Model, outputPath: string, // resolve relative plugin paths against the schema file where the plugin is declared, // not the entry schema file const pluginSourcePath = - plugin.$cstNode?.parent?.element.$document?.uri?.fsPath ?? schemaFile; + plugin.$cstNode?.container?.astNode.$document?.uri?.fsPath ?? schemaFile; cliPlugin = await loadPluginModule(provider, path.dirname(pluginSourcePath)); } diff --git a/packages/cli/src/actions/proxy.ts b/packages/cli/src/actions/proxy.ts index 54c29c0ab..16c7715d5 100644 --- a/packages/cli/src/actions/proxy.ts +++ b/packages/cli/src/actions/proxy.ts @@ -12,6 +12,7 @@ import { MysqlDialect } from '@zenstackhq/orm/dialects/mysql'; import { PostgresDialect } from '@zenstackhq/orm/dialects/postgres'; import { SqliteDialect } from '@zenstackhq/orm/dialects/sqlite'; import type { SchemaDef } from '@zenstackhq/orm/schema'; +import { PolicyPlugin } from '@zenstackhq/plugin-policy'; import { RPCApiHandler } from '@zenstackhq/server/api'; import { ZenStackMiddleware } from '@zenstackhq/server/express'; import type BetterSqlite3 from 'better-sqlite3'; @@ -20,21 +21,72 @@ import cors from 'cors'; import express from 'express'; import { createJiti } from 'jiti'; import type { createPool as MysqlCreatePool } from 'mysql2'; +import { verify } from 'node:crypto'; import path from 'node:path'; import type { Pool as PgPoolType } from 'pg'; import { CliError } from '../cli-error'; import { getVersion } from '../utils/version-utils'; import { getOutputPath, getSchemaFile, loadSchemaDocument } from './action-utils'; +import { z } from 'zod'; type Options = { output?: string; schema?: string; - port?: number; + port: number; logLevel?: string[]; databaseUrl?: string; + studioAuthKey?: string; + signatureToleranceSecs: number; }; +export const ProxyAuthError = { + MISSING_SIGNATURE_HEADER: 'Missing x-zenstack-signature header', + INVALID_TIMESTAMP: 'Request timestamp is expired or invalid', + INVALID_SIGNATURE_FORMAT: 'Invalid x-zenstack-signature format', +} as const; + +type ProxyAuthErrorCode = keyof typeof ProxyAuthError; + +function rejectAuth(res: express.Response, code: ProxyAuthErrorCode) { + return res.status(401).json({ code, message: ProxyAuthError[code] }); +} +/** + * Represents the identity claim embedded in the Authorization header. + * The bearer token is a plain base64-encoded JSON string. + */ +const UserClaimSchema = z.discriminatedUnion('type', [ + z.object({ type: z.literal('superUser') }), + z.object({ type: z.literal('user'), data: z.record(z.string(), z.unknown()) }), +]); + +type UserClaim = z.infer; + +/** + * Accepts a public key in either PEM format or as a raw base64 / base64url DER string + * (without the `-----BEGIN PUBLIC KEY-----` markers) and always returns a PEM string. + */ +function normalizePublicKey(key: string): string { + key = key.trim(); + if (key.startsWith('-----BEGIN PUBLIC KEY-----')) { + return key; + } + // Convert base64url → standard base64, then wrap in PEM markers. + const b64 = key.replace(/-/g, '+').replace(/_/g, '/'); + return `-----BEGIN PUBLIC KEY-----\n${b64}\n-----END PUBLIC KEY-----`; +} + export async function run(options: Options) { + // Resolve public key: CLI arg takes precedence, then ZENSTACK_STUDIO_AUTH_KEY env var. + options = { ...options, studioAuthKey: options.studioAuthKey ?? process.env['ZENSTACK_STUDIO_AUTH_KEY'] }; + if (!options.studioAuthKey) { + console.warn( + colors.yellow( + 'Warning: This proxy has no authentication. Do not expose it to the public network.\n' + + 'To secure it, get an API key from ZenStack Studio and set it via the ZENSTACK_STUDIO_AUTH_KEY environment variable.', + ), + ); + } + const allowedLogLevels = ['error', 'query'] as const; const log = options.logLevel?.filter((level): level is (typeof allowedLogLevels)[number] => allowedLogLevels.includes(level as any), @@ -95,7 +147,7 @@ export async function run(options: Options) { log: log && log.length > 0 ? log : undefined, omit: Object.keys(omit).length > 0 ? omit : undefined, skipValidationForComputedFields: true, - }); + }) as ClientContract; // check whether the database is reachable try { @@ -104,7 +156,13 @@ export async function run(options: Options) { throw new CliError(`Failed to connect to the database: ${err instanceof Error ? err.message : String(err)}`); } - startServer(db, schemaModule.schema, options); + // If a studioAuthKey is provided, create an authDb with the policy plugin + const authDb = db.$use(new PolicyPlugin()) as ClientContract; + if (options.studioAuthKey) { + console.log(colors.gray('Access policy plugin enabled for authorization.')); + } + + startServer(db, schemaModule.schema, options, authDb); } function evaluateUrl(schemaUrl: ConfigExpr) { @@ -198,17 +256,41 @@ async function createDialect(provider: string, databaseUrl: string, outputPath: } } -export function createProxyApp(client: ClientContract, schema: SchemaDef): express.Application { +export function createProxyApp( + client: ClientContract, + schema: SchemaDef, + authDb: ClientContract, + auth?: { + studioAuthKey: string; + /** Seconds within which a signed request is considered valid. Defaults to 60. */ + signatureToleranceSecs: number; + }, +): express.Application { const app = express(); app.use(cors()); - app.use(express.json({ limit: '5mb' })); + app.use( + express.json({ + limit: '5mb', + verify: (req, _res, buf) => { + // Capture the raw body string for use in signature verification. + (req as express.Request & { rawBody?: string }).rawBody = buf.toString('utf8'); + }, + }), + ); app.use(express.urlencoded({ extended: true, limit: '5mb' })); + if (auth?.studioAuthKey) { + // Apply signature-verification middleware to all authenticated endpoints. + const toleranceSecs = auth.signatureToleranceSecs; + const normalizedKey = normalizePublicKey(auth.studioAuthKey); + app.use(['/api/model', '/api/schema'], createSignatureMiddleware(normalizedKey, toleranceSecs)); + } + app.use( '/api/model', ZenStackMiddleware({ apiHandler: new RPCApiHandler({ schema }), - getClient: () => client, + getClient: (req) => resolveClient(client, authDb, req, !!auth?.studioAuthKey), }), ); @@ -219,8 +301,149 @@ export function createProxyApp(client: ClientContract, schema: Schema return app; } -function startServer(client: ClientContract, schema: any, options: Options) { - const app = createProxyApp(client, schema); +/** + * Creates an Express middleware that verifies the ed25519 signature on every request. + * + * The signature header format is: `t=,v1=` + * + * The signed message is constructed as: + * - GET requests: `[]` + * - Other methods: `[]` + * + * `authorizationToken` is the bearer token value from the `Authorization` header (if present). + */ +function createSignatureMiddleware(publicKey: string, toleranceSeconds: number) { + // Throttle invalid-signature warnings to at most once per 60 seconds. + let lastInvalidSigWarnAt = 0; + const WARN_THROTTLE_SECS = 60; + + function warnInvalidSignature() { + const now = Math.floor(Date.now() / 1000); + if (now - lastInvalidSigWarnAt >= WARN_THROTTLE_SECS) { + lastInvalidSigWarnAt = now; + console.warn( + colors.yellow( + 'Warning: Received a request with an invalid signature. ' + + 'Please double-check whether you have the correct public API key configured.', + ), + ); + } + } + + return (req: express.Request, res: express.Response, next: express.NextFunction) => { + const signatureHeader = req.headers['x-zenstack-signature']; + if (!signatureHeader || typeof signatureHeader !== 'string') { + return rejectAuth(res, 'MISSING_SIGNATURE_HEADER'); + } + + const parts = signatureHeader.split(','); + const timestampPart = parts.find((p) => p.startsWith('t=')); + const sigPart = parts.find((p) => p.startsWith('v1=')); + if (!timestampPart || !sigPart) { + return rejectAuth(res, 'INVALID_SIGNATURE_FORMAT'); + } + const timestamp = timestampPart.substring(2); + const sig = sigPart.substring(3); + + // Replay-attack prevention: reject requests whose timestamp deviates + // from server time by more than the configured tolerance. + const requestTime = parseInt(timestamp, 10); + const now = Math.floor(Date.now() / 1000); + if (isNaN(requestTime) || Math.abs(now - requestTime) > toleranceSeconds) { + return rejectAuth(res, 'INVALID_TIMESTAMP'); + } + + // Payload: raw query string for GET/DELETE, raw body for other methods. + let payload: string; + if (req.method === 'GET' || req.method === 'DELETE') { + const qMark = req.originalUrl.indexOf('?'); + payload = qMark >= 0 ? req.originalUrl.substring(qMark + 1) : ''; + } else { + payload = (req as express.Request & { rawBody?: string }).rawBody ?? ''; + } + + // authorizationToken is the bearer token value (if present). + const authHeader = req.headers['authorization']; + const authorizationToken = authHeader && authHeader.startsWith('Bearer ') ? authHeader.substring(7) : undefined; + + const message = authorizationToken ? `${payload}${timestamp}${authorizationToken}` : `${payload}${timestamp}`; + + try { + const isValid = verify(null, Buffer.from(message, 'utf8'), publicKey, Buffer.from(sig, 'base64url')); + if (!isValid) { + warnInvalidSignature(); + return rejectAuth(res, 'INVALID_SIGNATURE_FORMAT'); + } + } catch { + warnInvalidSignature(); + return rejectAuth(res, 'INVALID_SIGNATURE_FORMAT'); + } + + return next(); + }; +} + +/** + * Resolves the appropriate client for a request based on the Authorization header. + * + * - No studioAuthKey configured (authDb is undefined): always return the base client. + * - SuperUser claim: return the base client (full access, no policy enforcement). + * - Regular user claim: return authDb with the user identity set via $setAuth. + * - No / invalid token: return the base client. + */ +function resolveClient( + client: ClientContract, + authDb: ClientContract, + req: express.Request, + isAuthKeyEnabled: boolean, +): ClientContract { + const authHeader = req.headers['authorization']; + + // If AuthKey is not enabled, and Authorization header is not present, then it is the basic access without auth. + if (!isAuthKeyEnabled && !authHeader) { + return client; + } + + if (!authHeader?.startsWith('Bearer ')) { + return authDb; + } + + const token = authHeader.substring(7); + let claim: UserClaim; + try { + claim = UserClaimSchema.parse(JSON.parse(Buffer.from(token, 'base64').toString('utf8'))); + } catch (err) { + console.error( + colors.red(`Failed to parse user claim from token: ${err instanceof Error ? err.message : String(err)}`), + ); + return authDb; + } + + if (claim.type === 'superUser') { + // SuperUser has full access without policy enforcement, so we return the base client directly. + return client; + } else { + return authDb.$setAuth(claim.data) as ClientContract; + } +} + +function startServer( + client: ClientContract, + schema: any, + options: Options, + authDb: ClientContract, +) { + const app = createProxyApp( + client, + schema, + authDb, + options.studioAuthKey + ? { + studioAuthKey: options.studioAuthKey, + signatureToleranceSecs: options.signatureToleranceSecs, + } + : undefined, + ); const server = app.listen(options.port, () => { console.log(`ZenStack proxy server is running on port: ${options.port}`); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index dd5aea7aa..ba458ded5 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -263,6 +263,26 @@ Arguments following -- are passed to the seed script. E.g.: "zen db seed -- --us .addOption(new Option('-o, --output ', 'output directory for `zen generate` command')) .addOption(new Option('-d, --databaseUrl ', 'database connection URL')) .addOption(new Option('-l, --logLevel ', 'Query log levels (e.g., query, error)')) + .addOption( + new Option( + '--studioAuthKey ', + 'Authentication key from ZenStack Studio. When set, the proxy will only accept requests signed by your Studio project.\nCan also be set via the ZENSTACK_STUDIO_AUTH_KEY environment variable. ', + ), + ) + .addOption( + new Option( + '--signatureToleranceSecs ', + 'Maximum age (in seconds) of a signed request before it is rejected as a replay. Defaults to 60.', + ) + .default(60) + .argParser((v) => { + const parsed = parseInt(v, 10); + if (isNaN(parsed) || parsed < 0) { + throw new CliError(`--signatureToleranceSecs must be a positive integer, got: ${v}`); + } + return parsed; + }), + ) .addOption(noVersionCheckOption) .action(proxyAction); diff --git a/packages/cli/src/plugins/typescript.ts b/packages/cli/src/plugins/typescript.ts index 80576ac35..82fb48937 100644 --- a/packages/cli/src/plugins/typescript.ts +++ b/packages/cli/src/plugins/typescript.ts @@ -1,5 +1,5 @@ import type { CliPlugin } from '@zenstackhq/sdk'; -import { TsSchemaGenerator } from '@zenstackhq/sdk'; +import { detectImportFileExtension, TsSchemaGenerator } from '@zenstackhq/sdk'; import fs from 'node:fs'; import path from 'node:path'; @@ -22,9 +22,12 @@ const plugin: CliPlugin = { // liteOnly mode const liteOnly = pluginOptions['liteOnly'] === true; - // add .js extension when importing - const importWithFileExtension = pluginOptions['importWithFileExtension']; - if (importWithFileExtension && typeof importWithFileExtension !== 'string') { + // file extension to append to relative imports; when not explicitly set, + // auto-detect from the project's tsconfig (".js" for node16/nodenext ESM) + let importWithFileExtension = pluginOptions['importWithFileExtension']; + if (importWithFileExtension === undefined) { + importWithFileExtension = detectImportFileExtension(outDir); + } else if (typeof importWithFileExtension !== 'string') { throw new Error('The "importWithFileExtension" option must be a string if specified.'); } diff --git a/packages/cli/src/telemetry.ts b/packages/cli/src/telemetry.ts index a078f62d0..abbff92df 100644 --- a/packages/cli/src/telemetry.ts +++ b/packages/cli/src/telemetry.ts @@ -1,8 +1,7 @@ -import { init, type Mixpanel } from 'mixpanel'; +import { type Mixpanel } from 'mixpanel'; import { randomUUID } from 'node:crypto'; import fs from 'node:fs'; import * as os from 'os'; -import { TELEMETRY_TRACKING_TOKEN } from './constants'; import { isInCi } from './utils/is-ci'; import { isInContainer } from './utils/is-container'; import isDocker from './utils/is-docker'; @@ -44,11 +43,14 @@ export class Telemetry { private readonly isCi = isInCi; constructor() { - if (process.env['DO_NOT_TRACK'] !== '1' && TELEMETRY_TRACKING_TOKEN) { - this.mixpanel = init(TELEMETRY_TRACKING_TOKEN, { - geolocate: true, - }); - } + // if (process.env['DO_NOT_TRACK'] !== '1' && TELEMETRY_TRACKING_TOKEN) { + // this.mixpanel = init(TELEMETRY_TRACKING_TOKEN, { + // geolocate: true, + // }); + // } + + // Telemetry is currently muted + return; } get isTracking() { diff --git a/packages/cli/test/db/pull.test.ts b/packages/cli/test/db/pull.test.ts index 58649e3da..ec34c66c3 100644 --- a/packages/cli/test/db/pull.test.ts +++ b/packages/cli/test/db/pull.test.ts @@ -619,16 +619,18 @@ enum Status { it('should preserve field-level validation attributes after db pull', async () => { const { workDir, schema } = await createProject( `model User { - id Int @id @default(autoincrement()) - email String @unique @email - phone String @phone - name String @length(min: 2, max: 100) - website String? @url - code String? @regex('^[A-Z]+$') - age Int @gt(0) - score Float @gte(0.0) - rating Decimal @lt(10) - rank BigInt @lte(999) + id Int @id @default(autoincrement()) + email String @unique @email + phone String @phone + birthdate String @date + localTime String @time + name String @length(min: 2, max: 100) + website String? @url + code String? @regex('^[A-Z]+$') + age Int @gt(0) + score Float @gte(0.0) + rating Decimal @lt(10) + rank BigInt @lte(999) }`, ); runCli('db push', workDir); @@ -1406,7 +1408,7 @@ describe('DB pull - SQL specific features', () => { return; } - const { workDir, schema } = await createProject( + const { workDir } = await createProject( `model User { id Int @id @default(autoincrement()) email String @unique diff --git a/packages/cli/test/import-extension.test.ts b/packages/cli/test/import-extension.test.ts new file mode 100644 index 000000000..033de742c --- /dev/null +++ b/packages/cli/test/import-extension.test.ts @@ -0,0 +1,134 @@ +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { createProject, runCli } from './utils'; + +const model = ` +model User { + id String @id @default(cuid()) + name String + posts Post[] +} + +model Post { + id String @id @default(cuid()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId String +} +`; + +/** + * Overwrites the project's tsconfig.json with the given compiler options + * (merged over a sensible default that includes the generated files). + */ +function writeTsConfig(workDir: string, compilerOptions: Record) { + fs.writeFileSync( + path.join(workDir, 'tsconfig.json'), + JSON.stringify( + { + compilerOptions: { + target: 'ESNext', + esModuleInterop: true, + skipLibCheck: true, + strict: true, + noEmit: true, + types: ['node'], + ...compilerOptions, + }, + include: ['**/*.ts'], + }, + null, + 4, + ), + ); +} + +function readGenerated(workDir: string, file: string) { + return fs.readFileSync(path.join(workDir, 'zenstack', file), 'utf8'); +} + +function typeCheck(workDir: string) { + // throws (non-zero exit) if type checking fails + execSync('npx tsc --noEmit', { cwd: workDir, stdio: 'pipe' }); +} + +describe('Import file extension generation', () => { + it('omits the extension and compiles under bundler resolution', async () => { + const { workDir } = await createProject(model); + // createTestProject already writes a bundler tsconfig, but set it explicitly here + writeTsConfig(workDir, { module: 'ESNext', moduleResolution: 'Bundler' }); + + runCli('generate', workDir); + + // relative imports between generated files carry no extension + for (const file of ['models.ts', 'input.ts']) { + const content = readGenerated(workDir, file); + expect(content).toContain(`from "./schema"`); + expect(content).not.toContain(`./schema.js`); + } + + // and the project type-checks + expect(() => typeCheck(workDir)).not.toThrow(); + }); + + it('auto-adds ".js" and compiles under nodenext resolution', async () => { + const { workDir } = await createProject(model); + writeTsConfig(workDir, { module: 'NodeNext', moduleResolution: 'NodeNext' }); + + runCli('generate', workDir); + + // node16/nodenext requires explicit extensions; they must be present + for (const file of ['models.ts', 'input.ts']) { + const content = readGenerated(workDir, file); + expect(content).toContain(`from "./schema.js"`); + } + + // the real proof: nodenext would fail to resolve "./schema" without the extension + expect(() => typeCheck(workDir)).not.toThrow(); + }); + + it('fails to compile under nodenext if the extension is suppressed', async () => { + // negative control proving the auto-detected extension is load-bearing + const modelWithoutExtension = ` +plugin typescript { + provider = "@core/typescript" + importWithFileExtension = "" +} + +${model}`; + const { workDir } = await createProject(modelWithoutExtension); + writeTsConfig(workDir, { module: 'NodeNext', moduleResolution: 'NodeNext' }); + + runCli('generate', workDir); + + const content = readGenerated(workDir, 'input.ts'); + expect(content).toContain(`from "./schema"`); + expect(content).not.toContain(`./schema.js`); + + // missing extension is a hard error under nodenext + expect(() => typeCheck(workDir)).toThrow(); + }); + + it('honors an explicit importWithFileExtension over auto-detection', async () => { + const modelWithExtension = ` +plugin typescript { + provider = "@core/typescript" + importWithFileExtension = "js" +} + +${model}`; + const { workDir } = await createProject(modelWithExtension); + // bundler resolution would auto-detect "no extension"; the explicit option wins + writeTsConfig(workDir, { module: 'ESNext', moduleResolution: 'Bundler' }); + + runCli('generate', workDir); + + const content = readGenerated(workDir, 'input.ts'); + expect(content).toContain(`from "./schema.js"`); + + // bundler resolution also accepts the explicit extension + expect(() => typeCheck(workDir)).not.toThrow(); + }); +}); diff --git a/packages/cli/test/proxy.test.ts b/packages/cli/test/proxy.test.ts index 840412f23..e1f958450 100644 --- a/packages/cli/test/proxy.test.ts +++ b/packages/cli/test/proxy.test.ts @@ -1,8 +1,78 @@ +import { PolicyPlugin } from '@zenstackhq/plugin-policy'; import { createTestClient } from '@zenstackhq/testtools'; +import { sign } from 'node:crypto'; import http from 'node:http'; import { afterEach, describe, expect, it } from 'vitest'; import { createProxyApp } from '../src/actions/proxy'; +// ─── Ed25519 key pair for tests ─────────────────────────────────────────────── +const TEST_PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIHIlHXhk+zc9ziuvrYAnZZgGL36H1GXwfsYchM9dM8gR +-----END PRIVATE KEY-----`; + +const TEST_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAFSJV7wjdFuDz2CqYX7hGnITQvcmJYy7OJQq2Cy2Eiqs= +-----END PUBLIC KEY-----`; + +/** Raw base64 DER — the same key without PEM markers. */ +const TEST_PUBLIC_KEY_DER = 'MCowBQYDK2VwAyEAFSJV7wjdFuDz2CqYX7hGnITQvcmJYy7OJQq2Cy2Eiqs='; + +// ─── Helpers ────────────────────────────────────────────────────────────────── + +/** + * Builds the `x-zenstack-signature` header value for a request. + * The payload is: + * - GET / DELETE: the raw query string (URL-encoded, after `?`) + * - other methods: `JSON.stringify(body)` (the raw request body) + */ +function buildSignatureHeader(options: { + privateKey: string; + method: string; + /** Path + optional query string, e.g. `/api/model/user/findMany?q=%7B%7D` */ + pathWithQuery: string; + body?: unknown; + authorizationToken?: string; + /** Override timestamp (unix seconds as string). Defaults to `now`. */ + timestamp?: string; +}): string { + const timestamp = options.timestamp ?? String(Math.floor(Date.now() / 1000)); + + const method = options.method.toUpperCase(); + let payload: string; + if (method === 'GET' || method === 'DELETE') { + const qMark = options.pathWithQuery.indexOf('?'); + payload = qMark >= 0 ? options.pathWithQuery.substring(qMark + 1) : ''; + } else { + payload = options.body != null ? JSON.stringify(options.body) : ''; + } + + const message = options.authorizationToken + ? `${payload}${timestamp}${options.authorizationToken}` + : `${payload}${timestamp}`; + + const sig = sign(null, Buffer.from(message, 'utf8'), options.privateKey).toString('base64url'); + return `t=${timestamp},v1=${sig}`; +} + +/** Encodes a UserClaim as a plain base64 bearer token. */ +function makeUserToken(claim: { type: 'superUser' } | { type: 'user'; data: Record }): string { + return Buffer.from(JSON.stringify(claim)).toString('base64'); +} + +async function createPolicyApp(zmodel: string) { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + return { + client, + app: createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 60, + }), + }; +} + +// ─── Test suite ─────────────────────────────────────────────────────────────── + describe('CLI proxy tests', () => { let server: http.Server | undefined; @@ -35,7 +105,8 @@ describe('CLI proxy tests', () => { `; const client = await createTestClient(zmodel); - const app = createProxyApp(client, client.$schema); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb); const baseUrl = await startAt(app); const r = await fetch(`${baseUrl}/api/schema`); @@ -69,10 +140,12 @@ describe('CLI proxy tests', () => { // create the client with skipValidationForComputedFields. const client = await createTestClient(zmodel, { skipValidationForComputedFields: true, - omit: { User: { postCount: true } }, + // a string schema can't be statically typed, so the omit config is untyped here + omit: { User: { postCount: true } } as any, }); - const app = createProxyApp(client, client.$schema); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb); const baseUrl = await startAt(app); // Create a user via the proxy API. @@ -119,7 +192,8 @@ describe('CLI proxy tests', () => { `; const client = await createTestClient(zmodel); - const app = createProxyApp(client, client.$schema); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb); const baseUrl = await startAt(app); const txRes = await fetch(`${baseUrl}/api/model/$transaction/sequential`, { @@ -167,4 +241,563 @@ describe('CLI proxy tests', () => { const user = await userRes.json(); expect(user.data).toMatchObject({ id: 'u1', email: 'alice@example.com' }); }); + + // ─── AuthN: signature verification ───────────────────────────────────────── + + describe('signature verification (studioAuthKey configured)', async () => { + const zmodel = ` + model User { + id String @id @default(cuid()) + email String @unique + + @@allow('all', true) + } + `; + + it('should reject requests missing the signature header with 401', async () => { + const { app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + const r = await fetch(`${baseUrl}/api/model/user/findMany`); + expect(r.status).toBe(401); + + const schemaR = await fetch(`${baseUrl}/api/schema`); + expect(schemaR.status).toBe(401); + }); + + it('should reject requests with an invalid signature with 401', async () => { + const { app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + const r = await fetch(`${baseUrl}/api/model/user/findMany`, { + headers: { 'x-zenstack-signature': 't=1234567890,v1=invalidsignature' }, + }); + expect(r.status).toBe(401); + }); + + it('should allow GET requests with a valid signature', async () => { + const { app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + const path = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ privateKey: TEST_PRIVATE_KEY, method: 'GET', pathWithQuery: path }); + + const r = await fetch(`${baseUrl}${path}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(200); + const body = await r.json(); + expect(Array.isArray(body.data)).toBe(true); + }); + + it('should allow GET request with query params and a valid signature', async () => { + const { client, app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + // Pre-seed a record directly via client + await client.user.create({ data: { id: 'u1', email: 'alice@example.com' } }); + + const q = encodeURIComponent(JSON.stringify({ where: { id: 'u1' } })); + const pathWithQuery = `/api/model/user/findUnique?q=${q}`; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(200); + const body = await r.json(); + expect(body.data).toMatchObject({ id: 'u1', email: 'alice@example.com' }); + }); + + it('should allow POST (create) requests with a valid signature', async () => { + const { app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + const reqBody = { data: { email: 'bob@example.com' } }; + const pathWithQuery = '/api/model/user/create'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'POST', + pathWithQuery, + body: reqBody, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'x-zenstack-signature': sig }, + body: JSON.stringify(reqBody), + }); + expect(r.status).toBe(201); + const body = await r.json(); + expect(body.data).toMatchObject({ email: 'bob@example.com' }); + }); + + it('should allow PUT (update) requests with a valid signature', async () => { + const { app, client } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + // Seed a record + await client.user.create({ data: { id: 'u1', email: 'old@example.com' } }); + const reqBody = { where: { id: 'u1' }, data: { email: 'new@example.com' } }; + const pathWithQuery = '/api/model/user/update'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'PUT', + pathWithQuery, + body: reqBody, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json', 'x-zenstack-signature': sig }, + body: JSON.stringify(reqBody), + }); + expect(r.status).toBe(200); + const body = await r.json(); + expect(body.data).toMatchObject({ id: 'u1', email: 'new@example.com' }); + }); + + it('should allow signed schema endpoint', async () => { + const { app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + const pathWithQuery = '/api/schema'; + const sig = buildSignatureHeader({ privateKey: TEST_PRIVATE_KEY, method: 'GET', pathWithQuery }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(200); + const body = await r.json(); + expect(body).toHaveProperty('models'); + }); + + it('should not require signatures when studioAuthKey is not configured', async () => { + // No studioAuthKey — backwards-compatible mode + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb); + const baseUrl = await startAt(app); + + // No signature header — should still work + const r = await fetch(`${baseUrl}/api/model/user/findMany`); + expect(r.status).toBe(200); + + // Authorization header is silently ignored + const withAuthHeader = await fetch(`${baseUrl}/api/model/user/findMany`, { + headers: { Authorization: `Bearer ${makeUserToken({ type: 'superUser' })}` }, + }); + expect(withAuthHeader.status).toBe(200); + }); + }); + + // ─── AuthN: public key format ────────────────────────────────────────────── + + describe('public key format', () => { + const zmodel = ` + model User { + id String @id @default(cuid()) + email String @unique + } + `; + + it('should accept a raw base64 DER key (without PEM markers)', async () => { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + // Pass the key as raw base64 DER — no PEM markers + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY_DER, + signatureToleranceSecs: 60, + }); + const baseUrl = await startAt(app); + + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ privateKey: TEST_PRIVATE_KEY, method: 'GET', pathWithQuery }); + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(200); + }); + + it('should accept a key supplied via ZENSTACK_STUDIO_AUTH_KEY env variable', async () => { + const client = await createTestClient(zmodel); + // createProxyApp receives the already-resolved key (as run() would pass it), + // so we simulate env var resolution by passing the PEM directly. + process.env['ZENSTACK_STUDIO_AUTH_KEY'] = TEST_PUBLIC_KEY; + try { + // No studioAuthKey option — would normally fall back to env var via run(); + // here we verify the middleware still works when the resolved key is provided. + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: process.env['ZENSTACK_STUDIO_AUTH_KEY'], + signatureToleranceSecs: 60, + }); + const baseUrl = await startAt(app); + + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ privateKey: TEST_PRIVATE_KEY, method: 'GET', pathWithQuery }); + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(200); + } finally { + delete process.env['ZENSTACK_STUDIO_AUTH_KEY']; + } + }); + }); + + // ─── AuthN: timestamp / replay-attack prevention ─────────────────────────── + + describe('signature timestamp tolerance', () => { + const zmodel = ` + model User { + id String @id @default(cuid()) + email String @unique + } + `; + + it('should reject a request whose timestamp is older than the tolerance window', async () => { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 60, + }); + const baseUrl = await startAt(app); + + // Timestamp 120 seconds ago — exceeds default 60-second tolerance + const expiredTimestamp = String(Math.floor(Date.now() / 1000) - 120); + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + timestamp: expiredTimestamp, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(401); + const body = await r.json(); + expect(body.message).toMatch(/expired/i); + }); + + it('should reject a request whose timestamp is too far in the future', async () => { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 60, + }); + const baseUrl = await startAt(app); + + // Timestamp 120 seconds in the future — exceeds default 60-second tolerance + const futureTimestamp = String(Math.floor(Date.now() / 1000) + 120); + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + timestamp: futureTimestamp, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(401); + const body = await r.json(); + expect(body.message).toMatch(/expired/i); + }); + + it('should accept a request within a custom tolerance window', async () => { + const client = await createTestClient(zmodel); + // Custom tolerance of 300 seconds + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 300, + }); + const baseUrl = await startAt(app); + + // Timestamp 120 seconds ago — within the 300-second custom tolerance + const timestamp = String(Math.floor(Date.now() / 1000) - 120); + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + timestamp, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(200); + }); + + it('should reject a request that falls outside a custom tolerance window', async () => { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + // Very tight tolerance of 5 seconds + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 5, + }); + const baseUrl = await startAt(app); + + // Timestamp 10 seconds ago — exceeds the 5-second custom tolerance + const timestamp = String(Math.floor(Date.now() / 1000) - 10); + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + timestamp, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { 'x-zenstack-signature': sig }, + }); + expect(r.status).toBe(401); + }); + }); + + // ─── AuthN: signed request also carries Authorization header ────────────── + + describe('signature includes Authorization header in the signed message', () => { + const zmodel = ` + model User { + id String @id @default(cuid()) + email String @unique + } + `; + + it('should reject a valid signature if it was produced without the Authorization token', async () => { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 60, + }); + const baseUrl = await startAt(app); + + // Sign without including the auth token + const pathWithQuery = '/api/model/user/findMany'; + const sigWithoutAuth = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + // authorizationToken intentionally omitted + }); + + const authToken = makeUserToken({ type: 'superUser' }); + + // Send with Authorization header but signature that did NOT cover it + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { + 'x-zenstack-signature': sigWithoutAuth, + Authorization: `Bearer ${authToken}`, + }, + }); + expect(r.status).toBe(401); + }); + + it('should accept a request where the signature covers the Authorization token', async () => { + const client = await createTestClient(zmodel); + const authDb = client.$use(new PolicyPlugin()); + const app = createProxyApp(client, client.$schema, authDb, { + studioAuthKey: TEST_PUBLIC_KEY, + signatureToleranceSecs: 60, + }); + const baseUrl = await startAt(app); + + const authToken = makeUserToken({ type: 'superUser' }); + const pathWithQuery = '/api/model/user/findMany'; + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method: 'GET', + pathWithQuery, + authorizationToken: authToken, + }); + + const r = await fetch(`${baseUrl}${pathWithQuery}`, { + headers: { + 'x-zenstack-signature': sig, + Authorization: `Bearer ${authToken}`, + }, + }); + expect(r.status).toBe(200); + }); + }); + + // ─── AuthZ: user impersonation via PolicyPlugin ───────────────────────────── + + describe('authorization with policy plugin', () => { + // Users can only read/write their own record. + const zmodel = ` + model User { + id String @id @default(cuid()) + email String @unique + + @@allow('all', auth() != null && auth().id == id) + } + `; + + async function signedFetch(baseUrl: string, path: string, init: RequestInit = {}): Promise { + const method = (init.method ?? 'GET').toUpperCase(); + const body = init.body ? JSON.parse(init.body as string) : undefined; + const authHeader = (init.headers as Record | undefined)?.['Authorization']; + const authorizationToken = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : undefined; + + const sig = buildSignatureHeader({ + privateKey: TEST_PRIVATE_KEY, + method, + pathWithQuery: path, + body, + authorizationToken, + }); + return fetch(`${baseUrl}${path}`, { + ...init, + headers: { + ...(init.headers as Record), + 'x-zenstack-signature': sig, + }, + }); + } + + it('anonymous request should not bypass access policy', async () => { + const { client, app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + await client.user.create({ data: { id: 'u1', email: 'user1@example.com' } }); + await client.user.create({ data: { id: 'u2', email: 'user2@example.com' } }); + const pathWithQuery = '/api/model/user/findMany'; + const r = await signedFetch(baseUrl, pathWithQuery); + expect(r.status).toBe(200); + const body = await r.json(); + expect(body.data).toHaveLength(0); + }); + + it('superUser can access all records', async () => { + const { client, app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + + // Seed two users directly via base client (bypasses policy) + await client.user.create({ data: { id: 'u1', email: 'user1@example.com' } }); + await client.user.create({ data: { id: 'u2', email: 'user2@example.com' } }); + + const authToken = makeUserToken({ type: 'superUser' }); + const pathWithQuery = '/api/model/user/findMany'; + const r = await signedFetch(baseUrl, pathWithQuery, { + headers: { Authorization: `Bearer ${authToken}` }, + }); + expect(r.status).toBe(200); + const body = await r.json(); + // SuperUser bypasses policy — sees all records + expect(body.data).toHaveLength(2); + }); + + it('regular user can only access their own record', async () => { + const { client, app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + + // Seed two users + await client.user.create({ data: { id: 'u1', email: 'user1@example.com' } }); + await client.user.create({ data: { id: 'u2', email: 'user2@example.com' } }); + + // Authenticated as u1 + const authToken = makeUserToken({ type: 'user', data: { id: 'u1' } }); + const pathWithQuery = '/api/model/user/findMany'; + const r = await signedFetch(baseUrl, pathWithQuery, { + headers: { Authorization: `Bearer ${authToken}` }, + }); + expect(r.status).toBe(200); + const body = await r.json(); + // Policy restricts to own record only + expect(body.data).toHaveLength(1); + expect(body.data[0]).toMatchObject({ id: 'u1' }); + }); + + it("regular user cannot update another user's record", async () => { + const { client, app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + + await client.user.create({ data: { id: 'u1', email: 'user1@example.com' } }); + await client.user.create({ data: { id: 'u2', email: 'user2@example.com' } }); + + // Authenticated as u2 trying to update u1 + const reqBody = { where: { id: 'u1' }, data: { email: 'hacked@example.com' } }; + const authToken = makeUserToken({ type: 'user', data: { id: 'u2' } }); + const pathWithQuery = '/api/model/user/update'; + const r = await signedFetch(baseUrl, pathWithQuery, { + method: 'PUT', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${authToken}` }, + body: JSON.stringify(reqBody), + }); + // Policy should reject this — the policy plugin returns 404 (not found) + // rather than 403 to avoid leaking that the record exists. + expect([403, 404]).toContain(r.status); + }); + + it('superUser can create records on behalf of others', async () => { + const { client: _client, app } = await createPolicyApp(zmodel); + const baseUrl = await startAt(app); + + const reqBody = { data: { id: 'u1', email: 'user1@example.com' } }; + const authToken = makeUserToken({ type: 'superUser' }); + const pathWithQuery = '/api/model/user/create'; + const r = await signedFetch(baseUrl, pathWithQuery, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${authToken}` }, + body: JSON.stringify(reqBody), + }); + expect(r.status).toBe(201); + const body = await r.json(); + expect(body.data).toMatchObject({ id: 'u1', email: 'user1@example.com' }); + }); + + it('sequential transaction respects user-scoped policy', async () => { + const fullZModel = ` + model User { + id String @id @default(cuid()) + email String @unique + posts Post[] + + @@allow('all', auth() != null && auth().id == id) + } + model Post { + id String @id @default(cuid()) + title String + author User? @relation(fields: [authorId], references: [id]) + authorId String? + + @@allow('all', auth() != null && auth().id == authorId) + } + `; + const { client, app } = await createPolicyApp(fullZModel); + const baseUrl = await startAt(app); + + // Seed users + await client.user.create({ data: { id: 'u1', email: 'user1@example.com' } }); + await client.user.create({ data: { id: 'u2', email: 'user2@example.com' } }); + + // Transaction as u1: create own post and read own posts + const txBody = [ + { model: 'Post', op: 'create', args: { data: { id: 'p1', title: 'Post by u1', authorId: 'u1' } } }, + { model: 'Post', op: 'findMany', args: {} }, + ]; + const authToken = makeUserToken({ type: 'user', data: { id: 'u1' } }); + const pathWithQuery = '/api/model/$transaction/sequential'; + const r = await signedFetch(baseUrl, pathWithQuery, { + method: 'POST', + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${authToken}` }, + body: JSON.stringify(txBody), + }); + expect(r.status).toBe(200); + const body = await r.json(); + expect(Array.isArray(body.data)).toBe(true); + // Created post + expect(body.data[0]).toMatchObject({ id: 'p1', title: 'Post by u1' }); + // findMany respects policy — u1 sees only their posts + expect(body.data[1]).toHaveLength(1); + expect(body.data[1][0]).toMatchObject({ id: 'p1' }); + }); + }); }); diff --git a/packages/cli/test/ts-schema-gen.test.ts b/packages/cli/test/ts-schema-gen.test.ts index 29aa7b522..0bf7af48c 100644 --- a/packages/cli/test/ts-schema-gen.test.ts +++ b/packages/cli/test/ts-schema-gen.test.ts @@ -462,9 +462,9 @@ model User { true, ); - expect(schemaLite!.models.User.attributes).toBeUndefined(); - expect(schemaLite!.models.User.fields.id.attributes).toBeUndefined(); - expect(schemaLite!.models.User.fields.email.attributes).toBeUndefined(); + expect(schemaLite!.models['User']!.attributes).toBeUndefined(); + expect(schemaLite!.models['User']!.fields['id']!.attributes).toBeUndefined(); + expect(schemaLite!.models['User']!.fields['email']!.attributes).toBeUndefined(); }); it('supports ignorable fields for @updatedAt', async () => { diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 8ef64682a..e7ce31be8 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "@zenstackhq/typescript-config/base.json", - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "test/**/*.ts"] } diff --git a/packages/clients/client-helpers/package.json b/packages/clients/client-helpers/package.json index 40f5b6fbc..fc4ef18e4 100644 --- a/packages/clients/client-helpers/package.json +++ b/packages/clients/client-helpers/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/client-helpers", "displayName": "ZenStack Client Helpers", "description": "Helpers for implementing clients that consume ZenStack's CRUD service", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/clients/fetch-client/package.json b/packages/clients/fetch-client/package.json index 6836ffe1c..af544473c 100644 --- a/packages/clients/fetch-client/package.json +++ b/packages/clients/fetch-client/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/fetch-client", "displayName": "ZenStack Fetch Client", "description": "Simple fetch-based client for consuming ZenStack's RPC-style CRUD API", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/clients/tanstack-query/package.json b/packages/clients/tanstack-query/package.json index 90d4add21..3124dfe2e 100644 --- a/packages/clients/tanstack-query/package.json +++ b/packages/clients/tanstack-query/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/tanstack-query", "displayName": "ZenStack TanStack Query Integration", "description": "TanStack Query Client for consuming ZenStack v3's CRUD service", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/clients/tanstack-query/src/vue.ts b/packages/clients/tanstack-query/src/vue.ts index 687a43c4d..377da31fb 100644 --- a/packages/clients/tanstack-query/src/vue.ts +++ b/packages/clients/tanstack-query/src/vue.ts @@ -651,8 +651,8 @@ export function useInternalInfiniteQuery { - const reqUrl = makeUrl(endpoint, model, operation, argsValue); + queryFn: ({ pageParam, signal }: any) => { + const reqUrl = makeUrl(endpoint, model, operation, pageParam ?? argsValue); return fetcher(reqUrl, { signal }, fetch); }, initialPageParam: toValue(argsValue) as TPageParam, diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index c570e0e58..8f8af7346 100644 --- a/packages/common-helpers/package.json +++ b/packages/common-helpers/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/common-helpers", "displayName": "ZenStack Common Helpers", "description": "ZenStack Common Helpers", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/config/eslint-config/package.json b/packages/config/eslint-config/package.json index 560889bd7..b060de766 100644 --- a/packages/config/eslint-config/package.json +++ b/packages/config/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/eslint-config", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "private": true, "license": "MIT" diff --git a/packages/config/tsdown-config/package.json b/packages/config/tsdown-config/package.json index 477d67c02..a03f35558 100644 --- a/packages/config/tsdown-config/package.json +++ b/packages/config/tsdown-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/tsdown-config", - "version": "3.7.2", + "version": "3.8.0", "private": true, "type": "module", "license": "MIT", diff --git a/packages/config/typescript-config/package.json b/packages/config/typescript-config/package.json index 3f7337055..4b776e08d 100644 --- a/packages/config/typescript-config/package.json +++ b/packages/config/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/typescript-config", - "version": "3.7.2", + "version": "3.8.0", "private": true, "license": "MIT" } diff --git a/packages/config/vitest-config/package.json b/packages/config/vitest-config/package.json index 058d5ce67..e9a885bff 100644 --- a/packages/config/vitest-config/package.json +++ b/packages/config/vitest-config/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/vitest-config", "type": "module", - "version": "3.7.2", + "version": "3.8.0", "private": true, "license": "MIT", "exports": { diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index f20cfed63..3bd4b5925 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -2,7 +2,7 @@ "name": "create-zenstack", "displayName": "Create ZenStack", "description": "Create a new ZenStack project", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json index 7931002c9..5355b42de 100644 --- a/packages/ide/vscode/package.json +++ b/packages/ide/vscode/package.json @@ -1,7 +1,7 @@ { "name": "zenstack-v3", "publisher": "zenstack", - "version": "3.7.2", + "version": "3.8.0", "displayName": "ZenStack V3 Language Tools", "description": "VSCode extension for ZenStack (v3) ZModel language", "private": true, diff --git a/packages/language/package.json b/packages/language/package.json index 97d5f2805..f0d3667f4 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/language", "displayName": "ZenStack Language Tooling", "description": "ZenStack ZModel language specification", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", @@ -81,6 +81,7 @@ "vscode-languageserver": "^9.0.1" }, "devDependencies": { + "@types/node": "catalog:", "@types/pluralize": "^0.0.33", "@types/tmp": "catalog:", "@zenstackhq/eslint-config": "workspace:*", diff --git a/packages/language/res/stdlib.zmodel b/packages/language/res/stdlib.zmodel index 4561da14a..8f009d8ac 100644 --- a/packages/language/res/stdlib.zmodel +++ b/packages/language/res/stdlib.zmodel @@ -204,6 +204,12 @@ attribute @@@completionHint(_ values: String[]) */ attribute @@@once() +/** + * Indicates that a field-level attribute can only be applied to a single field within a model + * (including fields inherited from base models and mixins). + */ +attribute @@@onceInModel() + /** * Defines a single-field ID on the model. * @@ -218,7 +224,7 @@ attribute @id(map: String?, length: Int?, sort: SortOrder?, clustered: Boolean?) * Defines a default value for a field. * @param value: An expression (e.g. 5, true, now(), auth()). */ -attribute @default(_ value: ContextType, map: String?) @@@prisma +attribute @default(_ value: ContextType, map: String?) @@@prisma @@@once /** * Defines a unique constraint for this field. @@ -546,6 +552,16 @@ attribute @email(_ message: String?) @@@targetField([StringField]) @@@validation */ attribute @datetime(_ message: String?) @@@targetField([StringField]) @@@validation +/** + * Validates a string field value is a valid ISO date. + */ +attribute @date(_ message: String?) @@@targetField([StringField]) @@@validation + +/** + * Validates a string field value is a valid ISO time. + */ +attribute @time(_ precision: Int?, _ message: String?) @@@targetField([StringField]) @@@validation + /** * Validates a string field value is a valid url. */ @@ -621,6 +637,18 @@ function isEmail(field: String): Boolean { function isDateTime(field: String): Boolean { } @@@expressionContext([ValidationRule]) +/** + * Validates a string field value is a valid ISO date. + */ +function isDate(field: String): Boolean { +} @@@expressionContext([ValidationRule]) + +/** + * Validates a string field value is a valid ISO time. + */ +function isTime(field: String, precision: Int?): Boolean { +} @@@expressionContext([ValidationRule]) + /** * Validates a string field value is a valid url. */ diff --git a/packages/language/src/document.ts b/packages/language/src/document.ts index 87fc48564..dc58f93a7 100644 --- a/packages/language/src/document.ts +++ b/packages/language/src/document.ts @@ -251,7 +251,7 @@ function validationAfterImportMerge(model: Model) { export async function formatDocument(content: string) { const services = createZModelServices().ZModelLanguage; const langiumDocuments = services.shared.workspace.LangiumDocuments; - const document = langiumDocuments.createDocument(URI.parse('memory://schema.zmodel'), content); + const document = langiumDocuments.createDocument(URI.parse('memory:///schema.zmodel'), content); const formatter = services.lsp.Formatter as ZModelFormatter; const identifier = { uri: document.uri.toString() }; const options = formatter.getFormatOptions() ?? { diff --git a/packages/language/src/factory/attribute.ts b/packages/language/src/factory/attribute.ts index b59e35ef1..25bd55bc5 100644 --- a/packages/language/src/factory/attribute.ts +++ b/packages/language/src/factory/attribute.ts @@ -18,7 +18,7 @@ export class DataFieldAttributeFactory extends AstFactory { args: AttributeArgFactory[] = []; decl?: Reference; constructor() { - super({ type: DataFieldAttribute, node: { args: [] } }); + super({ type: DataFieldAttribute.$type, node: { args: [] } }); } setDecl(decl: Attribute) { if (!decl) { @@ -50,7 +50,7 @@ export class DataModelAttributeFactory extends AstFactory { args: AttributeArgFactory[] = []; decl?: Reference; constructor() { - super({ type: DataModelAttribute, node: { args: [] } }); + super({ type: DataModelAttribute.$type, node: { args: [] } }); } setDecl(decl: Attribute) { if (!decl) { @@ -83,7 +83,7 @@ export class AttributeArgFactory extends AstFactory { value?: AstFactory; constructor() { - super({ type: AttributeArg }); + super({ type: AttributeArg.$type }); } setName(name: RegularID) { @@ -108,7 +108,7 @@ export class InternalAttributeFactory extends AstFactory { args: AttributeArgFactory[] = []; constructor() { - super({ type: InternalAttribute, node: { args: [] } }); + super({ type: InternalAttribute.$type, node: { args: [] } }); } setDecl(decl: Attribute) { @@ -144,7 +144,7 @@ export class AttributeParamFactory extends AstFactory { constructor() { super({ - type: AttributeParam, + type: AttributeParam.$type, node: { comments: [], attributes: [], @@ -199,7 +199,7 @@ export class AttributeParamTypeFactory extends AstFactory { reference?: Reference; type?: AttributeParamType['type']; constructor() { - super({ type: AttributeParamType }); + super({ type: AttributeParamType.$type }); } setArray(array: boolean) { this.array = array; @@ -244,7 +244,7 @@ export class AttributeFactory extends AstFactory { params: AttributeParamFactory[] = []; constructor() { - super({ type: Attribute, node: { comments: [], attributes: [], params: [] } }); + super({ type: Attribute.$type, node: { comments: [], attributes: [], params: [] } }); } setName(name: string) { diff --git a/packages/language/src/factory/declaration.ts b/packages/language/src/factory/declaration.ts index a6f772a20..3351f9182 100644 --- a/packages/language/src/factory/declaration.ts +++ b/packages/language/src/factory/declaration.ts @@ -69,7 +69,7 @@ export class DataModelFactory extends AstFactory { constructor() { super({ - type: DataModel, + type: DataModel.$type, node: { attributes: [], comments: [], @@ -151,7 +151,7 @@ export class DataFieldFactory extends AstFactory { type?: DataFieldTypeFactory; constructor() { - super({ type: DataField, node: { attributes: [], comments: [] } }); + super({ type: DataField.$type, node: { attributes: [], comments: [] } }); } addAttribute( @@ -204,7 +204,7 @@ export class DataFieldTypeFactory extends AstFactory { unsupported?: UnsupportedFieldTypeFactory; constructor() { - super({ type: DataFieldType }); + super({ type: DataFieldType.$type }); } setArray(array: boolean) { @@ -254,7 +254,7 @@ export class DataFieldTypeFactory extends AstFactory { export class UnsupportedFieldTypeFactory extends AstFactory { value?: AstFactory; constructor() { - super({ type: UnsupportedFieldType }); + super({ type: UnsupportedFieldType.$type }); } setValue(builder: (value: ExpressionBuilder) => AstFactory) { this.value = builder(ExpressionBuilder()); @@ -269,7 +269,7 @@ export class ModelFactory extends AstFactory { declarations: AstFactory[] = []; imports: ModelImportFactory[] = []; constructor() { - super({ type: Model, node: { declarations: [], imports: [] } }); + super({ type: Model.$type, node: { declarations: [], imports: [] } }); } addImport(builder: (b: ModelImportFactory) => ModelImportFactory) { this.imports.push(builder(new ModelImportFactory()).setContainer(this.node)); @@ -291,7 +291,7 @@ export class ModelImportFactory extends AstFactory { path?: string | undefined; constructor() { - super({ type: ModelImport }); + super({ type: ModelImport.$type }); } setPath(path: string) { @@ -310,7 +310,7 @@ export class EnumFactory extends AstFactory { attributes: DataModelAttributeFactory[] = []; constructor() { - super({ type: Enum, node: { comments: [], fields: [], attributes: [] } }); + super({ type: Enum.$type, node: { comments: [], fields: [], attributes: [] } }); } addField(builder: (b: EnumFieldFactory) => EnumFieldFactory) { @@ -344,7 +344,7 @@ export class EnumFieldFactory extends AstFactory { attributes: DataFieldAttributeFactory[] = []; constructor() { - super({ type: EnumField, node: { comments: [], attributes: [] } }); + super({ type: EnumField.$type, node: { comments: [], attributes: [] } }); } setName(name: RegularIDWithTypeNames) { diff --git a/packages/language/src/factory/expression.ts b/packages/language/src/factory/expression.ts index 19fe16af1..fee479b8b 100644 --- a/packages/language/src/factory/expression.ts +++ b/packages/language/src/factory/expression.ts @@ -79,7 +79,7 @@ export class UnaryExprFactory extends AstFactory { operand?: AstFactory; constructor() { - super({ type: UnaryExpr, node: { operator: '!' } }); + super({ type: UnaryExpr.$type, node: { operator: '!' } }); } setOperand(builder: (a: ExpressionBuilder) => AstFactory) { @@ -96,7 +96,7 @@ export class ReferenceExprFactory extends AstFactory { args: ReferenceArgFactory[] = []; constructor() { - super({ type: ReferenceExpr, node: { args: [] } }); + super({ type: ReferenceExpr.$type, node: { args: [] } }); } setTarget(target: ReferenceTarget) { @@ -128,7 +128,7 @@ export class ReferenceArgFactory extends AstFactory { value?: AstFactory; constructor() { - super({ type: ReferenceArg }); + super({ type: ReferenceArg.$type }); } setName(name: string) { @@ -153,7 +153,7 @@ export class MemberAccessExprFactory extends AstFactory { operand?: AstFactory; constructor() { - super({ type: MemberAccessExpr }); + super({ type: MemberAccessExpr.$type }); } setMember(target: Reference) { @@ -177,7 +177,7 @@ export class ObjectExprFactory extends AstFactory { fields: FieldInitializerFactory[] = []; constructor() { - super({ type: ObjectExpr, node: { fields: [] } }); + super({ type: ObjectExpr.$type, node: { fields: [] } }); } addField(builder: (b: FieldInitializerFactory) => FieldInitializerFactory) { @@ -194,7 +194,7 @@ export class FieldInitializerFactory extends AstFactory { value?: AstFactory; constructor() { - super({ type: FieldInitializer }); + super({ type: FieldInitializer.$type }); } setName(name: RegularID) { @@ -219,7 +219,7 @@ export class InvocationExprFactory extends AstFactory { function?: Reference; constructor() { - super({ type: InvocationExpr, node: { args: [] } }); + super({ type: InvocationExpr.$type, node: { args: [] } }); } addArg(builder: (arg: ArgumentFactory) => ArgumentFactory) { @@ -246,7 +246,7 @@ export class ArgumentFactory extends AstFactory { value?: AstFactory; constructor() { - super({ type: Argument }); + super({ type: Argument.$type }); } setValue(builder: (a: ExpressionBuilder) => AstFactory) { @@ -262,7 +262,7 @@ export class ArrayExprFactory extends AstFactory { items: AstFactory[] = []; constructor() { - super({ type: ArrayExpr, node: { items: [] } }); + super({ type: ArrayExpr.$type, node: { items: [] } }); } addItem(builder: (a: ExpressionBuilder) => AstFactory) { @@ -281,7 +281,7 @@ export class BinaryExprFactory extends AstFactory { // TODO: add support for CollectionPredicateBinding constructor() { - super({ type: BinaryExpr }); + super({ type: BinaryExpr.$type }); } setOperator(operator: BinaryExpr['operator']) { diff --git a/packages/language/src/factory/primitives.ts b/packages/language/src/factory/primitives.ts index e97310d54..276ebaf8e 100644 --- a/packages/language/src/factory/primitives.ts +++ b/packages/language/src/factory/primitives.ts @@ -3,13 +3,13 @@ import { BooleanLiteral, NullExpr, NumberLiteral, StringLiteral, ThisExpr } from export class ThisExprFactory extends AstFactory { constructor() { - super({ type: ThisExpr, node: { value: 'this' } }); + super({ type: ThisExpr.$type, node: { value: 'this' } }); } } export class NullExprFactory extends AstFactory { constructor() { - super({ type: NullExpr, node: { value: 'null' } }); + super({ type: NullExpr.$type, node: { value: 'null' } }); } } @@ -17,7 +17,7 @@ export class NumberLiteralFactory extends AstFactory { value?: number | string; constructor() { - super({ type: NumberLiteral }); + super({ type: NumberLiteral.$type }); } setValue(value: number | string) { @@ -33,7 +33,7 @@ export class StringLiteralFactory extends AstFactory { value?: string; constructor() { - super({ type: StringLiteral }); + super({ type: StringLiteral.$type }); } setValue(value: string) { @@ -48,7 +48,7 @@ export class BooleanLiteralFactory extends AstFactory { value?: boolean; constructor() { - super({ type: BooleanLiteral }); + super({ type: BooleanLiteral.$type }); } setValue(value: boolean) { diff --git a/packages/language/src/generated/ast.ts b/packages/language/src/generated/ast.ts index 97431eccf..8609927f4 100644 --- a/packages/language/src/generated/ast.ts +++ b/packages/language/src/generated/ast.ts @@ -1,5 +1,5 @@ /****************************************************************************** - * This file was generated by langium-cli 3.5.0. + * This file was generated by langium-cli 4.2.1. * DO NOT EDIT MANUALLY! ******************************************************************************/ @@ -88,88 +88,12 @@ export type ZModelTokenNames = ZModelTerminalNames | ZModelKeywordNames; export type AbstractDeclaration = Attribute | DataModel | DataSource | Enum | FunctionDecl | GeneratorDecl | Plugin | Procedure | TypeDef; -export const AbstractDeclaration = 'AbstractDeclaration'; +export const AbstractDeclaration = { + $type: 'AbstractDeclaration' +} as const; export function isAbstractDeclaration(item: unknown): item is AbstractDeclaration { - return reflection.isInstance(item, AbstractDeclaration); -} - -export type Boolean = boolean; - -export function isBoolean(item: unknown): item is Boolean { - return typeof item === 'boolean'; -} - -export type BuiltinType = 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'String'; - -export function isBuiltinType(item: unknown): item is BuiltinType { - return item === 'String' || item === 'Boolean' || item === 'Int' || item === 'BigInt' || item === 'Float' || item === 'Decimal' || item === 'DateTime' || item === 'Json' || item === 'Bytes'; -} - -export type ConfigExpr = ConfigArrayExpr | InvocationExpr | LiteralExpr; - -export const ConfigExpr = 'ConfigExpr'; - -export function isConfigExpr(item: unknown): item is ConfigExpr { - return reflection.isInstance(item, ConfigExpr); -} - -export type Expression = ArrayExpr | BinaryExpr | InvocationExpr | LiteralExpr | MemberAccessExpr | NullExpr | ObjectExpr | ReferenceExpr | ThisExpr | UnaryExpr; - -export const Expression = 'Expression'; - -export function isExpression(item: unknown): item is Expression { - return reflection.isInstance(item, Expression); -} - -export type ExpressionType = 'Any' | 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'Null' | 'Object' | 'String' | 'Undefined' | 'Unsupported' | 'Void'; - -export function isExpressionType(item: unknown): item is ExpressionType { - return item === 'String' || item === 'Int' || item === 'Float' || item === 'Boolean' || item === 'BigInt' || item === 'Decimal' || item === 'DateTime' || item === 'Json' || item === 'Bytes' || item === 'Null' || item === 'Object' || item === 'Any' || item === 'Void' || item === 'Undefined' || item === 'Unsupported'; -} - -export type LiteralExpr = BooleanLiteral | NumberLiteral | StringLiteral; - -export const LiteralExpr = 'LiteralExpr'; - -export function isLiteralExpr(item: unknown): item is LiteralExpr { - return reflection.isInstance(item, LiteralExpr); -} - -export type MemberAccessTarget = DataField; - -export const MemberAccessTarget = 'MemberAccessTarget'; - -export function isMemberAccessTarget(item: unknown): item is MemberAccessTarget { - return reflection.isInstance(item, MemberAccessTarget); -} - -export type ReferenceTarget = CollectionPredicateBinding | DataField | EnumField | FunctionParam; - -export const ReferenceTarget = 'ReferenceTarget'; - -export function isReferenceTarget(item: unknown): item is ReferenceTarget { - return reflection.isInstance(item, ReferenceTarget); -} - -export type RegularID = 'abstract' | 'attribute' | 'datasource' | 'enum' | 'import' | 'in' | 'model' | 'plugin' | 'type' | 'view' | string; - -export function isRegularID(item: unknown): item is RegularID { - return item === 'model' || item === 'enum' || item === 'attribute' || item === 'datasource' || item === 'plugin' || item === 'abstract' || item === 'in' || item === 'view' || item === 'import' || item === 'type' || (typeof item === 'string' && (/[_a-zA-Z][\w_]*/.test(item))); -} - -export type RegularIDWithTypeNames = 'Any' | 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'Null' | 'Object' | 'String' | 'Unsupported' | 'Void' | RegularID; - -export function isRegularIDWithTypeNames(item: unknown): item is RegularIDWithTypeNames { - return isRegularID(item) || item === 'String' || item === 'Boolean' || item === 'Int' || item === 'BigInt' || item === 'Float' || item === 'Decimal' || item === 'DateTime' || item === 'Json' || item === 'Bytes' || item === 'Null' || item === 'Object' || item === 'Any' || item === 'Void' || item === 'Unsupported'; -} - -export type TypeDeclaration = DataModel | Enum | TypeDef; - -export const TypeDeclaration = 'TypeDeclaration'; - -export function isTypeDeclaration(item: unknown): item is TypeDeclaration { - return reflection.isInstance(item, TypeDeclaration); + return reflection.isInstance(item, AbstractDeclaration.$type); } export interface Argument extends langium.AstNode { @@ -178,10 +102,13 @@ export interface Argument extends langium.AstNode { value: Expression; } -export const Argument = 'Argument'; +export const Argument = { + $type: 'Argument', + value: 'value' +} as const; export function isArgument(item: unknown): item is Argument { - return reflection.isInstance(item, Argument); + return reflection.isInstance(item, Argument.$type); } export interface ArrayExpr extends langium.AstNode { @@ -190,10 +117,13 @@ export interface ArrayExpr extends langium.AstNode { items: Array; } -export const ArrayExpr = 'ArrayExpr'; +export const ArrayExpr = { + $type: 'ArrayExpr', + items: 'items' +} as const; export function isArrayExpr(item: unknown): item is ArrayExpr { - return reflection.isInstance(item, ArrayExpr); + return reflection.isInstance(item, ArrayExpr.$type); } export interface Attribute extends langium.AstNode { @@ -205,10 +135,16 @@ export interface Attribute extends langium.AstNode { params: Array; } -export const Attribute = 'Attribute'; +export const Attribute = { + $type: 'Attribute', + attributes: 'attributes', + comments: 'comments', + name: 'name', + params: 'params' +} as const; export function isAttribute(item: unknown): item is Attribute { - return reflection.isInstance(item, Attribute); + return reflection.isInstance(item, Attribute.$type); } export interface AttributeArg extends langium.AstNode { @@ -220,10 +156,14 @@ export interface AttributeArg extends langium.AstNode { $resolvedParam?: AttributeParam; } -export const AttributeArg = 'AttributeArg'; +export const AttributeArg = { + $type: 'AttributeArg', + name: 'name', + value: 'value' +} as const; export function isAttributeArg(item: unknown): item is AttributeArg { - return reflection.isInstance(item, AttributeArg); + return reflection.isInstance(item, AttributeArg.$type); } export interface AttributeParam extends langium.AstNode { @@ -236,10 +176,17 @@ export interface AttributeParam extends langium.AstNode { type: AttributeParamType; } -export const AttributeParam = 'AttributeParam'; +export const AttributeParam = { + $type: 'AttributeParam', + attributes: 'attributes', + comments: 'comments', + default: 'default', + name: 'name', + type: 'type' +} as const; export function isAttributeParam(item: unknown): item is AttributeParam { - return reflection.isInstance(item, AttributeParam); + return reflection.isInstance(item, AttributeParam.$type); } export interface AttributeParamType extends langium.AstNode { @@ -251,10 +198,16 @@ export interface AttributeParamType extends langium.AstNode { type?: 'ContextType' | 'FieldReference' | 'TransitiveFieldReference' | ExpressionType; } -export const AttributeParamType = 'AttributeParamType'; +export const AttributeParamType = { + $type: 'AttributeParamType', + array: 'array', + optional: 'optional', + reference: 'reference', + type: 'type' +} as const; export function isAttributeParamType(item: unknown): item is AttributeParamType { - return reflection.isInstance(item, AttributeParamType); + return reflection.isInstance(item, AttributeParamType.$type); } export interface BinaryExpr extends langium.AstNode { @@ -266,10 +219,22 @@ export interface BinaryExpr extends langium.AstNode { right: Expression; } -export const BinaryExpr = 'BinaryExpr'; +export const BinaryExpr = { + $type: 'BinaryExpr', + binding: 'binding', + left: 'left', + operator: 'operator', + right: 'right' +} as const; export function isBinaryExpr(item: unknown): item is BinaryExpr { - return reflection.isInstance(item, BinaryExpr); + return reflection.isInstance(item, BinaryExpr.$type); +} + +export type Boolean = boolean; + +export function isBoolean(item: unknown): item is Boolean { + return typeof item === 'boolean'; } export interface BooleanLiteral extends langium.AstNode { @@ -278,10 +243,19 @@ export interface BooleanLiteral extends langium.AstNode { value: Boolean; } -export const BooleanLiteral = 'BooleanLiteral'; +export const BooleanLiteral = { + $type: 'BooleanLiteral', + value: 'value' +} as const; export function isBooleanLiteral(item: unknown): item is BooleanLiteral { - return reflection.isInstance(item, BooleanLiteral); + return reflection.isInstance(item, BooleanLiteral.$type); +} + +export type BuiltinType = 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'String'; + +export function isBuiltinType(item: unknown): item is BuiltinType { + return item === 'String' || item === 'Boolean' || item === 'Int' || item === 'BigInt' || item === 'Float' || item === 'Decimal' || item === 'DateTime' || item === 'Json' || item === 'Bytes'; } export interface CollectionPredicateBinding extends langium.AstNode { @@ -290,10 +264,13 @@ export interface CollectionPredicateBinding extends langium.AstNode { name: RegularID; } -export const CollectionPredicateBinding = 'CollectionPredicateBinding'; +export const CollectionPredicateBinding = { + $type: 'CollectionPredicateBinding', + name: 'name' +} as const; export function isCollectionPredicateBinding(item: unknown): item is CollectionPredicateBinding { - return reflection.isInstance(item, CollectionPredicateBinding); + return reflection.isInstance(item, CollectionPredicateBinding.$type); } export interface ConfigArrayExpr extends langium.AstNode { @@ -302,10 +279,23 @@ export interface ConfigArrayExpr extends langium.AstNode { items: Array; } -export const ConfigArrayExpr = 'ConfigArrayExpr'; +export const ConfigArrayExpr = { + $type: 'ConfigArrayExpr', + items: 'items' +} as const; export function isConfigArrayExpr(item: unknown): item is ConfigArrayExpr { - return reflection.isInstance(item, ConfigArrayExpr); + return reflection.isInstance(item, ConfigArrayExpr.$type); +} + +export type ConfigExpr = ConfigArrayExpr | InvocationExpr | LiteralExpr; + +export const ConfigExpr = { + $type: 'ConfigExpr' +} as const; + +export function isConfigExpr(item: unknown): item is ConfigExpr { + return reflection.isInstance(item, ConfigExpr.$type); } export interface ConfigField extends langium.AstNode { @@ -315,10 +305,14 @@ export interface ConfigField extends langium.AstNode { value: ConfigExpr; } -export const ConfigField = 'ConfigField'; +export const ConfigField = { + $type: 'ConfigField', + name: 'name', + value: 'value' +} as const; export function isConfigField(item: unknown): item is ConfigField { - return reflection.isInstance(item, ConfigField); + return reflection.isInstance(item, ConfigField.$type); } export interface ConfigInvocationArg extends langium.AstNode { @@ -328,10 +322,14 @@ export interface ConfigInvocationArg extends langium.AstNode { value: LiteralExpr; } -export const ConfigInvocationArg = 'ConfigInvocationArg'; +export const ConfigInvocationArg = { + $type: 'ConfigInvocationArg', + name: 'name', + value: 'value' +} as const; export function isConfigInvocationArg(item: unknown): item is ConfigInvocationArg { - return reflection.isInstance(item, ConfigInvocationArg); + return reflection.isInstance(item, ConfigInvocationArg.$type); } export interface ConfigInvocationExpr extends langium.AstNode { @@ -341,10 +339,14 @@ export interface ConfigInvocationExpr extends langium.AstNode { name: string; } -export const ConfigInvocationExpr = 'ConfigInvocationExpr'; +export const ConfigInvocationExpr = { + $type: 'ConfigInvocationExpr', + args: 'args', + name: 'name' +} as const; export function isConfigInvocationExpr(item: unknown): item is ConfigInvocationExpr { - return reflection.isInstance(item, ConfigInvocationExpr); + return reflection.isInstance(item, ConfigInvocationExpr.$type); } export interface DataField extends langium.AstNode { @@ -356,10 +358,16 @@ export interface DataField extends langium.AstNode { type: DataFieldType; } -export const DataField = 'DataField'; +export const DataField = { + $type: 'DataField', + attributes: 'attributes', + comments: 'comments', + name: 'name', + type: 'type' +} as const; export function isDataField(item: unknown): item is DataField { - return reflection.isInstance(item, DataField); + return reflection.isInstance(item, DataField.$type); } export interface DataFieldAttribute extends langium.AstNode { @@ -369,10 +377,14 @@ export interface DataFieldAttribute extends langium.AstNode { decl: langium.Reference; } -export const DataFieldAttribute = 'DataFieldAttribute'; +export const DataFieldAttribute = { + $type: 'DataFieldAttribute', + args: 'args', + decl: 'decl' +} as const; export function isDataFieldAttribute(item: unknown): item is DataFieldAttribute { - return reflection.isInstance(item, DataFieldAttribute); + return reflection.isInstance(item, DataFieldAttribute.$type); } export interface DataFieldType extends langium.AstNode { @@ -385,10 +397,17 @@ export interface DataFieldType extends langium.AstNode { unsupported?: UnsupportedFieldType; } -export const DataFieldType = 'DataFieldType'; +export const DataFieldType = { + $type: 'DataFieldType', + array: 'array', + optional: 'optional', + reference: 'reference', + type: 'type', + unsupported: 'unsupported' +} as const; export function isDataFieldType(item: unknown): item is DataFieldType { - return reflection.isInstance(item, DataFieldType); + return reflection.isInstance(item, DataFieldType.$type); } export interface DataModel extends langium.AstNode { @@ -405,10 +424,19 @@ export interface DataModel extends langium.AstNode { $allFields?: DataField[]; } -export const DataModel = 'DataModel'; +export const DataModel = { + $type: 'DataModel', + attributes: 'attributes', + baseModel: 'baseModel', + comments: 'comments', + fields: 'fields', + isView: 'isView', + mixins: 'mixins', + name: 'name' +} as const; export function isDataModel(item: unknown): item is DataModel { - return reflection.isInstance(item, DataModel); + return reflection.isInstance(item, DataModel.$type); } export interface DataModelAttribute extends langium.AstNode { @@ -418,10 +446,14 @@ export interface DataModelAttribute extends langium.AstNode { decl: langium.Reference; } -export const DataModelAttribute = 'DataModelAttribute'; +export const DataModelAttribute = { + $type: 'DataModelAttribute', + args: 'args', + decl: 'decl' +} as const; export function isDataModelAttribute(item: unknown): item is DataModelAttribute { - return reflection.isInstance(item, DataModelAttribute); + return reflection.isInstance(item, DataModelAttribute.$type); } export interface DataSource extends langium.AstNode { @@ -431,10 +463,14 @@ export interface DataSource extends langium.AstNode { name: RegularID; } -export const DataSource = 'DataSource'; +export const DataSource = { + $type: 'DataSource', + fields: 'fields', + name: 'name' +} as const; export function isDataSource(item: unknown): item is DataSource { - return reflection.isInstance(item, DataSource); + return reflection.isInstance(item, DataSource.$type); } export interface Enum extends langium.AstNode { @@ -446,10 +482,16 @@ export interface Enum extends langium.AstNode { name: RegularID; } -export const Enum = 'Enum'; +export const Enum = { + $type: 'Enum', + attributes: 'attributes', + comments: 'comments', + fields: 'fields', + name: 'name' +} as const; export function isEnum(item: unknown): item is Enum { - return reflection.isInstance(item, Enum); + return reflection.isInstance(item, Enum.$type); } export interface EnumField extends langium.AstNode { @@ -460,10 +502,31 @@ export interface EnumField extends langium.AstNode { name: RegularIDWithTypeNames; } -export const EnumField = 'EnumField'; +export const EnumField = { + $type: 'EnumField', + attributes: 'attributes', + comments: 'comments', + name: 'name' +} as const; export function isEnumField(item: unknown): item is EnumField { - return reflection.isInstance(item, EnumField); + return reflection.isInstance(item, EnumField.$type); +} + +export type Expression = ArrayExpr | BinaryExpr | InvocationExpr | LiteralExpr | MemberAccessExpr | NullExpr | ObjectExpr | ReferenceExpr | ThisExpr | UnaryExpr; + +export const Expression = { + $type: 'Expression' +} as const; + +export function isExpression(item: unknown): item is Expression { + return reflection.isInstance(item, Expression.$type); +} + +export type ExpressionType = 'Any' | 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'Null' | 'Object' | 'String' | 'Undefined' | 'Unsupported' | 'Void'; + +export function isExpressionType(item: unknown): item is ExpressionType { + return item === 'String' || item === 'Int' || item === 'Float' || item === 'Boolean' || item === 'BigInt' || item === 'Decimal' || item === 'DateTime' || item === 'Json' || item === 'Bytes' || item === 'Null' || item === 'Object' || item === 'Any' || item === 'Void' || item === 'Undefined' || item === 'Unsupported'; } export interface FieldInitializer extends langium.AstNode { @@ -473,10 +536,14 @@ export interface FieldInitializer extends langium.AstNode { value: Expression; } -export const FieldInitializer = 'FieldInitializer'; +export const FieldInitializer = { + $type: 'FieldInitializer', + name: 'name', + value: 'value' +} as const; export function isFieldInitializer(item: unknown): item is FieldInitializer { - return reflection.isInstance(item, FieldInitializer); + return reflection.isInstance(item, FieldInitializer.$type); } export interface FunctionDecl extends langium.AstNode { @@ -489,10 +556,17 @@ export interface FunctionDecl extends langium.AstNode { returnType: FunctionParamType; } -export const FunctionDecl = 'FunctionDecl'; +export const FunctionDecl = { + $type: 'FunctionDecl', + attributes: 'attributes', + expression: 'expression', + name: 'name', + params: 'params', + returnType: 'returnType' +} as const; export function isFunctionDecl(item: unknown): item is FunctionDecl { - return reflection.isInstance(item, FunctionDecl); + return reflection.isInstance(item, FunctionDecl.$type); } export interface FunctionParam extends langium.AstNode { @@ -503,10 +577,15 @@ export interface FunctionParam extends langium.AstNode { type: FunctionParamType; } -export const FunctionParam = 'FunctionParam'; +export const FunctionParam = { + $type: 'FunctionParam', + name: 'name', + optional: 'optional', + type: 'type' +} as const; export function isFunctionParam(item: unknown): item is FunctionParam { - return reflection.isInstance(item, FunctionParam); + return reflection.isInstance(item, FunctionParam.$type); } export interface FunctionParamType extends langium.AstNode { @@ -517,10 +596,15 @@ export interface FunctionParamType extends langium.AstNode { type?: ExpressionType; } -export const FunctionParamType = 'FunctionParamType'; +export const FunctionParamType = { + $type: 'FunctionParamType', + array: 'array', + reference: 'reference', + type: 'type' +} as const; export function isFunctionParamType(item: unknown): item is FunctionParamType { - return reflection.isInstance(item, FunctionParamType); + return reflection.isInstance(item, FunctionParamType.$type); } export interface GeneratorDecl extends langium.AstNode { @@ -530,10 +614,14 @@ export interface GeneratorDecl extends langium.AstNode { name: RegularID; } -export const GeneratorDecl = 'GeneratorDecl'; +export const GeneratorDecl = { + $type: 'GeneratorDecl', + fields: 'fields', + name: 'name' +} as const; export function isGeneratorDecl(item: unknown): item is GeneratorDecl { - return reflection.isInstance(item, GeneratorDecl); + return reflection.isInstance(item, GeneratorDecl.$type); } export interface InternalAttribute extends langium.AstNode { @@ -543,10 +631,14 @@ export interface InternalAttribute extends langium.AstNode { decl: langium.Reference; } -export const InternalAttribute = 'InternalAttribute'; +export const InternalAttribute = { + $type: 'InternalAttribute', + args: 'args', + decl: 'decl' +} as const; export function isInternalAttribute(item: unknown): item is InternalAttribute { - return reflection.isInstance(item, InternalAttribute); + return reflection.isInstance(item, InternalAttribute.$type); } export interface InvocationExpr extends langium.AstNode { @@ -556,10 +648,24 @@ export interface InvocationExpr extends langium.AstNode { function: langium.Reference; } -export const InvocationExpr = 'InvocationExpr'; +export const InvocationExpr = { + $type: 'InvocationExpr', + args: 'args', + function: 'function' +} as const; export function isInvocationExpr(item: unknown): item is InvocationExpr { - return reflection.isInstance(item, InvocationExpr); + return reflection.isInstance(item, InvocationExpr.$type); +} + +export type LiteralExpr = BooleanLiteral | NumberLiteral | StringLiteral; + +export const LiteralExpr = { + $type: 'LiteralExpr' +} as const; + +export function isLiteralExpr(item: unknown): item is LiteralExpr { + return reflection.isInstance(item, LiteralExpr.$type); } export interface MemberAccessExpr extends langium.AstNode { @@ -569,10 +675,24 @@ export interface MemberAccessExpr extends langium.AstNode { operand: Expression; } -export const MemberAccessExpr = 'MemberAccessExpr'; +export const MemberAccessExpr = { + $type: 'MemberAccessExpr', + member: 'member', + operand: 'operand' +} as const; export function isMemberAccessExpr(item: unknown): item is MemberAccessExpr { - return reflection.isInstance(item, MemberAccessExpr); + return reflection.isInstance(item, MemberAccessExpr.$type); +} + +export type MemberAccessTarget = DataField; + +export const MemberAccessTarget = { + $type: 'MemberAccessTarget' +} as const; + +export function isMemberAccessTarget(item: unknown): item is MemberAccessTarget { + return reflection.isInstance(item, MemberAccessTarget.$type); } export interface Model extends langium.AstNode { @@ -581,10 +701,14 @@ export interface Model extends langium.AstNode { imports: Array; } -export const Model = 'Model'; +export const Model = { + $type: 'Model', + declarations: 'declarations', + imports: 'imports' +} as const; export function isModel(item: unknown): item is Model { - return reflection.isInstance(item, Model); + return reflection.isInstance(item, Model.$type); } export interface ModelImport extends langium.AstNode { @@ -593,10 +717,13 @@ export interface ModelImport extends langium.AstNode { path: string; } -export const ModelImport = 'ModelImport'; +export const ModelImport = { + $type: 'ModelImport', + path: 'path' +} as const; export function isModelImport(item: unknown): item is ModelImport { - return reflection.isInstance(item, ModelImport); + return reflection.isInstance(item, ModelImport.$type); } export interface NullExpr extends langium.AstNode { @@ -605,10 +732,13 @@ export interface NullExpr extends langium.AstNode { value: 'null'; } -export const NullExpr = 'NullExpr'; +export const NullExpr = { + $type: 'NullExpr', + value: 'value' +} as const; export function isNullExpr(item: unknown): item is NullExpr { - return reflection.isInstance(item, NullExpr); + return reflection.isInstance(item, NullExpr.$type); } export interface NumberLiteral extends langium.AstNode { @@ -617,10 +747,13 @@ export interface NumberLiteral extends langium.AstNode { value: string; } -export const NumberLiteral = 'NumberLiteral'; +export const NumberLiteral = { + $type: 'NumberLiteral', + value: 'value' +} as const; export function isNumberLiteral(item: unknown): item is NumberLiteral { - return reflection.isInstance(item, NumberLiteral); + return reflection.isInstance(item, NumberLiteral.$type); } export interface ObjectExpr extends langium.AstNode { @@ -629,10 +762,13 @@ export interface ObjectExpr extends langium.AstNode { fields: Array; } -export const ObjectExpr = 'ObjectExpr'; +export const ObjectExpr = { + $type: 'ObjectExpr', + fields: 'fields' +} as const; export function isObjectExpr(item: unknown): item is ObjectExpr { - return reflection.isInstance(item, ObjectExpr); + return reflection.isInstance(item, ObjectExpr.$type); } export interface Plugin extends langium.AstNode { @@ -642,10 +778,14 @@ export interface Plugin extends langium.AstNode { name: RegularID; } -export const Plugin = 'Plugin'; +export const Plugin = { + $type: 'Plugin', + fields: 'fields', + name: 'name' +} as const; export function isPlugin(item: unknown): item is Plugin { - return reflection.isInstance(item, Plugin); + return reflection.isInstance(item, Plugin.$type); } export interface PluginField extends langium.AstNode { @@ -655,10 +795,14 @@ export interface PluginField extends langium.AstNode { value: ArrayExpr | LiteralExpr | ObjectExpr; } -export const PluginField = 'PluginField'; +export const PluginField = { + $type: 'PluginField', + name: 'name', + value: 'value' +} as const; export function isPluginField(item: unknown): item is PluginField { - return reflection.isInstance(item, PluginField); + return reflection.isInstance(item, PluginField.$type); } export interface Procedure extends langium.AstNode { @@ -671,10 +815,17 @@ export interface Procedure extends langium.AstNode { returnType: FunctionParamType; } -export const Procedure = 'Procedure'; +export const Procedure = { + $type: 'Procedure', + attributes: 'attributes', + mutation: 'mutation', + name: 'name', + params: 'params', + returnType: 'returnType' +} as const; export function isProcedure(item: unknown): item is Procedure { - return reflection.isInstance(item, Procedure); + return reflection.isInstance(item, Procedure.$type); } export interface ProcedureParam extends langium.AstNode { @@ -685,10 +836,15 @@ export interface ProcedureParam extends langium.AstNode { type: FunctionParamType; } -export const ProcedureParam = 'ProcedureParam'; +export const ProcedureParam = { + $type: 'ProcedureParam', + name: 'name', + optional: 'optional', + type: 'type' +} as const; export function isProcedureParam(item: unknown): item is ProcedureParam { - return reflection.isInstance(item, ProcedureParam); + return reflection.isInstance(item, ProcedureParam.$type); } export interface ReferenceArg extends langium.AstNode { @@ -698,10 +854,14 @@ export interface ReferenceArg extends langium.AstNode { value: Expression; } -export const ReferenceArg = 'ReferenceArg'; +export const ReferenceArg = { + $type: 'ReferenceArg', + name: 'name', + value: 'value' +} as const; export function isReferenceArg(item: unknown): item is ReferenceArg { - return reflection.isInstance(item, ReferenceArg); + return reflection.isInstance(item, ReferenceArg.$type); } export interface ReferenceExpr extends langium.AstNode { @@ -711,10 +871,36 @@ export interface ReferenceExpr extends langium.AstNode { target: langium.Reference; } -export const ReferenceExpr = 'ReferenceExpr'; +export const ReferenceExpr = { + $type: 'ReferenceExpr', + args: 'args', + target: 'target' +} as const; export function isReferenceExpr(item: unknown): item is ReferenceExpr { - return reflection.isInstance(item, ReferenceExpr); + return reflection.isInstance(item, ReferenceExpr.$type); +} + +export type ReferenceTarget = CollectionPredicateBinding | DataField | EnumField | FunctionParam; + +export const ReferenceTarget = { + $type: 'ReferenceTarget' +} as const; + +export function isReferenceTarget(item: unknown): item is ReferenceTarget { + return reflection.isInstance(item, ReferenceTarget.$type); +} + +export type RegularID = 'abstract' | 'attribute' | 'datasource' | 'enum' | 'import' | 'in' | 'model' | 'plugin' | 'type' | 'view' | string; + +export function isRegularID(item: unknown): item is RegularID { + return item === 'model' || item === 'enum' || item === 'attribute' || item === 'datasource' || item === 'plugin' || item === 'abstract' || item === 'in' || item === 'view' || item === 'import' || item === 'type' || (typeof item === 'string' && (/[_a-zA-Z][\w_]*/.test(item))); +} + +export type RegularIDWithTypeNames = 'Any' | 'BigInt' | 'Boolean' | 'Bytes' | 'DateTime' | 'Decimal' | 'Float' | 'Int' | 'Json' | 'Null' | 'Object' | 'String' | 'Unsupported' | 'Void' | RegularID; + +export function isRegularIDWithTypeNames(item: unknown): item is RegularIDWithTypeNames { + return isRegularID(item) || item === 'String' || item === 'Boolean' || item === 'Int' || item === 'BigInt' || item === 'Float' || item === 'Decimal' || item === 'DateTime' || item === 'Json' || item === 'Bytes' || item === 'Null' || item === 'Object' || item === 'Any' || item === 'Void' || item === 'Unsupported'; } export interface StringLiteral extends langium.AstNode { @@ -723,10 +909,13 @@ export interface StringLiteral extends langium.AstNode { value: string; } -export const StringLiteral = 'StringLiteral'; +export const StringLiteral = { + $type: 'StringLiteral', + value: 'value' +} as const; export function isStringLiteral(item: unknown): item is StringLiteral { - return reflection.isInstance(item, StringLiteral); + return reflection.isInstance(item, StringLiteral.$type); } export interface ThisExpr extends langium.AstNode { @@ -735,10 +924,23 @@ export interface ThisExpr extends langium.AstNode { value: 'this'; } -export const ThisExpr = 'ThisExpr'; +export const ThisExpr = { + $type: 'ThisExpr', + value: 'value' +} as const; export function isThisExpr(item: unknown): item is ThisExpr { - return reflection.isInstance(item, ThisExpr); + return reflection.isInstance(item, ThisExpr.$type); +} + +export type TypeDeclaration = DataModel | Enum | TypeDef; + +export const TypeDeclaration = { + $type: 'TypeDeclaration' +} as const; + +export function isTypeDeclaration(item: unknown): item is TypeDeclaration { + return reflection.isInstance(item, TypeDeclaration.$type); } export interface TypeDef extends langium.AstNode { @@ -751,10 +953,17 @@ export interface TypeDef extends langium.AstNode { name: RegularID; } -export const TypeDef = 'TypeDef'; +export const TypeDef = { + $type: 'TypeDef', + attributes: 'attributes', + comments: 'comments', + fields: 'fields', + mixins: 'mixins', + name: 'name' +} as const; export function isTypeDef(item: unknown): item is TypeDef { - return reflection.isInstance(item, TypeDef); + return reflection.isInstance(item, TypeDef.$type); } export interface UnaryExpr extends langium.AstNode { @@ -764,10 +973,14 @@ export interface UnaryExpr extends langium.AstNode { operator: '!'; } -export const UnaryExpr = 'UnaryExpr'; +export const UnaryExpr = { + $type: 'UnaryExpr', + operand: 'operand', + operator: 'operator' +} as const; export function isUnaryExpr(item: unknown): item is UnaryExpr { - return reflection.isInstance(item, UnaryExpr); + return reflection.isInstance(item, UnaryExpr.$type); } export interface UnsupportedFieldType extends langium.AstNode { @@ -776,10 +989,13 @@ export interface UnsupportedFieldType extends langium.AstNode { value: LiteralExpr; } -export const UnsupportedFieldType = 'UnsupportedFieldType'; +export const UnsupportedFieldType = { + $type: 'UnsupportedFieldType', + value: 'value' +} as const; export function isUnsupportedFieldType(item: unknown): item is UnsupportedFieldType { - return reflection.isInstance(item, UnsupportedFieldType); + return reflection.isInstance(item, UnsupportedFieldType.$type); } export type ZModelAstType = { @@ -838,534 +1054,716 @@ export type ZModelAstType = { } export class ZModelAstReflection extends langium.AbstractAstReflection { - - getAllTypes(): string[] { - return [AbstractDeclaration, Argument, ArrayExpr, Attribute, AttributeArg, AttributeParam, AttributeParamType, BinaryExpr, BooleanLiteral, CollectionPredicateBinding, ConfigArrayExpr, ConfigExpr, ConfigField, ConfigInvocationArg, ConfigInvocationExpr, DataField, DataFieldAttribute, DataFieldType, DataModel, DataModelAttribute, DataSource, Enum, EnumField, Expression, FieldInitializer, FunctionDecl, FunctionParam, FunctionParamType, GeneratorDecl, InternalAttribute, InvocationExpr, LiteralExpr, MemberAccessExpr, MemberAccessTarget, Model, ModelImport, NullExpr, NumberLiteral, ObjectExpr, Plugin, PluginField, Procedure, ProcedureParam, ReferenceArg, ReferenceExpr, ReferenceTarget, StringLiteral, ThisExpr, TypeDeclaration, TypeDef, UnaryExpr, UnsupportedFieldType]; - } - - protected override computeIsSubtype(subtype: string, supertype: string): boolean { - switch (subtype) { - case ArrayExpr: - case BinaryExpr: - case MemberAccessExpr: - case NullExpr: - case ObjectExpr: - case ReferenceExpr: - case ThisExpr: - case UnaryExpr: { - return this.isSubtype(Expression, supertype); - } - case Attribute: - case DataSource: - case FunctionDecl: - case GeneratorDecl: - case Plugin: - case Procedure: { - return this.isSubtype(AbstractDeclaration, supertype); - } - case BooleanLiteral: - case NumberLiteral: - case StringLiteral: { - return this.isSubtype(LiteralExpr, supertype); - } - case CollectionPredicateBinding: - case EnumField: - case FunctionParam: { - return this.isSubtype(ReferenceTarget, supertype); - } - case ConfigArrayExpr: { - return this.isSubtype(ConfigExpr, supertype); - } - case DataField: { - return this.isSubtype(MemberAccessTarget, supertype) || this.isSubtype(ReferenceTarget, supertype); - } - case DataModel: - case Enum: - case TypeDef: { - return this.isSubtype(AbstractDeclaration, supertype) || this.isSubtype(TypeDeclaration, supertype); - } - case InvocationExpr: - case LiteralExpr: { - return this.isSubtype(ConfigExpr, supertype) || this.isSubtype(Expression, supertype); - } - default: { - return false; - } - } - } - - getReferenceType(refInfo: langium.ReferenceInfo): string { - const referenceId = `${refInfo.container.$type}:${refInfo.property}`; - switch (referenceId) { - case 'AttributeParamType:reference': - case 'DataFieldType:reference': - case 'FunctionParamType:reference': { - return TypeDeclaration; - } - case 'DataFieldAttribute:decl': - case 'DataModelAttribute:decl': - case 'InternalAttribute:decl': { - return Attribute; - } - case 'DataModel:baseModel': { - return DataModel; - } - case 'DataModel:mixins': - case 'TypeDef:mixins': { - return TypeDef; - } - case 'InvocationExpr:function': { - return FunctionDecl; - } - case 'MemberAccessExpr:member': { - return MemberAccessTarget; - } - case 'ReferenceExpr:target': { - return ReferenceTarget; - } - default: { - throw new Error(`${referenceId} is not a valid reference id.`); - } - } - } - - getTypeMetaData(type: string): langium.TypeMetaData { - switch (type) { - case Argument: { - return { - name: Argument, - properties: [ - { name: 'value' } - ] - }; - } - case ArrayExpr: { - return { - name: ArrayExpr, - properties: [ - { name: 'items', defaultValue: [] } - ] - }; - } - case Attribute: { - return { - name: Attribute, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'comments', defaultValue: [] }, - { name: 'name' }, - { name: 'params', defaultValue: [] } - ] - }; - } - case AttributeArg: { - return { - name: AttributeArg, - properties: [ - { name: 'name' }, - { name: 'value' } - ] - }; - } - case AttributeParam: { - return { - name: AttributeParam, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'comments', defaultValue: [] }, - { name: 'default', defaultValue: false }, - { name: 'name' }, - { name: 'type' } - ] - }; - } - case AttributeParamType: { - return { - name: AttributeParamType, - properties: [ - { name: 'array', defaultValue: false }, - { name: 'optional', defaultValue: false }, - { name: 'reference' }, - { name: 'type' } - ] - }; - } - case BinaryExpr: { - return { - name: BinaryExpr, - properties: [ - { name: 'binding' }, - { name: 'left' }, - { name: 'operator' }, - { name: 'right' } - ] - }; - } - case BooleanLiteral: { - return { - name: BooleanLiteral, - properties: [ - { name: 'value' } - ] - }; - } - case CollectionPredicateBinding: { - return { - name: CollectionPredicateBinding, - properties: [ - { name: 'name' } - ] - }; - } - case ConfigArrayExpr: { - return { - name: ConfigArrayExpr, - properties: [ - { name: 'items', defaultValue: [] } - ] - }; - } - case ConfigField: { - return { - name: ConfigField, - properties: [ - { name: 'name' }, - { name: 'value' } - ] - }; - } - case ConfigInvocationArg: { - return { - name: ConfigInvocationArg, - properties: [ - { name: 'name' }, - { name: 'value' } - ] - }; - } - case ConfigInvocationExpr: { - return { - name: ConfigInvocationExpr, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case DataField: { - return { - name: DataField, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'comments', defaultValue: [] }, - { name: 'name' }, - { name: 'type' } - ] - }; - } - case DataFieldAttribute: { - return { - name: DataFieldAttribute, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'decl' } - ] - }; - } - case DataFieldType: { - return { - name: DataFieldType, - properties: [ - { name: 'array', defaultValue: false }, - { name: 'optional', defaultValue: false }, - { name: 'reference' }, - { name: 'type' }, - { name: 'unsupported' } - ] - }; - } - case DataModel: { - return { - name: DataModel, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'baseModel' }, - { name: 'comments', defaultValue: [] }, - { name: 'fields', defaultValue: [] }, - { name: 'isView', defaultValue: false }, - { name: 'mixins', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case DataModelAttribute: { - return { - name: DataModelAttribute, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'decl' } - ] - }; - } - case DataSource: { - return { - name: DataSource, - properties: [ - { name: 'fields', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case Enum: { - return { - name: Enum, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'comments', defaultValue: [] }, - { name: 'fields', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case EnumField: { - return { - name: EnumField, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'comments', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case FieldInitializer: { - return { - name: FieldInitializer, - properties: [ - { name: 'name' }, - { name: 'value' } - ] - }; - } - case FunctionDecl: { - return { - name: FunctionDecl, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'expression' }, - { name: 'name' }, - { name: 'params', defaultValue: [] }, - { name: 'returnType' } - ] - }; - } - case FunctionParam: { - return { - name: FunctionParam, - properties: [ - { name: 'name' }, - { name: 'optional', defaultValue: false }, - { name: 'type' } - ] - }; - } - case FunctionParamType: { - return { - name: FunctionParamType, - properties: [ - { name: 'array', defaultValue: false }, - { name: 'reference' }, - { name: 'type' } - ] - }; - } - case GeneratorDecl: { - return { - name: GeneratorDecl, - properties: [ - { name: 'fields', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case InternalAttribute: { - return { - name: InternalAttribute, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'decl' } - ] - }; - } - case InvocationExpr: { - return { - name: InvocationExpr, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'function' } - ] - }; - } - case MemberAccessExpr: { - return { - name: MemberAccessExpr, - properties: [ - { name: 'member' }, - { name: 'operand' } - ] - }; - } - case Model: { - return { - name: Model, - properties: [ - { name: 'declarations', defaultValue: [] }, - { name: 'imports', defaultValue: [] } - ] - }; - } - case ModelImport: { - return { - name: ModelImport, - properties: [ - { name: 'path' } - ] - }; - } - case NullExpr: { - return { - name: NullExpr, - properties: [ - { name: 'value' } - ] - }; - } - case NumberLiteral: { - return { - name: NumberLiteral, - properties: [ - { name: 'value' } - ] - }; - } - case ObjectExpr: { - return { - name: ObjectExpr, - properties: [ - { name: 'fields', defaultValue: [] } - ] - }; - } - case Plugin: { - return { - name: Plugin, - properties: [ - { name: 'fields', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case PluginField: { - return { - name: PluginField, - properties: [ - { name: 'name' }, - { name: 'value' } - ] - }; - } - case Procedure: { - return { - name: Procedure, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'mutation', defaultValue: false }, - { name: 'name' }, - { name: 'params', defaultValue: [] }, - { name: 'returnType' } - ] - }; - } - case ProcedureParam: { - return { - name: ProcedureParam, - properties: [ - { name: 'name' }, - { name: 'optional', defaultValue: false }, - { name: 'type' } - ] - }; - } - case ReferenceArg: { - return { - name: ReferenceArg, - properties: [ - { name: 'name' }, - { name: 'value' } - ] - }; - } - case ReferenceExpr: { - return { - name: ReferenceExpr, - properties: [ - { name: 'args', defaultValue: [] }, - { name: 'target' } - ] - }; - } - case StringLiteral: { - return { - name: StringLiteral, - properties: [ - { name: 'value' } - ] - }; - } - case ThisExpr: { - return { - name: ThisExpr, - properties: [ - { name: 'value' } - ] - }; - } - case TypeDef: { - return { - name: TypeDef, - properties: [ - { name: 'attributes', defaultValue: [] }, - { name: 'comments', defaultValue: [] }, - { name: 'fields', defaultValue: [] }, - { name: 'mixins', defaultValue: [] }, - { name: 'name' } - ] - }; - } - case UnaryExpr: { - return { - name: UnaryExpr, - properties: [ - { name: 'operand' }, - { name: 'operator' } - ] - }; - } - case UnsupportedFieldType: { - return { - name: UnsupportedFieldType, - properties: [ - { name: 'value' } - ] - }; - } - default: { - return { - name: type, - properties: [] - }; - } + override readonly types = { + AbstractDeclaration: { + name: AbstractDeclaration.$type, + properties: { + }, + superTypes: [] + }, + Argument: { + name: Argument.$type, + properties: { + value: { + name: Argument.value + } + }, + superTypes: [] + }, + ArrayExpr: { + name: ArrayExpr.$type, + properties: { + items: { + name: ArrayExpr.items, + defaultValue: [] + } + }, + superTypes: [Expression.$type] + }, + Attribute: { + name: Attribute.$type, + properties: { + attributes: { + name: Attribute.attributes, + defaultValue: [] + }, + comments: { + name: Attribute.comments, + defaultValue: [] + }, + name: { + name: Attribute.name + }, + params: { + name: Attribute.params, + defaultValue: [] + } + }, + superTypes: [AbstractDeclaration.$type] + }, + AttributeArg: { + name: AttributeArg.$type, + properties: { + name: { + name: AttributeArg.name + }, + value: { + name: AttributeArg.value + } + }, + superTypes: [] + }, + AttributeParam: { + name: AttributeParam.$type, + properties: { + attributes: { + name: AttributeParam.attributes, + defaultValue: [] + }, + comments: { + name: AttributeParam.comments, + defaultValue: [] + }, + default: { + name: AttributeParam.default, + defaultValue: false + }, + name: { + name: AttributeParam.name + }, + type: { + name: AttributeParam.type + } + }, + superTypes: [] + }, + AttributeParamType: { + name: AttributeParamType.$type, + properties: { + array: { + name: AttributeParamType.array, + defaultValue: false + }, + optional: { + name: AttributeParamType.optional, + defaultValue: false + }, + reference: { + name: AttributeParamType.reference, + referenceType: TypeDeclaration.$type + }, + type: { + name: AttributeParamType.type + } + }, + superTypes: [] + }, + BinaryExpr: { + name: BinaryExpr.$type, + properties: { + binding: { + name: BinaryExpr.binding + }, + left: { + name: BinaryExpr.left + }, + operator: { + name: BinaryExpr.operator + }, + right: { + name: BinaryExpr.right + } + }, + superTypes: [Expression.$type] + }, + BooleanLiteral: { + name: BooleanLiteral.$type, + properties: { + value: { + name: BooleanLiteral.value + } + }, + superTypes: [LiteralExpr.$type] + }, + CollectionPredicateBinding: { + name: CollectionPredicateBinding.$type, + properties: { + name: { + name: CollectionPredicateBinding.name + } + }, + superTypes: [ReferenceTarget.$type] + }, + ConfigArrayExpr: { + name: ConfigArrayExpr.$type, + properties: { + items: { + name: ConfigArrayExpr.items, + defaultValue: [] + } + }, + superTypes: [ConfigExpr.$type] + }, + ConfigExpr: { + name: ConfigExpr.$type, + properties: { + }, + superTypes: [] + }, + ConfigField: { + name: ConfigField.$type, + properties: { + name: { + name: ConfigField.name + }, + value: { + name: ConfigField.value + } + }, + superTypes: [] + }, + ConfigInvocationArg: { + name: ConfigInvocationArg.$type, + properties: { + name: { + name: ConfigInvocationArg.name + }, + value: { + name: ConfigInvocationArg.value + } + }, + superTypes: [] + }, + ConfigInvocationExpr: { + name: ConfigInvocationExpr.$type, + properties: { + args: { + name: ConfigInvocationExpr.args, + defaultValue: [] + }, + name: { + name: ConfigInvocationExpr.name + } + }, + superTypes: [] + }, + DataField: { + name: DataField.$type, + properties: { + attributes: { + name: DataField.attributes, + defaultValue: [] + }, + comments: { + name: DataField.comments, + defaultValue: [] + }, + name: { + name: DataField.name + }, + type: { + name: DataField.type + } + }, + superTypes: [MemberAccessTarget.$type, ReferenceTarget.$type] + }, + DataFieldAttribute: { + name: DataFieldAttribute.$type, + properties: { + args: { + name: DataFieldAttribute.args, + defaultValue: [] + }, + decl: { + name: DataFieldAttribute.decl, + referenceType: Attribute.$type + } + }, + superTypes: [] + }, + DataFieldType: { + name: DataFieldType.$type, + properties: { + array: { + name: DataFieldType.array, + defaultValue: false + }, + optional: { + name: DataFieldType.optional, + defaultValue: false + }, + reference: { + name: DataFieldType.reference, + referenceType: TypeDeclaration.$type + }, + type: { + name: DataFieldType.type + }, + unsupported: { + name: DataFieldType.unsupported + } + }, + superTypes: [] + }, + DataModel: { + name: DataModel.$type, + properties: { + attributes: { + name: DataModel.attributes, + defaultValue: [] + }, + baseModel: { + name: DataModel.baseModel, + referenceType: DataModel.$type + }, + comments: { + name: DataModel.comments, + defaultValue: [] + }, + fields: { + name: DataModel.fields, + defaultValue: [] + }, + isView: { + name: DataModel.isView, + defaultValue: false + }, + mixins: { + name: DataModel.mixins, + defaultValue: [], + referenceType: TypeDef.$type + }, + name: { + name: DataModel.name + } + }, + superTypes: [AbstractDeclaration.$type, TypeDeclaration.$type] + }, + DataModelAttribute: { + name: DataModelAttribute.$type, + properties: { + args: { + name: DataModelAttribute.args, + defaultValue: [] + }, + decl: { + name: DataModelAttribute.decl, + referenceType: Attribute.$type + } + }, + superTypes: [] + }, + DataSource: { + name: DataSource.$type, + properties: { + fields: { + name: DataSource.fields, + defaultValue: [] + }, + name: { + name: DataSource.name + } + }, + superTypes: [AbstractDeclaration.$type] + }, + Enum: { + name: Enum.$type, + properties: { + attributes: { + name: Enum.attributes, + defaultValue: [] + }, + comments: { + name: Enum.comments, + defaultValue: [] + }, + fields: { + name: Enum.fields, + defaultValue: [] + }, + name: { + name: Enum.name + } + }, + superTypes: [AbstractDeclaration.$type, TypeDeclaration.$type] + }, + EnumField: { + name: EnumField.$type, + properties: { + attributes: { + name: EnumField.attributes, + defaultValue: [] + }, + comments: { + name: EnumField.comments, + defaultValue: [] + }, + name: { + name: EnumField.name + } + }, + superTypes: [ReferenceTarget.$type] + }, + Expression: { + name: Expression.$type, + properties: { + }, + superTypes: [] + }, + FieldInitializer: { + name: FieldInitializer.$type, + properties: { + name: { + name: FieldInitializer.name + }, + value: { + name: FieldInitializer.value + } + }, + superTypes: [] + }, + FunctionDecl: { + name: FunctionDecl.$type, + properties: { + attributes: { + name: FunctionDecl.attributes, + defaultValue: [] + }, + expression: { + name: FunctionDecl.expression + }, + name: { + name: FunctionDecl.name + }, + params: { + name: FunctionDecl.params, + defaultValue: [] + }, + returnType: { + name: FunctionDecl.returnType + } + }, + superTypes: [AbstractDeclaration.$type] + }, + FunctionParam: { + name: FunctionParam.$type, + properties: { + name: { + name: FunctionParam.name + }, + optional: { + name: FunctionParam.optional, + defaultValue: false + }, + type: { + name: FunctionParam.type + } + }, + superTypes: [ReferenceTarget.$type] + }, + FunctionParamType: { + name: FunctionParamType.$type, + properties: { + array: { + name: FunctionParamType.array, + defaultValue: false + }, + reference: { + name: FunctionParamType.reference, + referenceType: TypeDeclaration.$type + }, + type: { + name: FunctionParamType.type + } + }, + superTypes: [] + }, + GeneratorDecl: { + name: GeneratorDecl.$type, + properties: { + fields: { + name: GeneratorDecl.fields, + defaultValue: [] + }, + name: { + name: GeneratorDecl.name + } + }, + superTypes: [AbstractDeclaration.$type] + }, + InternalAttribute: { + name: InternalAttribute.$type, + properties: { + args: { + name: InternalAttribute.args, + defaultValue: [] + }, + decl: { + name: InternalAttribute.decl, + referenceType: Attribute.$type + } + }, + superTypes: [] + }, + InvocationExpr: { + name: InvocationExpr.$type, + properties: { + args: { + name: InvocationExpr.args, + defaultValue: [] + }, + function: { + name: InvocationExpr.function, + referenceType: FunctionDecl.$type + } + }, + superTypes: [ConfigExpr.$type, Expression.$type] + }, + LiteralExpr: { + name: LiteralExpr.$type, + properties: { + }, + superTypes: [ConfigExpr.$type, Expression.$type] + }, + MemberAccessExpr: { + name: MemberAccessExpr.$type, + properties: { + member: { + name: MemberAccessExpr.member, + referenceType: MemberAccessTarget.$type + }, + operand: { + name: MemberAccessExpr.operand + } + }, + superTypes: [Expression.$type] + }, + MemberAccessTarget: { + name: MemberAccessTarget.$type, + properties: { + }, + superTypes: [] + }, + Model: { + name: Model.$type, + properties: { + declarations: { + name: Model.declarations, + defaultValue: [] + }, + imports: { + name: Model.imports, + defaultValue: [] + } + }, + superTypes: [] + }, + ModelImport: { + name: ModelImport.$type, + properties: { + path: { + name: ModelImport.path + } + }, + superTypes: [] + }, + NullExpr: { + name: NullExpr.$type, + properties: { + value: { + name: NullExpr.value + } + }, + superTypes: [Expression.$type] + }, + NumberLiteral: { + name: NumberLiteral.$type, + properties: { + value: { + name: NumberLiteral.value + } + }, + superTypes: [LiteralExpr.$type] + }, + ObjectExpr: { + name: ObjectExpr.$type, + properties: { + fields: { + name: ObjectExpr.fields, + defaultValue: [] + } + }, + superTypes: [Expression.$type] + }, + Plugin: { + name: Plugin.$type, + properties: { + fields: { + name: Plugin.fields, + defaultValue: [] + }, + name: { + name: Plugin.name + } + }, + superTypes: [AbstractDeclaration.$type] + }, + PluginField: { + name: PluginField.$type, + properties: { + name: { + name: PluginField.name + }, + value: { + name: PluginField.value + } + }, + superTypes: [] + }, + Procedure: { + name: Procedure.$type, + properties: { + attributes: { + name: Procedure.attributes, + defaultValue: [] + }, + mutation: { + name: Procedure.mutation, + defaultValue: false + }, + name: { + name: Procedure.name + }, + params: { + name: Procedure.params, + defaultValue: [] + }, + returnType: { + name: Procedure.returnType + } + }, + superTypes: [AbstractDeclaration.$type] + }, + ProcedureParam: { + name: ProcedureParam.$type, + properties: { + name: { + name: ProcedureParam.name + }, + optional: { + name: ProcedureParam.optional, + defaultValue: false + }, + type: { + name: ProcedureParam.type + } + }, + superTypes: [] + }, + ReferenceArg: { + name: ReferenceArg.$type, + properties: { + name: { + name: ReferenceArg.name + }, + value: { + name: ReferenceArg.value + } + }, + superTypes: [] + }, + ReferenceExpr: { + name: ReferenceExpr.$type, + properties: { + args: { + name: ReferenceExpr.args, + defaultValue: [] + }, + target: { + name: ReferenceExpr.target, + referenceType: ReferenceTarget.$type + } + }, + superTypes: [Expression.$type] + }, + ReferenceTarget: { + name: ReferenceTarget.$type, + properties: { + }, + superTypes: [] + }, + StringLiteral: { + name: StringLiteral.$type, + properties: { + value: { + name: StringLiteral.value + } + }, + superTypes: [LiteralExpr.$type] + }, + ThisExpr: { + name: ThisExpr.$type, + properties: { + value: { + name: ThisExpr.value + } + }, + superTypes: [Expression.$type] + }, + TypeDeclaration: { + name: TypeDeclaration.$type, + properties: { + }, + superTypes: [] + }, + TypeDef: { + name: TypeDef.$type, + properties: { + attributes: { + name: TypeDef.attributes, + defaultValue: [] + }, + comments: { + name: TypeDef.comments, + defaultValue: [] + }, + fields: { + name: TypeDef.fields, + defaultValue: [] + }, + mixins: { + name: TypeDef.mixins, + defaultValue: [], + referenceType: TypeDef.$type + }, + name: { + name: TypeDef.name + } + }, + superTypes: [AbstractDeclaration.$type, TypeDeclaration.$type] + }, + UnaryExpr: { + name: UnaryExpr.$type, + properties: { + operand: { + name: UnaryExpr.operand + }, + operator: { + name: UnaryExpr.operator + } + }, + superTypes: [Expression.$type] + }, + UnsupportedFieldType: { + name: UnsupportedFieldType.$type, + properties: { + value: { + name: UnsupportedFieldType.value + } + }, + superTypes: [] } - } + } as const satisfies langium.AstMetaData } export const reflection = new ZModelAstReflection(); diff --git a/packages/language/src/generated/grammar.ts b/packages/language/src/generated/grammar.ts index 925d4dcb6..27385931e 100644 --- a/packages/language/src/generated/grammar.ts +++ b/packages/language/src/generated/grammar.ts @@ -1,5 +1,5 @@ /****************************************************************************** - * This file was generated by langium-cli 3.5.0. + * This file was generated by langium-cli 4.2.1. * DO NOT EDIT MANUALLY! ******************************************************************************/ @@ -47,11 +47,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -82,12 +79,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -160,12 +154,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -220,12 +211,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -280,12 +268,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -331,12 +316,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -391,12 +373,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -461,12 +440,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -478,12 +454,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -500,12 +473,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "arguments": [] } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -522,12 +492,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "arguments": [] } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -544,12 +511,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "arguments": [] } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -580,12 +544,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -643,12 +604,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -692,12 +650,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -742,11 +697,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -784,12 +736,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -871,12 +820,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -907,12 +853,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -926,12 +869,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "value": "this" } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -945,12 +885,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "value": "null" } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -974,7 +911,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } }, { @@ -1000,12 +938,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1050,11 +985,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1092,12 +1024,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1160,12 +1089,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1215,12 +1141,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1237,7 +1160,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "type": { "$ref": "#/rules@47" }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } }, { @@ -1258,12 +1182,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1310,7 +1231,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "type": { "$ref": "#/types@1" }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } } ] @@ -1320,12 +1242,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1356,12 +1275,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1461,12 +1377,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1483,12 +1396,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "arguments": [] } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1545,12 +1455,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1624,12 +1531,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1695,12 +1599,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1766,12 +1667,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1861,12 +1759,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1911,11 +1806,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -1932,12 +1824,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "arguments": [] } }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2108,12 +1997,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2135,7 +2021,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "type": { "$ref": "#/rules@43" }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } }, { @@ -2155,7 +2042,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "type": { "$ref": "#/rules@43" }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } } ], @@ -2163,11 +2051,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2189,16 +2074,14 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "type": { "$ref": "#/rules@38" }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } } ] }, - "definesHiddenTokens": false, "entry": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2258,12 +2141,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2314,7 +2194,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } } ] @@ -2350,12 +2231,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2440,12 +2318,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2479,12 +2354,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2561,12 +2433,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2614,12 +2483,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2751,12 +2617,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2812,12 +2675,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2856,7 +2716,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } } ] @@ -2882,12 +2743,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -2943,12 +2801,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3069,12 +2924,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3132,12 +2984,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3211,12 +3060,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3335,12 +3181,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3414,12 +3257,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3475,7 +3315,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } } ] @@ -3511,12 +3352,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3540,7 +3378,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } }, { @@ -3567,12 +3406,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3604,7 +3440,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } }, { @@ -3631,12 +3468,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3660,7 +3494,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel }, "arguments": [] }, - "deprecatedSyntax": false + "deprecatedSyntax": false, + "isMulti": false } }, { @@ -3687,12 +3522,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3737,11 +3569,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3785,12 +3614,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3861,12 +3687,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3913,12 +3736,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "ParserRule", @@ -3937,12 +3757,9 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } ] }, - "definesHiddenTokens": false, "entry": false, "fragment": false, - "hiddenTokens": [], - "parameters": [], - "wildcard": false + "parameters": [] }, { "$type": "TerminalRule", @@ -3950,7 +3767,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "WS", "definition": { "$type": "RegexToken", - "regex": "/\\\\s+/" + "regex": "/\\\\s+/", + "parenthesized": false }, "fragment": false }, @@ -3959,7 +3777,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "INTERNAL_ATTRIBUTE_NAME", "definition": { "$type": "RegexToken", - "regex": "/@@@([_a-zA-Z][\\\\w_]*\\\\.)*[_a-zA-Z][\\\\w_]*/" + "regex": "/@@@([_a-zA-Z][\\\\w_]*\\\\.)*[_a-zA-Z][\\\\w_]*/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -3969,7 +3788,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "MODEL_ATTRIBUTE_NAME", "definition": { "$type": "RegexToken", - "regex": "/@@([_a-zA-Z][\\\\w_]*\\\\.)*[_a-zA-Z][\\\\w_]*/" + "regex": "/@@([_a-zA-Z][\\\\w_]*\\\\.)*[_a-zA-Z][\\\\w_]*/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -3979,7 +3799,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "FIELD_ATTRIBUTE_NAME", "definition": { "$type": "RegexToken", - "regex": "/@([_a-zA-Z][\\\\w_]*\\\\.)*[_a-zA-Z][\\\\w_]*/" + "regex": "/@([_a-zA-Z][\\\\w_]*\\\\.)*[_a-zA-Z][\\\\w_]*/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -3989,7 +3810,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "ID", "definition": { "$type": "RegexToken", - "regex": "/[_a-zA-Z][\\\\w_]*/" + "regex": "/[_a-zA-Z][\\\\w_]*/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -3999,7 +3821,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "STRING", "definition": { "$type": "RegexToken", - "regex": "/\\"(\\\\\\\\.|[^\\"\\\\\\\\])*\\"|'(\\\\\\\\.|[^'\\\\\\\\])*'/" + "regex": "/\\"(\\\\\\\\.|[^\\"\\\\\\\\])*\\"|'(\\\\\\\\.|[^'\\\\\\\\])*'/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -4009,7 +3832,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "NUMBER", "definition": { "$type": "RegexToken", - "regex": "/[+-]?[0-9]+(\\\\.[0-9]+)?/" + "regex": "/[+-]?[0-9]+(\\\\.[0-9]+)?/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -4019,7 +3843,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "TRIPLE_SLASH_COMMENT", "definition": { "$type": "RegexToken", - "regex": "/\\\\/\\\\/\\\\/[^\\\\n\\\\r]*/" + "regex": "/\\\\/\\\\/\\\\/[^\\\\n\\\\r]*/", + "parenthesized": false }, "fragment": false, "hidden": false @@ -4030,7 +3855,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "ML_COMMENT", "definition": { "$type": "RegexToken", - "regex": "/\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\//" + "regex": "/\\\\/\\\\*[\\\\s\\\\S]*?\\\\*\\\\//", + "parenthesized": false }, "fragment": false }, @@ -4040,7 +3866,8 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel "name": "SL_COMMENT", "definition": { "$type": "RegexToken", - "regex": "/\\\\/\\\\/[^\\\\n\\\\r]*/" + "regex": "/\\\\/\\\\/[^\\\\n\\\\r]*/", + "parenthesized": false }, "fragment": false } @@ -4117,9 +3944,6 @@ export const ZModelGrammar = (): Grammar => loadedZModelGrammar ?? (loadedZModel } } ], - "definesHiddenTokens": false, - "hiddenTokens": [], "imports": [], - "interfaces": [], - "usedGrammars": [] + "interfaces": [] }`)); diff --git a/packages/language/src/generated/module.ts b/packages/language/src/generated/module.ts index 0c621d00c..bc468edcf 100644 --- a/packages/language/src/generated/module.ts +++ b/packages/language/src/generated/module.ts @@ -1,5 +1,5 @@ /****************************************************************************** - * This file was generated by langium-cli 3.5.0. + * This file was generated by langium-cli 4.2.1. * DO NOT EDIT MANUALLY! ******************************************************************************/ diff --git a/packages/language/src/validators/datamodel-validator.ts b/packages/language/src/validators/datamodel-validator.ts index e8ddbe89c..5fc093da1 100644 --- a/packages/language/src/validators/datamodel-validator.ts +++ b/packages/language/src/validators/datamodel-validator.ts @@ -3,7 +3,9 @@ import { AstUtils, type AstNode, type DiagnosticInfo, type ValidationAcceptor } import { IssueCodes, SCALAR_TYPES } from '../constants'; import { ArrayExpr, + Attribute, DataField, + DataFieldAttribute, DataModel, DataModelAttribute, ReferenceExpr, @@ -38,6 +40,7 @@ export default class DataModelValidator implements AstValidator { validateDuplicatedDeclarations(dm, getAllFields(dm), accept); this.validateAttributes(dm, accept); this.validateFields(dm, accept); + this.validateOnceInModelAttributes(dm, accept); if (dm.mixins.length > 0) { this.validateMixins(dm, accept); } @@ -143,6 +146,40 @@ export default class DataModelValidator implements AstValidator { getAllAttributes(dm).forEach((attr) => validateAttributeApplication(attr, accept, dm)); } + // Validates field-level attributes marked with `@@@onceInModel`, which may be applied to at + // most one field per model (including fields inherited from base models and mixins). This must + // run at the model level so that duplicates which only co-occur through inheritance are detected + // — per-field validation only sees the model that physically declares each field. + private validateOnceInModelAttributes(dm: DataModel, accept: ValidationAcceptor) { + // group field attributes carrying `@@@onceInModel` by their attribute declaration + const occurrences = new Map(); + for (const field of getAllFields(dm)) { + for (const attr of field.attributes) { + const decl = attr.decl.ref; + if (decl && hasAttribute(decl, '@@@onceInModel')) { + const list = occurrences.get(decl) ?? []; + list.push(attr); + occurrences.set(decl, list); + } + } + } + + for (const [decl, attrs] of occurrences) { + if (attrs.length <= 1) { + continue; + } + const message = `Attribute "${decl.name}" can only be applied to one field per model`; + // prefer reporting on offending attributes declared on this model's own fields; if all + // offending fields are inherited, report on the model declaration itself + const local = attrs.filter((attr) => dm.fields.includes(attr.$container as DataField)); + if (local.length > 0) { + local.forEach((attr) => accept('error', message, { node: attr })); + } else { + accept('error', message, { node: dm }); + } + } + } + private parseRelation(field: DataField, accept?: ValidationAcceptor) { const relAttr = field.attributes.find((attr) => attr.decl.ref?.name === '@relation'); diff --git a/packages/language/src/zmodel-code-generator.ts b/packages/language/src/zmodel-code-generator.ts index e68ba7735..b4f61e9f4 100644 --- a/packages/language/src/zmodel-code-generator.ts +++ b/packages/language/src/zmodel-code-generator.ts @@ -57,10 +57,10 @@ export interface ZModelCodeOptions { const generationHandlers = new Map(); // generation handler decorator -function gen(name: string) { +function gen(type: { $type: string }) { return function (_target: unknown, _propertyKey: string, descriptor: PropertyDescriptor) { - if (!generationHandlers.get(name)) { - generationHandlers.set(name, descriptor); + if (!generationHandlers.get(type.$type)) { + generationHandlers.set(type.$type, descriptor); } return descriptor; }; @@ -407,7 +407,7 @@ ${ast.fields.map((x) => this.indent + this.generate(x)).join('\n')}${ const currentPriority = BinaryExprOperatorPriority[operator]; if ( - ast.left.$type === BinaryExpr && + ast.left.$type === BinaryExpr.$type && BinaryExprOperatorPriority[(ast.left as BinaryExpr)['operator']] < currentPriority ) { result.left = true; @@ -418,7 +418,7 @@ ${ast.fields.map((x) => this.indent + this.generate(x)).join('\n')}${ **/ if ( !isCollectionPredicate && - ast.right.$type === BinaryExpr && + ast.right.$type === BinaryExpr.$type && BinaryExprOperatorPriority[(ast.right as BinaryExpr)['operator']] <= currentPriority ) { result.right = true; diff --git a/packages/language/src/zmodel-linker.ts b/packages/language/src/zmodel-linker.ts index fc3a7f0dd..6766a6afc 100644 --- a/packages/language/src/zmodel-linker.ts +++ b/packages/language/src/zmodel-linker.ts @@ -101,7 +101,7 @@ export class ZModelLinker extends DefaultLinker { // already linked return; } - if (this.resolveFromScopeProviders(refInfo.reference, document, extraScopes)) { + if (this.resolveFromScopeProviders(defaultRef, document, extraScopes)) { // resolved from additional scope provider return; } @@ -135,57 +135,57 @@ export class ZModelLinker extends DefaultLinker { private resolve(node: AstNode, document: LangiumDocument, extraScopes: ScopeProvider[] = []) { switch (node.$type) { - case StringLiteral: - case NumberLiteral: - case BooleanLiteral: + case StringLiteral.$type: + case NumberLiteral.$type: + case BooleanLiteral.$type: this.resolveLiteral(node as LiteralExpr); break; - case InvocationExpr: + case InvocationExpr.$type: this.resolveInvocation(node as InvocationExpr, document, extraScopes); break; - case ArrayExpr: + case ArrayExpr.$type: this.resolveArray(node as ArrayExpr, document, extraScopes); break; - case ReferenceExpr: + case ReferenceExpr.$type: this.resolveReference(node as ReferenceExpr, document, extraScopes); break; - case MemberAccessExpr: + case MemberAccessExpr.$type: this.resolveMemberAccess(node as MemberAccessExpr, document, extraScopes); break; - case UnaryExpr: + case UnaryExpr.$type: this.resolveUnary(node as UnaryExpr, document, extraScopes); break; - case BinaryExpr: + case BinaryExpr.$type: this.resolveBinary(node as BinaryExpr, document, extraScopes); break; - case ObjectExpr: + case ObjectExpr.$type: this.resolveObject(node as ObjectExpr, document, extraScopes); break; - case ThisExpr: + case ThisExpr.$type: this.resolveThis(node as ThisExpr, document, extraScopes); break; - case NullExpr: + case NullExpr.$type: this.resolveNull(node as NullExpr, document, extraScopes); break; - case AttributeArg: + case AttributeArg.$type: this.resolveAttributeArg(node as AttributeArg, document, extraScopes); break; - case DataModel: + case DataModel.$type: this.resolveDataModel(node as DataModel, document, extraScopes); break; - case DataField: + case DataField.$type: this.resolveDataField(node as DataField, document, extraScopes); break; diff --git a/packages/language/src/zmodel-scope.ts b/packages/language/src/zmodel-scope.ts index d05a30cfb..8dd3c4dab 100644 --- a/packages/language/src/zmodel-scope.ts +++ b/packages/language/src/zmodel-scope.ts @@ -4,6 +4,7 @@ import { DefaultScopeComputation, DefaultScopeProvider, EMPTY_SCOPE, + MultiMap, StreamScope, UriUtils, interruptAndCheck, @@ -11,7 +12,6 @@ import { type AstNodeDescription, type LangiumCoreServices, type LangiumDocument, - type PrecomputedScopes, type ReferenceInfo, type Scope, } from 'langium'; @@ -52,11 +52,11 @@ export class ZModelScopeComputation extends DefaultScopeComputation { super(services); } - override async computeExports( + override async collectExportedSymbols( document: LangiumDocument, cancelToken?: Cancellation.CancellationToken | undefined, ): Promise { - const result = await super.computeExports(document, cancelToken); + const result = await super.collectExportedSymbols(document, cancelToken); // add enum fields so they can be globally resolved across modules for (const node of AstUtils.streamAllContents(document.parseResult.value)) { @@ -76,14 +76,18 @@ export class ZModelScopeComputation extends DefaultScopeComputation { return result; } - override processNode(node: AstNode, document: LangiumDocument, scopes: PrecomputedScopes) { - super.processNode(node, document, scopes); + protected override addLocalSymbol( + node: AstNode, + document: LangiumDocument, + symbols: MultiMap, + ) { + super.addLocalSymbol(node, document, symbols); if (isDataModel(node) || isTypeDef(node)) { // add base fields to the scope recursively const bases = getRecursiveBases(node, true, this.services.shared.workspace.LangiumDocuments); for (const base of bases) { for (const field of base.fields) { - scopes.add(node, this.descriptions.createDescription(field, this.nameProvider.getName(field))); + symbols.add(node, this.descriptions.createDescription(field, this.nameProvider.getName(field))); } } } diff --git a/packages/language/test/attribute-application.test.ts b/packages/language/test/attribute-application.test.ts index f412d7bec..973c11479 100644 --- a/packages/language/test/attribute-application.test.ts +++ b/packages/language/test/attribute-application.test.ts @@ -499,6 +499,95 @@ describe('Attribute application validation tests', () => { }); }); + describe('Field-level @@@onceInModel attribute', () => { + it('accepts a single field carrying the attribute', async () => { + await loadSchema(` + datasource db { + provider = 'sqlite' + url = 'file:./dev.db' + } + + attribute @softDelete() @@@targetField([DateTimeField]) @@@onceInModel + + model Foo { + id Int @id @default(autoincrement()) + deletedAt DateTime? @softDelete + } + `); + }); + + it('rejects two fields in the same model carrying the attribute', async () => { + await loadSchemaWithError( + ` + datasource db { + provider = 'sqlite' + url = 'file:./dev.db' + } + + attribute @softDelete() @@@targetField([DateTimeField]) @@@onceInModel + + model Foo { + id Int @id @default(autoincrement()) + deletedAt DateTime? @softDelete + removedAt DateTime? @softDelete + } + `, + /Attribute "@softDelete" can only be applied to one field per model/, + ); + }); + + it('rejects when the attribute is inherited from a mixin and also declared locally', async () => { + await loadSchemaWithError( + ` + datasource db { + provider = 'sqlite' + url = 'file:./dev.db' + } + + attribute @softDelete() @@@targetField([DateTimeField]) @@@onceInModel + + type Base { + deletedAt DateTime? @softDelete + } + + model Foo with Base { + id Int @id @default(autoincrement()) + removedAt DateTime? @softDelete + } + `, + /Attribute "@softDelete" can only be applied to one field per model/, + ); + }); + + // Duplicates that only co-occur through inheritance (none declared on the leaf model itself) + // are detected at the model level via `getAllFields`. + it('rejects when the attribute arrives only through inheritance (two mixins)', async () => { + await loadSchemaWithError( + ` + datasource db { + provider = 'sqlite' + url = 'file:./dev.db' + } + + attribute @softDelete() @@@targetField([DateTimeField]) @@@onceInModel + + type Base1 { + deletedAt DateTime? @softDelete + } + + type Base2 { + removedAt DateTime? @softDelete + } + + model Foo with Base1 Base2 { + id Int @id @default(autoincrement()) + } + `, + /Attribute "@softDelete" can only be applied to one field per model/, + ); + }); + }); + it('requires relation and fk to have consistent optionality', async () => { await loadSchemaWithError( ` diff --git a/packages/language/tsconfig.json b/packages/language/tsconfig.json index accdce5ed..3948382f8 100644 --- a/packages/language/tsconfig.json +++ b/packages/language/tsconfig.json @@ -2,6 +2,7 @@ "extends": "@zenstackhq/typescript-config/base.json", "include": ["src/**/*.ts"], "compilerOptions": { - "noUnusedLocals": false + "noUnusedLocals": false, + "types": ["node"] } } diff --git a/packages/orm/package.json b/packages/orm/package.json index ce62edc31..5b7bf333f 100644 --- a/packages/orm/package.json +++ b/packages/orm/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/orm", "displayName": "ZenStack ORM", "description": "ZenStack ORM", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", @@ -158,7 +158,7 @@ "@zenstackhq/tsdown-config": "workspace:*", "@zenstackhq/typescript-config": "workspace:*", "@zenstackhq/vitest-config": "workspace:*", - "tsx": "^4.19.2", + "tsx": "catalog:", "zod": "^4.1.0" }, "funding": "https://github.com/sponsors/zenstackhq" diff --git a/packages/orm/src/client/contract.ts b/packages/orm/src/client/contract.ts index f9a8aeae4..7b5541a54 100644 --- a/packages/orm/src/client/contract.ts +++ b/packages/orm/src/client/contract.ts @@ -20,7 +20,7 @@ import type { TypeDefResult, } from './crud-types'; import type { Diagnostics } from './diagnostics'; -import type { ClientOptions, QueryOptions } from './options'; +import type { ClientOptions, QueryOptions, QueryRelevantOptions } from './options'; import type { ExtClientMembersBase, ExtQueryArgsBase, @@ -63,6 +63,10 @@ export const ExtResultMarker: unique symbol = Symbol('zenstack.client.extResult' /** * ZenStack client interface. + * + * Note: this alias resolves to an intersection, so it cannot carry variance annotations itself + * (TS2637). It doesn't need them - measuring its variance recurses into {@link ModelOperations}, + * whose annotations short-circuit the expensive cascade. See {@link CommonModelOperations}. */ export type ClientContract< Schema extends SchemaDef, @@ -257,7 +261,7 @@ export type ClientContract< /** * Factory for creating zod schemas to validate query args. */ - get $zod(): ZodSchemaFactory; + get $zod(): ZodSchemaFactory, ExtQueryArgs>; /** * Pushes the schema to the database. For testing purposes only. @@ -270,10 +274,13 @@ export type ClientContract< */ get $diagnostics(): Promise; } & { - [Key in GetSlicedModels as Uncapitalize]: ModelOperations< + // Project `Options` to its query-relevant subset before fanning out across every model. This + // strips the heavy `computedFields`/`procedures` function types (never read by these types) so + // the 30+ `ModelOperations` instantiations stay cheap. See {@link QueryRelevantOptions}. + [Key in GetSlicedModels> as Uncapitalize]: ModelOperations< Schema, Key, - Options, + QueryRelevantOptions, ExtQueryArgs, ExtResult >; @@ -342,7 +349,7 @@ type SliceOperations< T extends Record, Schema extends SchemaDef, Model extends GetModels, - Options extends ClientOptions, + Options extends QueryOptions, > = Omit< { // keep only operations included by slicing options @@ -419,12 +426,21 @@ export type AllModelOperations< ): ZenStackPromise>; }); +// Explicit variance annotations bypass TypeScript's structural variance *measurement* for this +// large, deeply-recursive generic. Measurement here comes back "unreliable" (the type recurses +// across related models) and is pure wasted work - it dominated type-check time. The annotations +// below match the measured variance and let the checker skip that probing entirely. type CommonModelOperations< - Schema extends SchemaDef, - Model extends GetModels, - Options extends QueryOptions, - ExtQueryArgs extends ExtQueryArgsBase, - ExtResult extends ExtResultBase = {}, + in out Schema extends SchemaDef, + in out Model extends GetModels, + // `Options` is invariant (it is read for `omit`/`slicing` in both arg and result positions). + // Annotating it keeps the variance-measurement skip. Note: a client built with an explicit + // `omit`/`slicing` literal is then no longer assignable to the bare `ClientContract` + // (default options) - schema-agnostic call sites that take `ClientContract` should + // accept the client via a cast. This is a rare pattern and worth the type-check speedup. + in out Options extends QueryOptions, + in out ExtQueryArgs extends ExtQueryArgsBase, + out ExtResult extends ExtResultBase = {}, > = { /** * Returns a list of entities. @@ -955,12 +971,15 @@ type CommonModelOperations< export type OperationsRequiringCreate = 'create' | 'createMany' | 'createManyAndReturn' | 'upsert'; +// See the note on `CommonModelOperations` - explicit variance annotations skip the expensive, +// "unreliable" variance measurement of this recursive type. export type ModelOperations< - Schema extends SchemaDef, - Model extends GetModels, - Options extends ClientOptions = ClientOptions, - ExtQueryArgs extends ExtQueryArgsBase = {}, - ExtResult extends ExtResultBase = {}, + in out Schema extends SchemaDef, + in out Model extends GetModels, + // `Options` is invariant - see the note on {@link CommonModelOperations}. + in out Options extends QueryOptions = ClientOptions, + in out ExtQueryArgs extends ExtQueryArgsBase = {}, + out ExtResult extends ExtResultBase = {}, > = SliceOperations, Schema, Model, Options>; //#endregion diff --git a/packages/orm/src/client/crud-types.ts b/packages/orm/src/client/crud-types.ts index 3ed16e8a6..1d6542a99 100644 --- a/packages/orm/src/client/crud-types.ts +++ b/packages/orm/src/client/crud-types.ts @@ -1324,9 +1324,9 @@ export type SelectSubset = { : {}); type ToManyRelationFilter< - Schema extends SchemaDef, - Model extends GetModels, - Field extends RelationFields, + in out Schema extends SchemaDef, + in out Model extends GetModels, + in out Field extends RelationFields, Options extends QueryOptions, > = { every?: WhereInput, Options>; @@ -1453,7 +1453,7 @@ type OppositeRelationAndFK< //#region Find args -type FilterArgs, Options extends QueryOptions> = { +type FilterArgs, in out Options extends QueryOptions> = { /** * Filter conditions */ @@ -1461,9 +1461,9 @@ type FilterArgs, Optio }; type SortAndTakeArgs< - Schema extends SchemaDef, - Model extends GetModels, - Options extends QueryOptions, + in out Schema extends SchemaDef, + in out Model extends GetModels, + in out Options extends QueryOptions, > = { /** * Number of records to skip @@ -1844,9 +1844,9 @@ export type UpdateManyAndReturnArgs< ExtractExtQueryArgs; type UpdateManyPayload< - Schema extends SchemaDef, - Model extends GetModels, - Options extends QueryOptions = QueryOptions, + in out Schema extends SchemaDef, + in out Model extends GetModels, + out Options extends QueryOptions = QueryOptions, Without extends string = never, > = { /** @@ -2105,11 +2105,15 @@ type ToManyRelationUpdateInput< : 'create' | 'createMany' | 'connectOrCreate' | 'upsert' >; +// Variance-annotated to skip TypeScript's (unreliable, expensive) variance measurement of this +// recursive arg type. Annotating the parent also short-circuits measurement of its conditional +// children (`DisconnectInput`/`NestedDeleteInput`), which can't be annotated directly. `Options` +// is invariant - see the note on `ClientContract`'s `CommonModelOperations`. type ToOneRelationUpdateInput< - Schema extends SchemaDef, - Model extends GetModels, - Field extends RelationFields, - Options extends QueryOptions, + in out Schema extends SchemaDef, + in out Model extends GetModels, + in out Field extends RelationFields, + in out Options extends QueryOptions, > = Omit< { /** @@ -2356,9 +2360,9 @@ type AggCommonOutput = Input extends true // #region GroupBy type GroupByHaving< - Schema extends SchemaDef, - Model extends GetModels, - Options extends QueryOptions = QueryOptions, + in out Schema extends SchemaDef, + in out Model extends GetModels, + out Options extends QueryOptions = QueryOptions, > = Omit, '$expr'>; export type GroupByArgs< diff --git a/packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts b/packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts index e4c13fe58..cb5035d40 100644 --- a/packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts +++ b/packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts @@ -301,9 +301,19 @@ export abstract class LateralJoinDialectBase extends B objArgs, ...Object.entries((payload as any).include) .filter(([, value]) => value) - .map(([field]) => ({ - [field]: eb.ref(`${parentResultName}$${field}.$data`), - })), + .map(([field, value]) => { + if (field === '_count') { + return { + [field]: this.buildCountJson( + relationModel as GetModels, + eb, + relationModelAlias, + value, + ), + }; + } + return { [field]: eb.ref(`${parentResultName}$${field}.$data`) }; + }), ); } diff --git a/packages/orm/src/client/crud/dialects/postgresql.ts b/packages/orm/src/client/crud/dialects/postgresql.ts index 41d9b9006..25345ada0 100644 --- a/packages/orm/src/client/crud/dialects/postgresql.ts +++ b/packages/orm/src/client/crud/dialects/postgresql.ts @@ -409,7 +409,7 @@ export class PostgresCrudDialect extends LateralJoinDi } override buildArrayLength(array: Expression): AliasableExpression { - return this.eb.fn('array_length', [array]); + return this.eb.fn('array_length', [array, sql.lit(1)]); } override buildArrayValue(values: Expression[], elemType: string): AliasableExpression { diff --git a/packages/orm/src/client/crud/dialects/sqlite.ts b/packages/orm/src/client/crud/dialects/sqlite.ts index 48418072c..52a0a315d 100644 --- a/packages/orm/src/client/crud/dialects/sqlite.ts +++ b/packages/orm/src/client/crud/dialects/sqlite.ts @@ -317,6 +317,15 @@ export class SqliteCrudDialect extends BaseCrudDialect ...Object.entries((payload as any).include) .filter(([, value]) => value) .map(([field, value]) => { + if (field === '_count') { + const subJson = this.buildCountJson( + relationModel, + eb, + tmpAlias(`${parentAlias}$${relationField}`), + value, + ); + return [sql.lit(field), subJson]; + } const subJson = this.buildRelationJSON( relationModel, eb, diff --git a/packages/orm/src/client/options.ts b/packages/orm/src/client/options.ts index 136c4928b..d70cc6120 100644 --- a/packages/orm/src/client/options.ts +++ b/packages/orm/src/client/options.ts @@ -156,6 +156,21 @@ export type QueryOptions = { slicing?: SlicingOptions; }; +/** + * Projects a (typically inferred) client options type down to only the members that influence + * ORM typing - the {@link QueryOptions} fields (`omit`, `allowQueryTimeOmitOverride`, `slicing`). + * + * The full options object inferred at `new ZenStackClient(...)` carries heavy function types for + * `computedFields` and `procedures`. Those are never read by the model/operation types, but if the + * raw options type is fanned out across every model's `ModelOperations` instantiation (30+ for a + * typical schema) it inflates type checking dramatically. Projecting to the query-relevant subset + * before the fan-out keeps the per-model types cheap while preserving full options on `$options`. + */ +export type QueryRelevantOptions = Pick< + Options, + Extract> +>; + /** * ZenStack client options. */ diff --git a/packages/orm/src/client/zod/factory.ts b/packages/orm/src/client/zod/factory.ts index 5c39946e6..42b0b8fb3 100644 --- a/packages/orm/src/client/zod/factory.ts +++ b/packages/orm/src/client/zod/factory.ts @@ -129,8 +129,11 @@ export type CreateSchemaOptions = { * Factory class responsible for creating and caching Zod schemas for ORM input validation. */ export class ZodSchemaFactory< - Schema extends SchemaDef, - Options extends ClientOptions = ClientOptions, + in out Schema extends SchemaDef, + // Bounded by `QueryOptions` (not `ClientOptions`): only `omit`/`slicing` shape the schema types. + // The `$zod` accessor passes the client's *projected* (query-relevant) options, so the heavy + // options literal never reaches the (invariant) arg types this factory produces. + in out Options extends QueryOptions = ClientOptions, ExtQueryArgs extends ExtQueryArgsBase = {}, > { private readonly schemaCache = new Map(); @@ -140,7 +143,10 @@ export class ZodSchemaFactory< private readonly options: Options; private readonly extraValidationsEnabled = true; - constructor(client: ClientContract); + // The client's `Options` type arg is intentionally erased here (`any`) - the factory only needs + // the schema and the runtime options object; its own `Options` type param is supplied (projected) + // by the caller (`$zod`). + constructor(client: ClientContract); constructor(schema: Schema, options?: Options); constructor(clientOrSchema: any, options?: Options) { if ('$schema' in clientOrSchema) { @@ -153,7 +159,9 @@ export class ZodSchemaFactory< } private get plugins(): AnyPlugin[] { - return this.options.plugins ?? []; + // `plugins` is a runtime-only field absent from the query-relevant `Options` type; + // read it weakly (the concrete options object always carries it at runtime). + return (this.options as { plugins?: AnyPlugin[] }).plugins ?? []; } /** @@ -1145,7 +1153,13 @@ export class ZodSchemaFactory< fields[field] = this.makeRelationSelectIncludeSchema(model, field, options).optional(); } } else { - fields[field] = z.boolean().optional(); + if (this.options.allowQueryTimeOmitOverride === false && this.isFieldOmittedByConfig(model, field)) { + // when query-time omit override is disallowed, an omitted field cannot be + // un-omitted by explicitly selecting it, so only allow `false` + fields[field] = z.literal(false).optional(); + } else { + fields[field] = z.boolean().optional(); + } } } @@ -1253,6 +1267,24 @@ export class ZodSchemaFactory< return result; } + /** + * Determines whether a field is configured to be omitted at the schema or client-options level + * (query-level omit is excluded as it's mutually exclusive with `select`). + */ + private isFieldOmittedByConfig(model: string, field: string): boolean { + // options-level omit + const omitConfig = + (this.options.omit as Record | undefined)?.[lowerCaseFirst(model)] ?? + (this.options.omit as Record | undefined)?.[model]; + if (omitConfig && typeof omitConfig[field] === 'boolean') { + return omitConfig[field]; + } + + // schema-level omit + const fieldDef = requireField(this.schema, model, field); + return !!fieldDef.omit; + } + @cache() private makeOmitSchema(model: string) { const fields: Record = {}; diff --git a/packages/plugins/policy/package.json b/packages/plugins/policy/package.json index 9cbb28126..535c47f99 100644 --- a/packages/plugins/policy/package.json +++ b/packages/plugins/policy/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/plugin-policy", "displayName": "ZenStack Access Policy Plugin", "description": "ZenStack plugin that enforces access control policies defined in the schema", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/plugins/policy/src/expression-transformer.ts b/packages/plugins/policy/src/expression-transformer.ts index 565afe875..27d77432a 100644 --- a/packages/plugins/policy/src/expression-transformer.ts +++ b/packages/plugins/policy/src/expression-transformer.ts @@ -254,8 +254,14 @@ export class ExpressionTransformer { return BinaryOperationNode.create(left, OperatorNode.create('in'), right); } else { // array contains + const leftFieldDef = this.getFieldDefFromFieldRef(normalizedLeft, context); + const comparand = + leftFieldDef && QueryUtils.isEnum(this.schema, leftFieldDef.type) + ? // cast lhs otherwise dialect like pg can reject due to type mismatch + this.dialect.castText(new ExpressionWrapper(left)).toOperationNode() + : left; return BinaryOperationNode.create( - left, + comparand, OperatorNode.create('='), FunctionNode.create('any', [right]), ); diff --git a/packages/plugins/soft-delete/README.md b/packages/plugins/soft-delete/README.md new file mode 100644 index 000000000..ebd094ab5 --- /dev/null +++ b/packages/plugins/soft-delete/README.md @@ -0,0 +1,80 @@ +# @zenstackhq/plugin-soft-delete + +A ZenStack runtime plugin that implements **soft delete** by intercepting Kysely queries. Instead of physically removing rows, delete operations mark them with a timestamp, and reads automatically exclude the marked rows. + +## How It Works + +The plugin works off a single `@deletedAt` marker field on each model that should support soft deletion: + +- **Deletes become updates** — a `delete`/`deleteMany` against a soft-delete model is rewritten to set the `@deletedAt` field to the current timestamp instead of issuing a `DELETE`. +- **Reads are filtered** — `find*` queries (and joined relations) automatically add a ` IS NULL` condition, so soft-deleted rows are invisible. +- **Updates skip tombstones** — `update`/`updateMany` won't touch rows that are already soft-deleted. + +Models without a `@deletedAt` field are left completely untouched. + +## Installation + +```bash +npm install @zenstackhq/plugin-soft-delete +``` + +## Usage + +### 1. Declare the plugin in your ZModel schema + +This makes the `@deletedAt` attribute available in your schema. + +```zmodel +plugin softDelete { + provider = '@zenstackhq/plugin-soft-delete' +} +``` + +### 2. Mark a nullable `DateTime` field with `@deletedAt` + +A model can have at most one `@deletedAt` field, and it must be optional (so that "not deleted" is represented by `null`). + +```zmodel +model User { + id Int @id @default(autoincrement()) + email String @unique + posts Post[] + deletedAt DateTime? @deletedAt +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int + deletedAt DateTime? @deletedAt +} +``` + +### 3. Install the plugin on your client at runtime + +```ts +import { ZenStackClient } from '@zenstackhq/orm'; +import { SoftDeletePlugin } from '@zenstackhq/plugin-soft-delete'; +import { schema } from './schema'; + +const db = new ZenStackClient(schema, { ... }).$use(new SoftDeletePlugin()); + +const user = await db.user.create({ data: { email: 'a@example.com' } }); + +// rewritten to set `deletedAt`, the row is kept in the database +await db.user.delete({ where: { id: user.id } }); + +// returns `null` — soft-deleted rows are hidden from reads +await db.user.findUnique({ where: { id: user.id } }); +``` + +## Caveats + +- **Soft deletes do not cascade.** Children of a soft-deleted parent are left untouched — managing them is up to you. (Note that a *hard* delete on a model without `@deletedAt` still triggers database-level `onDelete: Cascade` as usual.) +- **Multi-table / joined deletes can't be rewritten.** A joined or multi-table `DELETE` that targets a soft-delete model is rejected rather than silently hard-deleting rows. Use a single-table delete instead. +- **Unique constraints and tombstones.** Because soft-deleted rows physically remain, a plain `@unique` field will reject reusing a value held by a tombstone. The common mitigation is a partial unique index scoped to live rows (e.g. `... WHERE "deletedAt" IS NULL` on PostgreSQL/SQLite, or a functional index over a `CASE` expression on MySQL). + +## Learn More + +- [ZenStack Documentation](https://zenstack.dev) diff --git a/packages/plugins/soft-delete/eslint.config.js b/packages/plugins/soft-delete/eslint.config.js new file mode 100644 index 000000000..5698b9910 --- /dev/null +++ b/packages/plugins/soft-delete/eslint.config.js @@ -0,0 +1,4 @@ +import config from '@zenstackhq/eslint-config/base.js'; + +/** @type {import("eslint").Linter.Config} */ +export default config; diff --git a/packages/plugins/soft-delete/package.json b/packages/plugins/soft-delete/package.json new file mode 100644 index 000000000..3cc7a16c6 --- /dev/null +++ b/packages/plugins/soft-delete/package.json @@ -0,0 +1,68 @@ +{ + "name": "@zenstackhq/plugin-soft-delete", + "displayName": "ZenStack Soft Delete Plugin", + "description": "ZenStack plugin that implements soft-delete by intercepting Kysely queries", + "version": "3.8.0", + "type": "module", + "author": { + "name": "ZenStack Team", + "email": "contact@zenstack.dev" + }, + "homepage": "https://zenstack.dev", + "repository": { + "type": "git", + "url": "https://github.com/zenstackhq/zenstack" + }, + "license": "MIT", + "scripts": { + "build": "tsc --noEmit && tsdown", + "watch": "tsdown --watch", + "lint": "eslint src --ext ts", + "test": "vitest run", + "pack": "pnpm pack" + }, + "keywords": [], + "files": [ + "dist", + "plugin.zmodel" + ], + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./plugin.zmodel": { + "import": "./plugin.zmodel", + "require": "./plugin.zmodel" + }, + "./package.json": { + "import": "./package.json", + "require": "./package.json" + } + }, + "dependencies": { + "@zenstackhq/common-helpers": "workspace:*", + "@zenstackhq/orm": "workspace:*", + "ts-pattern": "catalog:" + }, + "peerDependencies": { + "kysely": "catalog:" + }, + "devDependencies": { + "@types/better-sqlite3": "catalog:", + "@types/node": "catalog:", + "@zenstackhq/eslint-config": "workspace:*", + "@zenstackhq/testtools": "workspace:*", + "@zenstackhq/tsdown-config": "workspace:*", + "@zenstackhq/typescript-config": "workspace:*", + "@zenstackhq/vitest-config": "workspace:*", + "better-sqlite3": "catalog:" + }, + "funding": "https://github.com/sponsors/zenstackhq" +} diff --git a/packages/plugins/soft-delete/plugin.zmodel b/packages/plugins/soft-delete/plugin.zmodel new file mode 100644 index 000000000..ba657198b --- /dev/null +++ b/packages/plugins/soft-delete/plugin.zmodel @@ -0,0 +1,10 @@ +/** + * Marks a `DateTime` field as the soft-delete indicator for its owning model. + * + * When the soft-delete plugin is installed, reads against the model are automatically filtered + * to exclude rows whose marked field is non-null, and delete operations are rewritten to set + * the marked field to the current timestamp instead of physically removing the row. + * + * A model can have at most one `@deletedAt` field. + */ +attribute @deletedAt() @@@targetField([DateTimeField]) @@@once @@@onceInModel diff --git a/packages/plugins/soft-delete/src/index.ts b/packages/plugins/soft-delete/src/index.ts new file mode 100644 index 000000000..1110b6451 --- /dev/null +++ b/packages/plugins/soft-delete/src/index.ts @@ -0,0 +1 @@ +export * from './plugin'; diff --git a/packages/plugins/soft-delete/src/plugin.ts b/packages/plugins/soft-delete/src/plugin.ts new file mode 100644 index 000000000..a20cac974 --- /dev/null +++ b/packages/plugins/soft-delete/src/plugin.ts @@ -0,0 +1,302 @@ +import { + getCrudDialect, + ORMError, + ORMErrorReason, + QueryUtils, + type BaseCrudDialect, + type ClientContract, + type OnKyselyQueryArgs, + type ProceedKyselyQueryFunction, + type RuntimePlugin, +} from '@zenstackhq/orm'; +import type { FieldDef, SchemaDef } from '@zenstackhq/orm/schema'; +import { + AliasNode, + AndNode, + BinaryOperationNode, + ColumnNode, + ColumnUpdateNode, + DeleteQueryNode, + IdentifierNode, + JoinNode, + OnNode, + OperationNodeTransformer, + OperatorNode, + OrNode, + ParensNode, + ReferenceNode, + SelectQueryNode, + TableNode, + UpdateQueryNode, + ValueNode, + WhereNode, + type OperationNode, + type RootOperationNode, +} from 'kysely'; + +const SOFT_DELETE_ATTRIBUTE = '@deletedAt'; + +export class SoftDeletePlugin implements RuntimePlugin { + get id() { + return 'soft-delete' as const; + } + + get name() { + return 'Soft Delete'; + } + + get description() { + return 'Filters reads against models with @deletedAt and transforms delete operations into updates of the @deletedAt field.'; + } + + onKyselyQuery({ query, client, proceed }: OnKyselyQueryArgs) { + const handler = new SoftDeleteHandler(client); + return handler.handle(query, proceed); + } +} + +type TableInfo = { model: string; alias?: string }; + +class SoftDeleteHandler extends OperationNodeTransformer { + private readonly dialect: BaseCrudDialect; + + constructor(private readonly client: ClientContract) { + super(); + this.dialect = getCrudDialect(client.$schema, client.$options); + } + + async handle(node: RootOperationNode, proceed: ProceedKyselyQueryFunction) { + if (DeleteQueryNode.is(node)) { + const converted = this.tryConvertDeleteToUpdate(node); + if (converted) { + // The rewritten UPDATE still flows through `transformUpdateQuery` so the + // soft-delete filter is added and already-soft-deleted rows aren't re-touched. + return proceed(this.transformNode(converted)); + } + // Not a soft-delete target: let the delete (and any DB-level cascade) proceed naturally. + } + return proceed(this.transformNode(node)); + } + + // Inject ` IS NULL` for soft-delete tables in the FROM clause. + protected override transformSelectQuery(node: SelectQueryNode): SelectQueryNode { + const result = super.transformSelectQuery(node); + if (!result.from) { + return result; + } + const filter = this.buildSoftDeleteFilterForTables(result.from.froms); + if (!filter) { + return result; + } + return { + ...result, + where: this.mergeWhere(result.where, filter), + }; + } + + // Inject ` IS NULL` into ON clauses of joins against soft-delete tables. + protected override transformJoin(node: JoinNode): JoinNode { + const result = super.transformJoin(node); + const info = this.extractTableInfo(result.table); + if (!info) { + return result; + } + // Any model that participates in a delegate (polymorphic) hierarchy has its `@deletedAt` + // handled by `transformSelectQuery`, never here: reconstruction joins are filtered through + // the outer WHERE (walking the FROM entity's base chain), and relation reads become subqueries + // filtered by their own FROM. The marker may also live on a table that isn't the one under + // this join's alias (a base, or a descendant joined for polymorphic packing). So never add an + // ON-clause predicate for a hierarchy member — at best it's redundant, and on a LEFT join it + // would only null the joined columns and leak the row. + if (this.isInDelegateHierarchy(info.model)) { + return result; + } + const deletedAt = this.getDeletedAtField(info.model); + if (!deletedAt) { + return result; + } + const filter = this.buildIsNullPredicate(info.alias ?? info.model, deletedAt.name); + return { + ...result, + on: result.on ? OnNode.create(this.andNode(result.on.on, filter)) : OnNode.create(filter), + }; + } + + // Prevent updates from touching already soft-deleted rows. + protected override transformUpdateQuery(node: UpdateQueryNode): UpdateQueryNode { + const result = super.transformUpdateQuery(node); + if (!result.table) { + return result; + } + const info = this.extractTableInfo(result.table); + if (!info) { + return result; + } + const deletedAt = this.getDeletedAtField(info.model); + if (!deletedAt) { + return result; + } + const filter = this.buildIsNullPredicate(info.alias ?? info.model, deletedAt.name); + return { + ...result, + where: this.mergeWhere(result.where, filter), + }; + } + + private tryConvertDeleteToUpdate(node: DeleteQueryNode): UpdateQueryNode | undefined { + // Only single-table deletes can be converted. Multi-table/joined deletes can't be rewritten + // into an @deletedAt update — if such a delete targets a soft-delete model, refuse rather + // than silently hard-deleting its rows; otherwise let it fall through and cascade naturally. + if (node.from.froms.length !== 1 || node.using || node.joins?.length) { + for (const fromNode of node.from.froms) { + const info = this.extractTableInfo(fromNode); + if (info && this.getDeletedAtField(info.model)) { + throw new ORMError( + ORMErrorReason.NOT_SUPPORTED, + `Cannot soft-delete from "${info.model}": multi-table or joined DELETE statements cannot be rewritten into an @deletedAt update. Use a single-table delete instead.`, + ); + } + } + return undefined; + } + const fromNode = node.from.froms[0]!; + const info = this.extractTableInfo(fromNode); + if (!info) { + return undefined; + } + const deletedAt = this.getDeletedAtField(info.model); + if (!deletedAt) { + return undefined; + } + + const now = this.dialect.transformInput(new Date(), 'DateTime', false); + const update: UpdateQueryNode = { + kind: 'UpdateQueryNode', + table: fromNode, + updates: [ColumnUpdateNode.create(ColumnNode.create(deletedAt.name), ValueNode.create(now))], + where: node.where, + with: node.with, + returning: node.returning, + limit: node.limit, + orderBy: node.orderBy, + explain: node.explain, + }; + return update; + } + + // #region helpers + + private buildSoftDeleteFilterForTables(tables: readonly OperationNode[]): OperationNode | undefined { + const filters: OperationNode[] = []; + for (const table of tables) { + const info = this.extractTableInfo(table); + if (!info) { + continue; + } + const target = this.getSoftDeleteTarget(info); + if (target) { + filters.push(this.buildIsNullPredicate(target.table, target.column)); + } + } + if (filters.length === 0) { + return undefined; + } + return filters.reduce((acc, f) => this.andNode(acc, f)); + } + + // Resolve the single `@deletedAt` column to filter when reading a table. `@@@onceInModel` allows + // at most one marker per inheritance hierarchy, so it's either declared on the model itself or + // inherited from exactly one delegate base — where the column physically lives on the base's + // table (LEFT-joined under its own model name, see buildDelegateJoin), so the filter must key off + // that base rather than the queried table. + private getSoftDeleteTarget(info: TableInfo): { table: string; column: string } | undefined { + const own = this.getDeletedAtField(info.model); + if (own) { + return { table: info.alias ?? info.model, column: own.name }; + } + let base = QueryUtils.getModel(this.client.$schema, info.model)?.baseModel; + while (base) { + const marker = this.getDeletedAtField(base); + if (marker) { + return { table: base, column: marker.name }; + } + base = QueryUtils.getModel(this.client.$schema, base)?.baseModel; + } + return undefined; + } + + private isInDelegateHierarchy(model: string): boolean { + const modelDef = QueryUtils.getModel(this.client.$schema, model); + return !!modelDef && (!!modelDef.isDelegate || !!modelDef.baseModel); + } + + private buildIsNullPredicate(table: string, column: string): OperationNode { + return BinaryOperationNode.create( + ReferenceNode.create(ColumnNode.create(column), TableNode.create(table)), + OperatorNode.create('is'), + ValueNode.createImmediate(null), + ); + } + + private andNode(a: OperationNode, b: OperationNode): OperationNode { + return AndNode.create(this.wrap(a), this.wrap(b)); + } + + private wrap(node: OperationNode): OperationNode { + return OrNode.is(node) ? ParensNode.create(node) : node; + } + + private mergeWhere(existing: WhereNode | undefined, filter: OperationNode): WhereNode { + return WhereNode.create(existing ? this.andNode(existing.where, filter) : filter); + } + + private extractTableInfo(node: OperationNode): TableInfo | undefined { + if (TableNode.is(node)) { + return { model: node.table.identifier.name }; + } + if (AliasNode.is(node)) { + const inner = this.extractTableInfo(node.node); + if (!inner) { + return undefined; + } + return { + model: inner.model, + alias: IdentifierNode.is(node.alias) ? node.alias.name : inner.alias, + }; + } + return undefined; + } + + private getDeletedAtField(model: string): FieldDef | undefined { + const modelDef = QueryUtils.getModel(this.client.$schema, model); + if (!modelDef) { + return undefined; + } + for (const fieldDef of Object.values(modelDef.fields)) { + if (fieldDef.attributes?.some((a) => a.name === SOFT_DELETE_ATTRIBUTE)) { + if (fieldDef.originModel) { + // In a delegate (polymorphic) hierarchy the marker physically lives on the base + // table, but it's surfaced on every concrete sub-model's field list (tagged with + // `originModel`). Skip it here so we don't reference a non-existent + // `.` column — the base model contributes its own FROM/JOIN + // node that carries the soft-delete filter on the real column. Likewise, a + // concrete delete is rewritten by the ORM into a delete on the base table, so the + // delete-to-update conversion also keys off the base model. + continue; + } + if (!fieldDef.optional) { + // A non-nullable marker can never be null, so the `IS NULL` read filter would + // hide every row. Require the marker to be optional so "not deleted" === null. + throw new ORMError( + ORMErrorReason.NOT_SUPPORTED, + `Field "${model}.${fieldDef.name}" is marked @deletedAt but is not optional. The soft-delete marker must be a nullable field (e.g. "DateTime?").`, + ); + } + return fieldDef; + } + } + return undefined; + } + + // #endregion +} diff --git a/packages/plugins/soft-delete/test/soft-delete.test.ts b/packages/plugins/soft-delete/test/soft-delete.test.ts new file mode 100644 index 000000000..d4a7eac28 --- /dev/null +++ b/packages/plugins/soft-delete/test/soft-delete.test.ts @@ -0,0 +1,434 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; +import { SoftDeletePlugin } from '../src'; + +// The `@deletedAt` attribute is defined in this plugin's `plugin.zmodel`. We feed it to the +// test client explicitly so that `@zenstackhq/testtools` doesn't need to depend on this plugin. +const PLUGIN_MODEL_FILE = fileURLToPath(new URL('../plugin.zmodel', import.meta.url)); + +function createSoftDeleteTestClient(schema: string, extraOptions: Record = {}) { + return createTestClient(schema, { extraPluginModelFiles: [PLUGIN_MODEL_FILE], ...extraOptions }); +} + +const schema = ` +model User { + id Int @id @default(autoincrement()) + email String @unique + posts Post[] + deletedAt DateTime? @deletedAt +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int + deletedAt DateTime? @deletedAt +} + +model Tag { + id Int @id @default(autoincrement()) + name String +} +`; + +async function makeClient() { + const raw = await createSoftDeleteTestClient(schema); + const db = raw.$use(new SoftDeletePlugin()); + return { db, raw }; +} + +describe('soft-delete plugin', () => { + it('hides soft-deleted rows from reads', async () => { + const { db } = await makeClient(); + + const a = await db.user.create({ data: { email: 'a@test.com' } }); + const b = await db.user.create({ data: { email: 'b@test.com' } }); + + await db.user.delete({ where: { id: a.id } }); + + await expect(db.user.findMany()).resolves.toEqual([ + expect.objectContaining({ id: b.id, email: 'b@test.com', deletedAt: null }), + ]); + + await expect(db.user.findUnique({ where: { id: a.id } })).resolves.toBeNull(); + await expect(db.user.findUnique({ where: { id: b.id } })).resolves.toMatchObject({ id: b.id }); + }); + + it('rewrites delete into update on the @deletedAt column', async () => { + const { db, raw } = await makeClient(); + const user = await db.user.create({ data: { email: 'soft@test.com' } }); + + // delete returns the affected record (whether its `deletedAt` reflects the new value + // depends on the dialect's delete-return strategy — RETURNING vs select-then-mutate — + // so we assert the persisted state below instead) + const deleted = await db.user.delete({ where: { id: user.id } }); + expect(deleted).toMatchObject({ id: user.id, email: 'soft@test.com' }); + + // the row is still physically present, just marked deleted (peek via plugin-less client) + const row = await raw.user.findUniqueOrThrow({ where: { id: user.id } }); + expect(row.deletedAt).not.toBeNull(); + expect(row.deletedAt!.getTime()).not.toBeNaN(); + }); + + it('skips already soft-deleted rows on subsequent deletes and updates', async () => { + const { db, raw } = await makeClient(); + const user = await db.user.create({ data: { email: 'one@test.com' } }); + + const firstDelete = await db.user.delete({ where: { id: user.id } }); + expect(firstDelete).toMatchObject({ id: user.id }); + + const firstDeletedAt = (await raw.user.findUniqueOrThrow({ where: { id: user.id } })).deletedAt!; + + // a follow-up delete should be a no-op (row already soft-deleted) and report a zero count + const secondDelete = await db.user.deleteMany({ where: { id: user.id } }); + expect(secondDelete).toEqual({ count: 0 }); + const afterSecondDelete = await raw.user.findUniqueOrThrow({ where: { id: user.id } }); + expect(afterSecondDelete.deletedAt!.getTime()).toBe(firstDeletedAt.getTime()); + + // updateMany should also skip soft-deleted rows + await db.user.updateMany({ where: { id: user.id }, data: { email: 'updated@test.com' } }); + const afterUpdate = await raw.user.findUniqueOrThrow({ where: { id: user.id } }); + expect(afterUpdate.email).toBe('one@test.com'); + }); + + it('soft-deletes a related row via a nested delete', async () => { + const { db, raw } = await makeClient(); + const user = await db.user.create({ + data: { email: 'nested@test.com', posts: { create: [{ title: 'a' }, { title: 'b' }] } }, + include: { posts: true }, + }); + // pick by title — `include` has no orderBy, so array position isn't guaranteed + const postA = user.posts.find((p: any) => p.title === 'a'); + const postB = user.posts.find((p: any) => p.title === 'b'); + + // nested delete of a soft-delete child runs as a soft delete + const updated = await db.user.update({ + where: { id: user.id }, + data: { posts: { delete: { id: postA!.id } } }, + include: { posts: true }, + }); + + // the returned relation only surfaces the surviving post + expect(updated.posts).toEqual([expect.objectContaining({ id: postB!.id, title: 'b', deletedAt: null })]); + + // post A is physically present but marked deleted; post B untouched + // (read via the plugin-less `raw` client so the soft-delete filter doesn't hide it) + const rowA = await raw.post.findUniqueOrThrow({ where: { id: postA!.id } }); + expect(rowA.deletedAt).not.toBeNull(); + + const rowB = await raw.post.findUniqueOrThrow({ where: { id: postB!.id } }); + expect(rowB.deletedAt).toBeNull(); + }); + + it('soft-deletes matching rows on deleteMany and reports the live count', async () => { + const { db, raw } = await makeClient(); + const keep = await db.user.create({ data: { email: 'keep@x.com' } }); + const drop1 = await db.user.create({ data: { email: 'drop1@y.com' } }); + const drop2 = await db.user.create({ data: { email: 'drop2@y.com' } }); + // pre-soft-deleted row that also matches the filter — must not be re-counted + const already = await db.user.create({ data: { email: 'already@y.com' } }); + await db.user.delete({ where: { id: already.id } }); + + // only the two live matching rows are counted + const result = await db.user.deleteMany({ where: { email: { endsWith: '@y.com' } } }); + expect(result).toEqual({ count: 2 }); + + // matched rows are soft-deleted (marked, not physically removed)... + const droppedRows = await raw.user.findMany({ where: { id: { in: [drop1.id, drop2.id] } } }); + expect(droppedRows).toHaveLength(2); + for (const row of droppedRows) { + expect(row.deletedAt).not.toBeNull(); + } + + // ...and reads only surface the untouched row + await expect(db.user.findMany()).resolves.toEqual([ + expect.objectContaining({ id: keep.id, email: 'keep@x.com', deletedAt: null }), + ]); + }); + + it('filters joined relations against the @deletedAt column', async () => { + const { db } = await makeClient(); + const user = await db.user.create({ data: { email: 'rel@test.com' } }); + const live = await db.post.create({ data: { title: 'live', authorId: user.id } }); + const tombstoned = await db.post.create({ data: { title: 'gone', authorId: user.id } }); + + await db.post.delete({ where: { id: tombstoned.id } }); + + const reloaded = await db.user.findUniqueOrThrow({ + where: { id: user.id }, + include: { posts: true }, + }); + + expect(reloaded.posts).toEqual([expect.objectContaining({ id: live.id, title: 'live' })]); + }); + + it('leaves models without @deletedAt untouched', async () => { + const { db, raw } = await makeClient(); + + const tag = await db.tag.create({ data: { name: 'keep' } }); + await db.tag.delete({ where: { id: tag.id } }); + + await expect(db.tag.findUnique({ where: { id: tag.id } })).resolves.toBeNull(); + + // physically gone (confirm via the plugin-less client) + await expect(raw.tag.findUnique({ where: { id: tag.id } })).resolves.toBeNull(); + }); + + it('rejects a non-nullable @deletedAt field', async () => { + // A non-optional marker can never be null, so the IS NULL read filter would hide every row. + const badSchema = ` +model Post { + id Int @id @default(autoincrement()) + title String + deletedAt DateTime @default(now()) @deletedAt +} +`; + const raw = await createSoftDeleteTestClient(badSchema); + const db = raw.$use(new SoftDeletePlugin()); + await expect(db.post.findMany()).rejects.toThrow(/"Post\.deletedAt".*not optional/); + }); + + it('rejects a model with more than one @deletedAt field', async () => { + const twoFieldsSchema = ` +model Post { + id Int @id @default(autoincrement()) + title String + deletedAt DateTime? @deletedAt + removedAt DateTime? @deletedAt +} +`; + await expect(createSoftDeleteTestClient(twoFieldsSchema)).rejects.toThrow( + /@deletedAt.*can only be applied to one field per model/, + ); + }); + + it('rejects a joined/multi-table delete that targets a soft-delete model', async () => { + const { db, raw } = await makeClient(); + const user = await db.user.create({ data: { email: 'spam@test.com' } }); + const post = await db.post.create({ data: { title: 'spammy', authorId: user.id } }); + + // A joined delete on Post (a soft-delete model) can't be rewritten into an @deletedAt update. + await expect( + db.$qb + .deleteFrom('Post') + .using('User') + .whereRef('Post.authorId', '=', 'User.id') + .where('User.email', '=', 'spam@test.com') + .execute(), + ).rejects.toThrow(/Cannot soft-delete from "Post".*single-table delete/s); + + // the row is untouched — neither hard- nor soft-deleted + const row = await raw.post.findUniqueOrThrow({ where: { id: post.id } }); + expect(row.deletedAt).toBeNull(); + }); + + it('rewrites a single-table $qb delete on a soft-delete model into an update', async () => { + const { db, raw } = await makeClient(); + const user = await db.user.create({ data: { email: 'qb@test.com' } }); + + // low-level Kysely delete still flows through the plugin's onKyselyQuery hook; + // the rewritten update still reports the affected-row count as a delete result + const result = await db.$qb.deleteFrom('User').where('id', '=', user.id).execute(); + expect(result).toEqual([{ numDeletedRows: 1n }]); + + // physically present, just marked deleted + const row = await raw.user.findUniqueOrThrow({ where: { id: user.id } }); + expect(row.deletedAt).not.toBeNull(); + + // and hidden from reads through the plugin + await expect(db.user.findUnique({ where: { id: user.id } })).resolves.toBeNull(); + }); + + it('lets a $qb delete on a model without @deletedAt delete physically', async () => { + const { db, raw } = await makeClient(); + const tag = await db.tag.create({ data: { name: 'qb-keep' } }); + + const result = await db.$qb.deleteFrom('Tag').where('id', '=', tag.id).execute(); + expect(result).toEqual([{ numDeletedRows: 1n }]); + + await expect(raw.tag.findUnique({ where: { id: tag.id } })).resolves.toBeNull(); + }); + + it('does not propagate the soft-delete to children (left to the user)', async () => { + // The plugin intentionally does not cascade soft-deletes. Children of a soft-deleted + // parent are left untouched; managing them is the user's responsibility. + const cascadeSchema = ` +model Parent { + id Int @id @default(autoincrement()) + name String + children Child[] + deletedAt DateTime? @deletedAt +} + +model Child { + id Int @id @default(autoincrement()) + parent Parent @relation(fields: [parentId], references: [id]) + parentId Int + deletedAt DateTime? @deletedAt +} +`; + const raw = await createSoftDeleteTestClient(cascadeSchema); + const db = raw.$use(new SoftDeletePlugin()); + + const parent = await db.parent.create({ + data: { name: 'p', children: { create: [{}, {}] } }, + }); + + await db.parent.delete({ where: { id: parent.id } }); + + // parent is soft-deleted... + const parentRow = await raw.parent.findUniqueOrThrow({ where: { id: parent.id } }); + expect(parentRow.deletedAt).not.toBeNull(); + + // ...but its children are left untouched + const childRows = await raw.child.findMany({ where: { parentId: parent.id } }); + expect(childRows).toHaveLength(2); + for (const row of childRows) { + expect(row.deletedAt).toBeNull(); + } + }); + + it('lets a hard-delete cascade naturally at the database', async () => { + // The parent has no @deletedAt, so its delete is a real DELETE. The plugin does not + // interfere, so the DB-level onDelete: Cascade hard-deletes the children too. + const cascadeSchema = ` +model Parent { + id Int @id @default(autoincrement()) + name String + children Child[] +} + +model Child { + id Int @id @default(autoincrement()) + parent Parent @relation(fields: [parentId], references: [id], onDelete: Cascade) + parentId Int + deletedAt DateTime? @deletedAt +} +`; + const raw = await createSoftDeleteTestClient(cascadeSchema); + const db = raw.$use(new SoftDeletePlugin()); + + const parent = await db.parent.create({ + data: { name: 'p', children: { create: [{}, {}] } }, + }); + + await db.parent.delete({ where: { id: parent.id } }); + + // parent and its children are physically gone + await expect(raw.parent.findUnique({ where: { id: parent.id } })).resolves.toBeNull(); + await expect(raw.child.findMany({ where: { parentId: parent.id } })).resolves.toHaveLength(0); + }); + + it('soft-deletes a model whose @deletedAt is inherited from a mixin', async () => { + // The marker is declared on a `type` mixin and flattened into the model's fields, so the + // plugin should treat the composed model exactly like one that declares @deletedAt directly. + const mixinSchema = ` +type SoftDeletable { + deletedAt DateTime? @deletedAt +} + +model Item with SoftDeletable { + id Int @id @default(autoincrement()) + name String +} +`; + const raw = await createSoftDeleteTestClient(mixinSchema); + const db = raw.$use(new SoftDeletePlugin()); + + const a = await db.item.create({ data: { name: 'a' } }); + const b = await db.item.create({ data: { name: 'b' } }); + + await db.item.delete({ where: { id: a.id } }); + + // hidden from reads through the plugin + await expect(db.item.findMany()).resolves.toEqual([ + expect.objectContaining({ id: b.id, name: 'b', deletedAt: null }), + ]); + await expect(db.item.findUnique({ where: { id: a.id } })).resolves.toBeNull(); + + // physically present, just marked deleted (peek via the plugin-less client) + const row = await raw.item.findUniqueOrThrow({ where: { id: a.id } }); + expect(row.deletedAt).not.toBeNull(); + }); + + it('soft-deletes a delegate model marked on the base', async () => { + // The @deletedAt marker lives on the polymorphic base. Deleting through either the base or a + // concrete model should soft-delete the base row, and reads through both should hide it. + const delegateSchema = ` +model Content { + id Int @id @default(autoincrement()) + contentType String + deletedAt DateTime? @deletedAt + @@delegate(contentType) +} + +model Article extends Content { + title String +} +`; + const raw = await createSoftDeleteTestClient(delegateSchema, { usePrismaPush: true }); + const db = raw.$use(new SoftDeletePlugin()); + + const a = await db.article.create({ data: { title: 'a' } }); + const b = await db.article.create({ data: { title: 'b' } }); + + // delete through the concrete model + await db.article.delete({ where: { id: a.id } }); + + // hidden from reads through both the concrete and base accessors + await expect(db.article.findUnique({ where: { id: a.id } })).resolves.toBeNull(); + await expect(db.content.findUnique({ where: { id: a.id } })).resolves.toBeNull(); + await expect(db.article.findMany()).resolves.toEqual([expect.objectContaining({ id: b.id, title: 'b' })]); + + // the base row is physically present, just marked deleted (the concrete row survives too) + const baseRow = await raw.content.findUniqueOrThrow({ where: { id: a.id } }); + expect(baseRow.deletedAt).not.toBeNull(); + await expect(raw.article.findUnique({ where: { id: a.id } })).resolves.not.toBeNull(); + + // delete through the base model soft-deletes as well + await db.content.delete({ where: { id: b.id } }); + await expect(db.article.findUnique({ where: { id: b.id } })).resolves.toBeNull(); + const baseRowB = await raw.content.findUniqueOrThrow({ where: { id: b.id } }); + expect(baseRowB.deletedAt).not.toBeNull(); + }); + + it('does not update an already soft-deleted delegate row', async () => { + // The marker lives on the base, but updates target the concrete sub-table. Updating a + // concrete-only field of a soft-deleted row must still be a no-op. The ORM funnels every + // delegate update through an `id IN ()` subquery that inherits the soft-delete read + // filter, so the soft-deleted row is invisible to the update. + const delegateSchema = ` +model Content { + id Int @id @default(autoincrement()) + contentType String + deletedAt DateTime? @deletedAt + @@delegate(contentType) +} + +model Article extends Content { + title String +} +`; + const raw = await createSoftDeleteTestClient(delegateSchema, { usePrismaPush: true }); + const db = raw.$use(new SoftDeletePlugin()); + + const a = await db.article.create({ data: { title: 'a' } }); + await db.article.delete({ where: { id: a.id } }); + + // updateMany on the concrete field skips the soft-deleted row and reports a zero count + await expect(db.article.updateMany({ where: { id: a.id }, data: { title: 'changed' } })).resolves.toEqual({ + count: 0, + }); + + // a single update can't find the soft-deleted row at all + await expect(db.article.update({ where: { id: a.id }, data: { title: 'changed' } })).rejects.toThrow( + /not found/i, + ); + + // the concrete row is physically untouched (peek via the plugin-less client) + const row = await raw.article.findUniqueOrThrow({ where: { id: a.id } }); + expect(row.title).toBe('a'); + }); +}); diff --git a/packages/plugins/soft-delete/test/tombstone-unique.test.ts b/packages/plugins/soft-delete/test/tombstone-unique.test.ts new file mode 100644 index 000000000..1ad2d3f6f --- /dev/null +++ b/packages/plugins/soft-delete/test/tombstone-unique.test.ts @@ -0,0 +1,79 @@ +import { createTestClient, getTestDbProvider } from '@zenstackhq/testtools'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; +import { SoftDeletePlugin } from '../src'; + +// The `@deletedAt` attribute is defined in this plugin's `plugin.zmodel`. We feed it to the +// test client explicitly so that `@zenstackhq/testtools` doesn't need to depend on this plugin. +const PLUGIN_MODEL_FILE = fileURLToPath(new URL('../plugin.zmodel', import.meta.url)); + +function createSoftDeleteTestClient(schema: string, provider?: 'sqlite' | 'postgresql' | 'mysql') { + return createTestClient(schema, { extraPluginModelFiles: [PLUGIN_MODEL_FILE], provider }); +} + +// A column-based soft delete leaves a tombstone behind, so a plain `@unique` would reject reusing +// the value. The mitigation is a unique index scoped to live (non-deleted) rows. ZModel can't +// express that condition, so each test below emits the dialect-specific DDL directly (no `@unique` +// on the model) and asserts the same behavior: live rows are unique, tombstones may share a value. +describe('tombstone unique conflict mitigation (per-dialect index)', () => { + const uniqueSchema = ` +model User { + id Int @id @default(autoincrement()) + email String + deletedAt DateTime? @deletedAt +} +`; + + async function expectTombstoneUniqueBehavior(db: any) { + const a = await db.user.create({ data: { email: 'a@x.com' } }); + + // a second *live* row with the same email collides + await expect(db.user.create({ data: { email: 'a@x.com' } })).rejects.toThrow(); + + // soft-deleting frees the value — the tombstone leaves the index's scope + await db.user.delete({ where: { id: a.id } }); + const b = await db.user.create({ data: { email: 'a@x.com' } }); + expect(b.id).not.toBe(a.id); + + // uniqueness is still enforced among the remaining live rows + await expect(db.user.create({ data: { email: 'a@x.com' } })).rejects.toThrow(); + } + + it('sqlite: partial unique index scoped to live rows', async ({ skip }) => { + if (getTestDbProvider() !== 'sqlite') { + skip(); + } + const raw = await createSoftDeleteTestClient(uniqueSchema, 'sqlite'); + const db = raw.$use(new SoftDeletePlugin()); + await raw.$executeRawUnsafe( + `CREATE UNIQUE INDEX "User_email_active" ON "User" ("email") WHERE "deletedAt" IS NULL`, + ); + await expectTombstoneUniqueBehavior(db); + }); + + it('postgresql: partial unique index scoped to live rows', async ({ skip }) => { + if (getTestDbProvider() !== 'postgresql') { + skip(); + } + const raw = await createSoftDeleteTestClient(uniqueSchema, 'postgresql'); + const db = raw.$use(new SoftDeletePlugin()); + await raw.$executeRawUnsafe( + `CREATE UNIQUE INDEX "User_email_active" ON "User" ("email") WHERE "deletedAt" IS NULL`, + ); + await expectTombstoneUniqueBehavior(db); + }); + + it('mysql: functional unique index over a CASE expression', async ({ skip }) => { + if (getTestDbProvider() !== 'mysql') { + skip(); + } + const raw = await createSoftDeleteTestClient(uniqueSchema, 'mysql'); + const db = raw.$use(new SoftDeletePlugin()); + // MySQL has no partial indexes; a functional key part over a CASE expr yields NULL for + // tombstones (and MySQL allows multiple NULLs in a unique index). Requires MySQL 8.0.13+. + await raw.$executeRawUnsafe( + 'ALTER TABLE `User` ADD UNIQUE INDEX `User_email_active` ((CASE WHEN `deletedAt` IS NULL THEN `email` END))', + ); + await expectTombstoneUniqueBehavior(db); + }); +}); diff --git a/packages/plugins/soft-delete/tsconfig.json b/packages/plugins/soft-delete/tsconfig.json new file mode 100644 index 000000000..1100998bc --- /dev/null +++ b/packages/plugins/soft-delete/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@zenstackhq/typescript-config/base.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["src/**/*", "test/**/*.ts"] +} diff --git a/packages/plugins/soft-delete/tsdown.config.ts b/packages/plugins/soft-delete/tsdown.config.ts new file mode 100644 index 000000000..e0a6d5624 --- /dev/null +++ b/packages/plugins/soft-delete/tsdown.config.ts @@ -0,0 +1,3 @@ +import { createConfig } from '@zenstackhq/tsdown-config'; + +export default createConfig({ entry: { index: 'src/index.ts' } }); diff --git a/packages/plugins/soft-delete/vitest.config.ts b/packages/plugins/soft-delete/vitest.config.ts new file mode 100644 index 000000000..75a9f709c --- /dev/null +++ b/packages/plugins/soft-delete/vitest.config.ts @@ -0,0 +1,4 @@ +import base from '@zenstackhq/vitest-config/base'; +import { defineConfig, mergeConfig } from 'vitest/config'; + +export default mergeConfig(base, defineConfig({})); diff --git a/packages/schema/package.json b/packages/schema/package.json index a5e42c1fb..6ddd024d6 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/schema", "displayName": "ZenStack Schema Object Model", "description": "TypeScript representation of ZModel schema", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d886d8765..4bef954f7 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/sdk", "displayName": "ZenStack SDK", "description": "Utilities for building ZenStack plugins", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index b213ff89f..c962b626b 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -2,4 +2,5 @@ import * as ModelUtils from './model-utils'; export * from './cli-plugin'; export { PrismaSchemaGenerator } from './prisma/prisma-schema-generator'; export * from './ts-schema-generator'; +export * from './tsconfig-utils'; export { ModelUtils }; diff --git a/packages/sdk/src/prisma/prisma-schema-generator.ts b/packages/sdk/src/prisma/prisma-schema-generator.ts index 57fd9c6e9..f15422678 100644 --- a/packages/sdk/src/prisma/prisma-schema-generator.ts +++ b/packages/sdk/src/prisma/prisma-schema-generator.ts @@ -90,19 +90,19 @@ export class PrismaSchemaGenerator { for (const decl of this.zmodel.declarations) { switch (decl.$type) { - case DataSource: + case DataSource.$type: this.generateDataSource(prisma, decl as DataSource); break; - case Enum: + case Enum.$type: this.generateEnum(prisma, decl as Enum); break; - case DataModel: + case DataModel.$type: this.generateModel(prisma, decl as DataModel); break; - case GeneratorDecl: + case GeneratorDecl.$type: this.generateGenerator(prisma, decl as GeneratorDecl); break; } @@ -351,9 +351,9 @@ export class PrismaSchemaGenerator { private makeAttributeArgValue(node: Expression): PrismaAttributeArgValue { if (isLiteralExpr(node)) { const argType = match(node.$type) - .with(StringLiteral, () => 'String' as const) - .with(NumberLiteral, () => 'Number' as const) - .with(BooleanLiteral, () => 'Boolean' as const) + .with(StringLiteral.$type, () => 'String' as const) + .with(NumberLiteral.$type, () => 'Number' as const) + .with(BooleanLiteral.$type, () => 'Boolean' as const) .exhaustive(); return new PrismaAttributeArgValue(argType, node.value); } else if (isArrayExpr(node)) { diff --git a/packages/sdk/src/tsconfig-utils.ts b/packages/sdk/src/tsconfig-utils.ts new file mode 100644 index 000000000..c294114c3 --- /dev/null +++ b/packages/sdk/src/tsconfig-utils.ts @@ -0,0 +1,54 @@ +import path from 'node:path'; +import * as ts from 'typescript'; + +/** + * Detects the file extension that should be appended to relative imports in + * generated TypeScript, by inspecting the nearest `tsconfig.json` to the given + * directory. + * + * Returns `'.js'` when the project uses native ESM module resolution + * (`node16`/`nodenext`), which requires explicit extensions on relative imports. + * Returns `undefined` for `bundler`/`node` resolution (where extensionless + * imports are the idiomatic, maximally-compatible form) or when no tsconfig is + * found. + */ +export function detectImportFileExtension(fromDir: string): string | undefined { + const configPath = ts.findConfigFile(fromDir, ts.sys.fileExists, 'tsconfig.json'); + if (!configPath) { + return undefined; + } + + const configFile = ts.readConfigFile(configPath, ts.sys.readFile); + if (configFile.error) { + return undefined; + } + + // resolve `extends` chains and module/moduleResolution defaults + const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath)); + + // Prefer an explicit `moduleResolution`; when it's omitted, TypeScript derives + // it from `module`, so fall back to inspecting the module kind ourselves. + let moduleResolution = parsed.options.moduleResolution; + if (moduleResolution === undefined) { + switch (parsed.options.module) { + case ts.ModuleKind.Node16: + case ts.ModuleKind.Node18: + case ts.ModuleKind.Node20: + case ts.ModuleKind.NodeNext: + moduleResolution = ts.ModuleResolutionKind.NodeNext; + break; + default: + break; + } + } + + // node16/nodenext resolution requires explicit extensions on relative imports + if ( + moduleResolution === ts.ModuleResolutionKind.Node16 || + moduleResolution === ts.ModuleResolutionKind.NodeNext + ) { + return '.js'; + } + + return undefined; +} diff --git a/packages/server/package.json b/packages/server/package.json index f71f11d6e..b48020437 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/server", "displayName": "ZenStack Automatic CRUD Server", "description": "ZenStack automatic CRUD API handlers and server adapters for popular frameworks", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/server/src/api/rpc/openapi.ts b/packages/server/src/api/rpc/openapi.ts index 6bd6e2c5c..35ff95649 100644 --- a/packages/server/src/api/rpc/openapi.ts +++ b/packages/server/src/api/rpc/openapi.ts @@ -9,6 +9,7 @@ import { DEFAULT_SPEC_VERSION, getIncludedModels, getMetaDescription, + isModelIncluded, isOperationIncluded, isProcedureIncluded, mayDenyAccess, @@ -485,6 +486,10 @@ export class RPCApiSpecGenerator { if (this.isBuiltinType(returnType)) { base = this.builtinTypeToJsonSchema(returnType as BuiltinType); + } else if (this.schema.models?.[returnType] && !isModelIncluded(returnType, this.queryOptions)) { + // Return type is a model that's sliced away — its entity schema isn't emitted, + // so reference it with a generic schema instead of a dangling `$ref`. + base = {}; } else if ( this.schema.enums?.[returnType] || this.schema.models?.[returnType] || @@ -685,6 +690,10 @@ export class RPCApiSpecGenerator { if (fieldDef.omit) continue; if (fieldDef.relation) { + // Skip relations pointing to a model that's sliced away — otherwise we'd emit + // a dangling `$ref` to a schema that's not in the spec. + if (!isModelIncluded(fieldDef.type, this.queryOptions)) continue; + // Relation fields appear only with `include` — mark as optional. // To-one optional relations are nullable (the ORM returns null when not found). const refSchema: ReferenceObject = { $ref: `#/components/schemas/${fieldDef.type}` }; diff --git a/packages/server/test/openapi/rpc-openapi.test.ts b/packages/server/test/openapi/rpc-openapi.test.ts index 64d4b1ca4..6e25cb270 100644 --- a/packages/server/test/openapi/rpc-openapi.test.ts +++ b/packages/server/test/openapi/rpc-openapi.test.ts @@ -596,6 +596,26 @@ describe('RPC OpenAPI spec generation - queryOptions slicing', () => { expect(spec.components?.schemas?.['User']).toBeDefined(); }); + it('drops relation fields pointing to excluded models from entity schemas', async () => { + const client = await createTestClient(schema); + const handler = new RPCApiHandler({ + schema: client.$schema, + queryOptions: { slicing: { excludedModels: ['Post'] as any } }, + }); + // `generateSpec` also validates the document, so a dangling `$ref` to the + // excluded `Post` schema would fail here. + const spec = await generateSpec(handler); + + // referencing models keep their other fields but lose relations to the excluded model + const userProps = (spec.components?.schemas?.['User'] as any)?.properties; + expect(userProps?.['email']).toBeDefined(); + expect(userProps?.['posts']).toBeUndefined(); + + const commentProps = (spec.components?.schemas?.['Comment'] as any)?.properties; + expect(commentProps?.['content']).toBeDefined(); + expect(commentProps?.['post']).toBeUndefined(); + }); + it('includedModels removes excluded model entity schemas from components', async () => { const client = await createTestClient(schema); const handler = new RPCApiHandler({ @@ -858,6 +878,30 @@ mutation procedure softDelete(id: Int?): User expect(schemaKeys.some((k) => k.startsWith('createUser'))).toBe(true); }); + it('procedure returning an excluded model does not emit a dangling ref', async () => { + const client = await createTestClient(procSchema); + const handler = new RPCApiHandler({ + schema: client.$schema, + queryOptions: { slicing: { excludedModels: ['User'] as any } }, + }); + // `getUser`/`createUser`/`optionalSearch` all return `User`, which is excluded. + // `generateSpec` validates, so a dangling `$ref` to the absent `User` schema would fail. + const spec = await generateSpec(handler); + + expect(spec.components?.schemas?.['User']).toBeUndefined(); + + // the procedures themselves are still exposed, but their result shape is generic + const dataSchema = (spec.paths?.['/$procs/getUser']?.get as any)?.responses?.['200']?.content?.[ + 'application/json' + ]?.schema?.properties?.data; + expect(dataSchema).toEqual({}); + + const listDataSchema = (spec.paths?.['/$procs/optionalSearch']?.get as any)?.responses?.['200']?.content?.[ + 'application/json' + ]?.schema?.properties?.data; + expect(listDataSchema).toEqual({ type: 'array', items: {} }); + }); + it('slicing includedProcedures removes non-listed procedure args from components schemas', async () => { const client = await createTestClient(procSchema); const handler = new RPCApiHandler({ diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 5e9cb76e7..d445bba40 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/testtools", "displayName": "ZenStack Test Tools", "description": "ZenStack Test Tools", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/testtools/src/client.ts b/packages/testtools/src/client.ts index c9d0ca8b3..6b1bc107c 100644 --- a/packages/testtools/src/client.ts +++ b/packages/testtools/src/client.ts @@ -72,6 +72,13 @@ type ExtraTestClientOptions = { */ extraZModelFiles?: Record; + /** + * Extra plugin `plugin.zmodel` files (resolved absolute paths) to merge into the schema + * when loading. Use this to make a plugin's custom attributes available without testtools + * having to depend on the plugin. + */ + extraPluginModelFiles?: string[]; + /** * Extra TypeScript source files to create and compile. */ @@ -143,6 +150,7 @@ export async function createTestClient( options?.extraSourceFiles, undefined, options?.extraZModelFiles, + options?.extraPluginModelFiles, ); workDir = generated.workDir; model = generated.model; @@ -226,7 +234,10 @@ export async function createTestClient( 'a schema file must be provided when using prisma db push', ); if (!model) { - const r = await loadDocumentWithPlugins(path.join(workDir, 'schema.zmodel')); + const r = await loadDocumentWithPlugins( + path.join(workDir, 'schema.zmodel'), + options?.extraPluginModelFiles, + ); if (!r.success) { throw new Error(r.errors.join('\n')); } diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index ffd016b11..98c836435 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -62,6 +62,7 @@ export async function generateTsSchema( extraSourceFiles?: Record, withLiteSchema?: boolean, extraZModelFiles?: Record, + extraPluginModelFiles?: string[], ) { const workDir = createTestProject(); @@ -85,7 +86,7 @@ export async function generateTsSchema( } } - const result = await loadDocumentWithPlugins(zmodelPath); + const result = await loadDocumentWithPlugins(zmodelPath, extraPluginModelFiles); if (!result.success) { throw new Error(`Failed to load schema from ${zmodelPath}: ${result.errors}`); } diff --git a/packages/testtools/src/utils.ts b/packages/testtools/src/utils.ts index 1f8119fe3..52ec98c40 100644 --- a/packages/testtools/src/utils.ts +++ b/packages/testtools/src/utils.ts @@ -1,6 +1,6 @@ import { loadDocument } from '@zenstackhq/language'; -export function loadDocumentWithPlugins(filePath: string) { - const pluginModelFiles = [require.resolve('@zenstackhq/plugin-policy/plugin.zmodel')]; +export function loadDocumentWithPlugins(filePath: string, extraPluginModelFiles: string[] = []) { + const pluginModelFiles = [require.resolve('@zenstackhq/plugin-policy/plugin.zmodel'), ...extraPluginModelFiles]; return loadDocument(filePath, pluginModelFiles); } diff --git a/packages/zod/package.json b/packages/zod/package.json index 4161686f8..417ab942f 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -2,7 +2,7 @@ "name": "@zenstackhq/zod", "displayName": "ZenStack Zod Integration", "description": "Automatically deriving Zod schemas from ZModel schemas", - "version": "3.7.2", + "version": "3.8.0", "type": "module", "author": { "name": "ZenStack Team", diff --git a/packages/zod/src/utils.ts b/packages/zod/src/utils.ts index f21d9196f..b048f6abf 100644 --- a/packages/zod/src/utils.ts +++ b/packages/zod/src/utils.ts @@ -12,6 +12,16 @@ import Decimal from 'decimal.js'; import { z } from 'zod'; import { SchemaFactoryError } from './error'; +// z.string()[mapped] +const stringFuncZodMap = { + isEmail: 'email', + isUrl: 'url', + isPhone: 'e164', + isDate: 'date', + isTime: 'time', + isDateTime: 'datetime', +} as const; + function getArgValue(expr: Expression | undefined): T | undefined { if (!expr || !ExpressionUtils.isLiteral(expr)) { return undefined; @@ -75,6 +85,14 @@ export function addStringValidation( case '@phone': result = result.e164(); break; + case '@date': + result = result.date(); + break; + case '@time': { + const precision = getArgValue(attr.args?.[0]?.value); + result = result.time({ precision }); + break; + } case '@datetime': result = result.datetime(); break; @@ -537,18 +555,19 @@ function evalCall(data: any, expr: CallExpression) { case 'isEmail': case 'isUrl': case 'isPhone': + case 'isDate': + case 'isTime': case 'isDateTime': { if (fieldArg === undefined || fieldArg === null || fieldArg === ABSENT) { return false; } invariant(typeof fieldArg === 'string', `"${f}" first argument must be a string`); - const fn = f === 'isEmail' - ? ('email' as const) - : f === 'isUrl' - ? ('url' as const) - : f === 'isPhone' - ? ('e164' as const) - : ('datetime' as const); + if (f === 'isTime') { + const precision = getArgValue(expr.args?.[1]); + invariant((precision === null || precision == undefined) || typeof precision === 'number', `"isTime" optional second argument must be a number`); + return z.iso.time({ precision }).safeParse(fieldArg).success; + } + const fn = stringFuncZodMap[f]; return z.string()[fn]().safeParse(fieldArg).success; } // list functions diff --git a/packages/zod/test/factory.test.ts b/packages/zod/test/factory.test.ts index 8e81b69ea..a0bc7592c 100644 --- a/packages/zod/test/factory.test.ts +++ b/packages/zod/test/factory.test.ts @@ -21,6 +21,8 @@ const validUser = { balance: 10.0, active: true, birthdate: null, + localTime: null, + createdAt: null, avatar: null, metadata: null, status: 'ACTIVE', @@ -50,6 +52,8 @@ describe('SchemaFactory - makeModelSchema', () => { expectTypeOf().toEqualTypeOf(); // optional string field (nullable + optional) expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); // number fields (Int and Float both map to ZodNumber) expectTypeOf().toEqualTypeOf(); @@ -65,7 +69,7 @@ describe('SchemaFactory - makeModelSchema', () => { expectTypeOf().toEqualTypeOf(); // DateTime - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); // optional Bytes expectTypeOf().toEqualTypeOf(); @@ -144,7 +148,7 @@ describe('SchemaFactory - makeModelSchema', () => { it('accepts DateTime as a Date object', () => { const userSchema = factory.makeModelSchema('User'); - const result = userSchema.safeParse({ ...validUser, birthdate: new Date() }); + const result = userSchema.safeParse({ ...validUser, createdAt: new Date() }); expect(result.success).toBe(true); }); @@ -152,7 +156,7 @@ describe('SchemaFactory - makeModelSchema', () => { const userSchema = factory.makeModelSchema('User'); const result = userSchema.safeParse({ ...validUser, - birthdate: '2024-01-15T10:30:00.000Z', + createdAt: '2024-01-15T10:30:00.000Z', }); expect(result.success).toBe(true); }); @@ -214,7 +218,7 @@ describe('SchemaFactory - makeModelSchema', () => { it('infers correct input types for fields', () => { const _userSchema = factory.makeModelSchema('User'); type UserInput = z.input; - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); }); @@ -281,6 +285,42 @@ describe('SchemaFactory - makeModelSchema', () => { expect(result.success).toBe(true); }); + it('rejects invalid date for @date field', () => { + const userSchema = factory.makeModelSchema('User'); + const result = userSchema.safeParse({ ...validUser, birthdate: 'not-a-date' }); + expect(result.success).toBe(false); + }); + + it('accepts valid date for @date field', () => { + const userSchema = factory.makeModelSchema('User'); + const result = userSchema.safeParse({ ...validUser, birthdate: '2000-01-01' }); + expect(result.success).toBe(true); + }); + + it('accepts null for optional @date field', () => { + const userSchema = factory.makeModelSchema('User'); + const result = userSchema.safeParse({ ...validUser, birthdate: null }); + expect(result.success).toBe(true); + }); + + it('rejects invalid time for @time field', () => { + const userSchema = factory.makeModelSchema('User'); + const result = userSchema.safeParse({ ...validUser, localTime: 'not-a-time' }); + expect(result.success).toBe(false); + }); + + it('accepts valid time for @time field', () => { + const userSchema = factory.makeModelSchema('User'); + const result = userSchema.safeParse({ ...validUser, localTime: '03:15:00' }); + expect(result.success).toBe(true); + }); + + it('accepts null for optional @time field', () => { + const userSchema = factory.makeModelSchema('User'); + const result = userSchema.safeParse({ ...validUser, localTime: null }); + expect(result.success).toBe(true); + }); + it('rejects code that does not start with "USR" for @startsWith', () => { const userSchema = factory.makeModelSchema('User'); const result = userSchema.safeParse({ ...validUser, code: 'ABC001' }); @@ -608,6 +648,8 @@ describe('SchemaFactory - makeTypeSchema', () => { balance: 1, active: true, birthdate: null, + localTime: null, + createdAt: null, avatar: null, metadata: null, status: 'ACTIVE', @@ -1378,6 +1420,8 @@ describe('SchemaFactory - makeModelSchema with options', () => { expectTypeOf().toEqualTypeOf(); // already-optional nullable field expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); }); it('infers omitted field absent even with optionality all', () => { diff --git a/packages/zod/test/schema/schema.ts b/packages/zod/test/schema/schema.ts index 3b8dd6eb2..fa7bc045c 100644 --- a/packages/zod/test/schema/schema.ts +++ b/packages/zod/test/schema/schema.ts @@ -73,6 +73,18 @@ export class SchemaType implements SchemaDef { }, birthdate: { name: "birthdate", + type: "String", + optional: true, + attributes: [{ name: "@date" }] as readonly AttributeApplication[] + }, + localTime: { + name: "localTime", + type: "String", + optional: true, + attributes: [{ name: "@time" }] as readonly AttributeApplication[] + }, + createdAt: { + name: "createdAt", type: "DateTime", optional: true }, diff --git a/packages/zod/test/schema/schema.zmodel b/packages/zod/test/schema/schema.zmodel index 0fc3aec88..e7deb27aa 100644 --- a/packages/zod/test/schema/schema.zmodel +++ b/packages/zod/test/schema/schema.zmodel @@ -32,7 +32,9 @@ model User { bigNum BigInt @gte(0) balance Decimal @gt(0) active Boolean - birthdate DateTime? + birthdate String? @date + localTime String? @time + createdAt DateTime? avatar Bytes? metadata Json? status Status diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f22e8e26..dd9b6bbc3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,11 +46,11 @@ catalogs: specifier: ~0.29.0 version: 0.29.0 langium: - specifier: 3.5.0 - version: 3.5.0 + specifier: 4.2.4 + version: 4.2.4 langium-cli: - specifier: 3.5.0 - version: 3.5.0 + specifier: 4.2.1 + version: 4.2.1 mysql2: specifier: ^3.16.1 version: 3.16.1 @@ -84,6 +84,9 @@ catalogs: ts-pattern: specifier: ^5.7.1 version: 5.7.1 + tsx: + specifier: ^4.22.0 + version: 4.22.4 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -99,8 +102,7 @@ catalogs: overrides: cookie@<0.7.0: '>=0.7.0' - lodash-es@>=4.0.0 <=4.17.22: '>=4.17.23' - lodash@>=4.0.0 <=4.17.22: '>=4.17.23' + lodash@>=4.0.0 <=4.17.23: '>=4.18.0' '@better-auth/core': 1.4.19 importers: @@ -115,7 +117,7 @@ importers: version: 20.19.24 '@vitest/coverage-v8': specifier: ^4.0.16 - version: 4.0.16(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0)) + version: 4.0.16(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0)) eslint: specifier: ~9.29.0 version: 9.29.0(jiti@2.6.1) @@ -135,8 +137,8 @@ importers: specifier: ^0.21.8 version: 0.21.8(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3)) tsx: - specifier: ^4.20.3 - version: 4.20.3 + specifier: 'catalog:' + version: 4.22.4 turbo: specifier: ^2.5.4 version: 2.5.4 @@ -148,7 +150,7 @@ importers: version: 8.34.1(eslint@9.29.0(jiti@2.6.1))(typescript@6.0.3) vitest: specifier: ^4.0.14 - version: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0) + version: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0) yaml: specifier: ^2.8.0 version: 2.8.0 @@ -170,10 +172,10 @@ importers: devDependencies: '@better-auth/cli': specifier: 1.4.19 - version: 1.4.19(@better-fetch/fetch@1.1.21)(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-call@1.1.8(zod@4.3.6))(bun-types@1.3.3)(jose@6.1.2)(kysely@0.29.0)(magicast@0.5.1)(mysql2@3.16.1)(nanostores@1.0.1)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sql.js@1.13.0)(svelte@5.53.5)(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + version: 1.4.19(@better-fetch/fetch@1.1.21)(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-call@1.1.8(zod@4.3.6))(bun-types@1.3.3)(jose@6.2.3)(kysely@0.29.0)(magicast@0.5.1)(mysql2@3.16.1)(nanostores@1.3.0)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sql.js@1.13.0)(svelte@5.53.5)(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) '@better-auth/core': specifier: 1.4.19 - version: 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1) + version: 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) '@types/tmp': specifier: 'catalog:' version: 0.2.6 @@ -194,7 +196,7 @@ importers: version: link:../../config/vitest-config better-auth: specifier: 1.4.19 - version: 1.4.19(1495ef1d827113360533150571904e77) + version: 1.4.19(aa46c218c2111ba42e6bbb995c1cdf9e) kysely: specifier: 'catalog:' version: 0.29.0 @@ -213,6 +215,9 @@ importers: '@zenstackhq/orm': specifier: workspace:* version: link:../orm + '@zenstackhq/plugin-policy': + specifier: workspace:* + version: link:../plugins/policy '@zenstackhq/schema': specifier: workspace:* version: link:../schema @@ -251,7 +256,7 @@ importers: version: 2.6.1 langium: specifier: 'catalog:' - version: 3.5.0 + version: 4.2.4 mixpanel: specifier: ^0.18.1 version: 0.18.1 @@ -532,7 +537,7 @@ importers: version: link:../../language langium: specifier: 'catalog:' - version: 3.5.0 + version: 4.2.4 mixpanel: specifier: ^0.18.0 version: 0.18.1 @@ -575,7 +580,7 @@ importers: version: link:../common-helpers langium: specifier: 'catalog:' - version: 3.5.0 + version: 4.2.4 pluralize: specifier: ^8.0.0 version: 8.0.0 @@ -586,6 +591,9 @@ importers: specifier: ^9.0.1 version: 9.0.1 devDependencies: + '@types/node': + specifier: 'catalog:' + version: 20.19.24 '@types/pluralize': specifier: ^0.0.33 version: 0.0.33 @@ -609,7 +617,7 @@ importers: version: 11.1.0 langium-cli: specifier: 'catalog:' - version: 3.5.0 + version: 4.2.1 tmp: specifier: 'catalog:' version: 0.2.5 @@ -699,8 +707,8 @@ importers: specifier: workspace:* version: link:../config/vitest-config tsx: - specifier: ^4.19.2 - version: 4.20.3 + specifier: 'catalog:' + version: 4.22.4 zod: specifier: ^4.1.0 version: 4.1.12 @@ -739,6 +747,46 @@ importers: specifier: workspace:* version: link:../../config/vitest-config + packages/plugins/soft-delete: + dependencies: + '@zenstackhq/common-helpers': + specifier: workspace:* + version: link:../../common-helpers + '@zenstackhq/orm': + specifier: workspace:* + version: link:../../orm + kysely: + specifier: 'catalog:' + version: 0.29.0 + ts-pattern: + specifier: 'catalog:' + version: 5.7.1 + devDependencies: + '@types/better-sqlite3': + specifier: 'catalog:' + version: 7.6.13 + '@types/node': + specifier: 'catalog:' + version: 20.19.24 + '@zenstackhq/eslint-config': + specifier: workspace:* + version: link:../../config/eslint-config + '@zenstackhq/testtools': + specifier: workspace:* + version: link:../../testtools + '@zenstackhq/tsdown-config': + specifier: workspace:* + version: link:../../config/tsdown-config + '@zenstackhq/typescript-config': + specifier: workspace:* + version: link:../../config/typescript-config + '@zenstackhq/vitest-config': + specifier: workspace:* + version: link:../../config/vitest-config + better-sqlite3: + specifier: 'catalog:' + version: 12.5.0 + packages/schema: dependencies: decimal.js: @@ -768,7 +816,7 @@ importers: version: link:../language langium: specifier: 'catalog:' - version: 3.5.0 + version: 4.2.4 ts-pattern: specifier: 'catalog:' version: 5.7.1 @@ -830,7 +878,7 @@ importers: version: 6.0.0(openapi-types@12.1.3) '@sveltejs/kit': specifier: 'catalog:' - version: 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + version: 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@types/body-parser': specifier: ^1.19.6 version: 1.19.6 @@ -881,7 +929,7 @@ importers: version: 16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) nuxt: specifier: 'catalog:' - version: 4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2) + version: 4.3.1(526b065509e6a4d54781a1030a9e863f) supertest: specifier: ^7.1.4 version: 7.1.4 @@ -1055,7 +1103,7 @@ importers: dependencies: '@tailwindcss/vite': specifier: ^4.1.18 - version: 4.1.18(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + version: 4.1.18(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@tanstack/vue-query': specifier: 'catalog:' version: 5.90.2(vue@3.5.22(typescript@6.0.3)) @@ -1079,7 +1127,7 @@ importers: version: 2.0.8 nuxt: specifier: 'catalog:' - version: 4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190) + version: 4.3.1(95266d6f061a460ce208abe1df07947b) tailwindcss: specifier: ^4.1.18 version: 4.1.18 @@ -1163,16 +1211,16 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^7.0.0 - version: 7.0.0(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))) + version: 7.0.0(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))) '@sveltejs/kit': specifier: 'catalog:' - version: 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + version: 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@sveltejs/vite-plugin-svelte': specifier: ^6.2.1 - version: 6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + version: 6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@tailwindcss/vite': specifier: ^4.1.17 - version: 4.1.18(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + version: 4.1.18(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@types/better-sqlite3': specifier: 'catalog:' version: 7.6.13 @@ -1195,14 +1243,48 @@ importers: specifier: ^4.1.17 version: 4.1.18 tsx: - specifier: ^4.19.2 - version: 4.20.3 + specifier: 'catalog:' + version: 4.22.4 typescript: specifier: ^5.9.3 version: 5.9.3 vite: specifier: ^7.2.6 - version: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + version: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + + samples/taskforge: + dependencies: + '@zenstackhq/better-auth': + specifier: workspace:* + version: link:../../packages/auth-adapters/better-auth + '@zenstackhq/orm': + specifier: workspace:* + version: link:../../packages/orm + '@zenstackhq/schema': + specifier: workspace:* + version: link:../../packages/schema + better-auth: + specifier: ^1.4.21 + version: 1.6.15(261c4d1149703dc38709f1a99ea83b5c) + better-sqlite3: + specifier: 'catalog:' + version: 12.5.0 + commander: + specifier: ^14.0.2 + version: 14.0.3 + devDependencies: + '@better-auth/cli': + specifier: ^1.4.21 + version: 1.4.21(@better-fetch/fetch@1.1.21)(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-call@1.1.8(zod@4.3.6))(bun-types@1.3.3)(jose@6.2.3)(kysely@0.29.0)(magicast@0.5.1)(mysql2@3.16.1)(nanostores@1.3.0)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sql.js@1.13.0)(svelte@5.53.5)(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + '@types/better-sqlite3': + specifier: 'catalog:' + version: 7.6.13 + '@types/node': + specifier: 'catalog:' + version: 20.19.24 + '@zenstackhq/cli': + specifier: workspace:* + version: link:../../packages/cli tests/e2e: dependencies: @@ -1675,6 +1757,10 @@ packages: resolution: {integrity: sha512-kH1e+F8sPwcfEuhyNzFt3rdLo5SBTfw7a9m/hyMv1E7tk/yfsqOZRL0I5GR+Fkm0FJ7SoVA4LJREFV3S0Px8iA==} hasBin: true + '@better-auth/cli@1.4.21': + resolution: {integrity: sha512-bKEa8BupnZxNjLk9ZDntvgQGm5jogeE2wHdMbYifhet3GTyxgDi6pXoOK8+aqHYQGg1C3OALi9hVVWnrv7JJWQ==} + hasBin: true + '@better-auth/core@1.4.19': resolution: {integrity: sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA==} peerDependencies: @@ -1685,14 +1771,78 @@ packages: kysely: ^0.28.5 nanostores: ^1.0.1 + '@better-auth/drizzle-adapter@1.6.15': + resolution: {integrity: sha512-+ho2RozN6cZmCN+6XkZd/ec5iolVlefgInQ7znJ18qRPbgllHxdyu9tGrgKZyEsXSCyqAa6wi5Yb4hd3WZmTNQ==} + peerDependencies: + '@better-auth/core': 1.4.19 + '@better-auth/utils': 0.4.1 + drizzle-orm: ^0.45.2 + peerDependenciesMeta: + drizzle-orm: + optional: true + + '@better-auth/kysely-adapter@1.6.15': + resolution: {integrity: sha512-E29Sugm+DWRK4oQNBPrjPA4kBYiKy2bBtX1arIlkuyAB9A1BEb4Xn+8t7wlNIF5eFcxpkFZ3F80ceSpWDSYU0Q==} + peerDependencies: + '@better-auth/core': 1.4.19 + '@better-auth/utils': 0.4.1 + kysely: ^0.28.17 || ^0.29.0 + peerDependenciesMeta: + kysely: + optional: true + + '@better-auth/memory-adapter@1.6.15': + resolution: {integrity: sha512-BRE5Ft0Tn5thOPjCmyXHge7kOI/paLybaKDU+Hzg24DZMU6JvWooYWzvf52r1viDbxq5tz0G97iyaH4bouDyug==} + peerDependencies: + '@better-auth/core': 1.4.19 + '@better-auth/utils': 0.4.1 + + '@better-auth/mongo-adapter@1.6.15': + resolution: {integrity: sha512-r0X9AtFhwDeOU1KMP9kZ5NeV0O3TSFzn8+uidaH+aicy2e1dNUsd9nPYrozEloSxdA2ZOnv5gD4jzH7HPeKkUQ==} + peerDependencies: + '@better-auth/core': 1.4.19 + '@better-auth/utils': 0.4.1 + mongodb: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + mongodb: + optional: true + + '@better-auth/prisma-adapter@1.6.15': + resolution: {integrity: sha512-pUOlIUnpMu2l8C2ytgu46+OoOr3LZ+aLpJMFRGIPdnWl1BF2co2YaoZPT3+ZBu1Lzm0FrBN1ZRCAzZbkFoeIAQ==} + peerDependencies: + '@better-auth/core': 1.4.19 + '@better-auth/utils': 0.4.1 + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + '@prisma/client': + optional: true + prisma: + optional: true + '@better-auth/telemetry@1.4.19': resolution: {integrity: sha512-ApGNS7olCTtDpKF8Ow3Z+jvFAirOj7c4RyFUpu8axklh3mH57ndpfUAUjhgA8UVoaaH/mnm/Tl884BlqiewLyw==} peerDependencies: '@better-auth/core': 1.4.19 + '@better-auth/telemetry@1.4.21': + resolution: {integrity: sha512-LX+FGMZnhR2KQZ0idHH1+UwlXvkOl6P8w3Gne4TtjvUCt3QjG9FKIuP9JD3MAmEEkwGt0SoAPHPJEGTjUl3ydg==} + peerDependencies: + '@better-auth/core': 1.4.19 + + '@better-auth/telemetry@1.6.15': + resolution: {integrity: sha512-eoFlUPVrVXLML9saHD11OSKKSRCAYETgRgBW4vQF3Y6pCgmThKD9oehYk3kkb/FsRgfqmsocmvAFqsiyjPIygg==} + peerDependencies: + '@better-auth/core': 1.4.19 + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.1.21 + '@better-auth/utils@0.3.0': resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==} + '@better-auth/utils@0.4.1': + resolution: {integrity: sha512-SZBPRPF3z0nBvE5ygOkxae35wnnXPRShmqFo78S+qslLeFoPu/pMgnXAuNKFMMybac3tiLaVg1e3MQW5MC+1iA==} + '@better-fetch/fetch@1.1.21': resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} @@ -1717,29 +1867,29 @@ packages: '@chevrotain/cst-dts-gen@10.5.0': resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} - '@chevrotain/cst-dts-gen@11.0.3': - resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} + '@chevrotain/cst-dts-gen@12.0.0': + resolution: {integrity: sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==} '@chevrotain/gast@10.5.0': resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} - '@chevrotain/gast@11.0.3': - resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} + '@chevrotain/gast@12.0.0': + resolution: {integrity: sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==} - '@chevrotain/regexp-to-ast@11.0.3': - resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} + '@chevrotain/regexp-to-ast@12.0.0': + resolution: {integrity: sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==} '@chevrotain/types@10.5.0': resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} - '@chevrotain/types@11.0.3': - resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} + '@chevrotain/types@12.0.0': + resolution: {integrity: sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==} '@chevrotain/utils@10.5.0': resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} - '@chevrotain/utils@11.0.3': - resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} + '@chevrotain/utils@12.0.0': + resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==} '@clack/core@0.5.0': resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} @@ -1828,12 +1978,6 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.25.5': - resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} @@ -1846,11 +1990,11 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.5': - resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} engines: {node: '>=18'} - cpu: [arm64] - os: [android] + cpu: [ppc64] + os: [aix] '@esbuild/android-arm64@0.27.2': resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} @@ -1864,10 +2008,10 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.5': - resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [android] '@esbuild/android-arm@0.27.2': @@ -1882,10 +2026,10 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.5': - resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm] os: [android] '@esbuild/android-x64@0.27.2': @@ -1900,11 +2044,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.5': - resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] '@esbuild/darwin-arm64@0.27.2': resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} @@ -1918,10 +2062,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.5': - resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.27.2': @@ -1936,11 +2080,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.5': - resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] '@esbuild/freebsd-arm64@0.27.2': resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} @@ -1954,10 +2098,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.5': - resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.27.2': @@ -1972,11 +2116,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.5': - resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] '@esbuild/linux-arm64@0.27.2': resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} @@ -1990,10 +2134,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.5': - resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.27.2': @@ -2008,10 +2152,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.5': - resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.27.2': @@ -2026,10 +2170,10 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.5': - resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} engines: {node: '>=18'} - cpu: [loong64] + cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.27.2': @@ -2044,10 +2188,10 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.5': - resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} engines: {node: '>=18'} - cpu: [mips64el] + cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.27.2': @@ -2062,10 +2206,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.5': - resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} engines: {node: '>=18'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.27.2': @@ -2080,10 +2224,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.5': - resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} engines: {node: '>=18'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.27.2': @@ -2098,10 +2242,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.5': - resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} engines: {node: '>=18'} - cpu: [s390x] + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.27.2': @@ -2116,10 +2260,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.5': - resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} engines: {node: '>=18'} - cpu: [x64] + cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.27.2': @@ -2134,11 +2278,11 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.5': - resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] + cpu: [x64] + os: [linux] '@esbuild/netbsd-arm64@0.27.2': resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} @@ -2152,10 +2296,10 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.5': - resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.27.2': @@ -2170,11 +2314,11 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.5': - resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] + cpu: [x64] + os: [netbsd] '@esbuild/openbsd-arm64@0.27.2': resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} @@ -2188,10 +2332,10 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.5': - resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.27.2': @@ -2206,6 +2350,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.27.2': resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} engines: {node: '>=18'} @@ -2218,11 +2368,11 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.5': - resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} engines: {node: '>=18'} - cpu: [x64] - os: [sunos] + cpu: [arm64] + os: [openharmony] '@esbuild/sunos-x64@0.27.2': resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} @@ -2236,11 +2386,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.5': - resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} engines: {node: '>=18'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] '@esbuild/win32-arm64@0.27.2': resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} @@ -2254,10 +2404,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.5': - resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.27.2': @@ -2272,10 +2422,10 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.5': - resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} engines: {node: '>=18'} - cpu: [x64] + cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.27.2': @@ -2290,6 +2440,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.7.0': resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2637,6 +2793,10 @@ packages: resolution: {integrity: sha512-xHK3XHPUW8DTAobU+G0XT+/w+JLM7/8k1UFdB5xg/zTFPnFCobhftzw8wl4Lw2aq/Rvir5pxfZV5fEazmeCJ2g==} engines: {node: '>= 20.19.0'} + '@noble/ciphers@2.2.0': + resolution: {integrity: sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==} + engines: {node: '>= 20.19.0'} + '@noble/hashes@1.7.1': resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} engines: {node: ^14.21.3 || >=16} @@ -4772,6 +4932,130 @@ packages: vue: optional: true + better-auth@1.4.21: + resolution: {integrity: sha512-qdrIZS7xnGF2HPBV5wYNPWTkPojhauOOjz1+MhLvwFy+zXpgLofQmWsI5I9DY+ef845NKt93XcgpyAc4RPPT9A==} + peerDependencies: + '@lynx-js/react': '*' + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + '@sveltejs/kit': ^2.0.0 + '@tanstack/react-start': ^1.0.0 + '@tanstack/solid-start': ^1.0.0 + better-sqlite3: ^12.0.0 + drizzle-kit: '>=0.31.4' + drizzle-orm: '>=0.41.0' + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@lynx-js/react': + optional: true + '@prisma/client': + optional: true + '@sveltejs/kit': + optional: true + '@tanstack/react-start': + optional: true + '@tanstack/solid-start': + optional: true + better-sqlite3: + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + + better-auth@1.6.15: + resolution: {integrity: sha512-0nuQuEru3ZrLF+9xFUuN3llAmR+6gHLtLunoXaZxB9lXGjSmfBcc6SZUgYq4DfzugPnLvdnzYazsyprZFSFC4Q==} + peerDependencies: + '@lynx-js/react': '*' + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + '@sveltejs/kit': ^2.0.0 + '@tanstack/react-start': ^1.0.0 + '@tanstack/solid-start': ^1.0.0 + better-sqlite3: ^12.0.0 + drizzle-kit: '>=0.31.4' + drizzle-orm: ^0.45.2 + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@lynx-js/react': + optional: true + '@prisma/client': + optional: true + '@sveltejs/kit': + optional: true + '@tanstack/react-start': + optional: true + '@tanstack/solid-start': + optional: true + better-sqlite3: + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + better-call@1.1.8: resolution: {integrity: sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==} peerDependencies: @@ -4780,6 +5064,14 @@ packages: zod: optional: true + better-call@1.3.5: + resolution: {integrity: sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA==} + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + better-sqlite3@12.5.0: resolution: {integrity: sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==} engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} @@ -4915,24 +5207,21 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chevrotain-allstar@0.3.1: - resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} + chevrotain-allstar@0.4.3: + resolution: {integrity: sha512-2X4mkroolSMKqW+H22pyPMUVDqYZzPhephTmg/NODKb1IGYPHfxfhcW0EjS7wcPJNbze2i4vBWT7zT5FKF2lrQ==} peerDependencies: - chevrotain: ^11.0.0 + chevrotain: ^12.0.0 chevrotain@10.5.0: resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} - chevrotain@11.0.3: - resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} + chevrotain@12.0.0: + resolution: {integrity: sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==} + engines: {node: '>=22.0.0'} chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} @@ -5010,10 +5299,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@11.0.0: - resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} - engines: {node: '>=16'} - commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -5022,6 +5307,10 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -5610,11 +5899,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.25.5: - resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -5625,6 +5909,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -5952,8 +6241,8 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.1.1: - resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + fs-extra@11.3.5: + resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} engines: {node: '>=14.14'} fsevents@2.3.3: @@ -6507,6 +6796,9 @@ packages: jose@6.1.2: resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -6575,8 +6867,8 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jsonschema@1.4.1: - resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} + jsonschema@1.5.0: + resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -6614,17 +6906,17 @@ packages: resolution: {integrity: sha512-LrQfPUeTW7MXbMvT62moEMnpMTuj9TO3lqjCeLKjM975PJ4Alrl/43f2tlDX7xOsNptKgH4LSNGwIbXwEkLg4g==} engines: {node: '>=22.0.0'} - langium-cli@3.5.0: - resolution: {integrity: sha512-TPIzIiMAQwTPPphtHGSrFXo4t0orx3aRh0syg9jnOihvBkBDvsQdJP9fBo9hp5Qaosklpc2CfbH0wh/dkgZcJA==} - engines: {node: '>=18.0.0'} + langium-cli@4.2.1: + resolution: {integrity: sha512-npo1fSoP/wUf4cxUcKUYph48+jHFPEjeLQJ3pQ1UnA4QRuz8JpOxWsLHB/fdmViZSnI0mb98f06Wo7PKecqoUw==} + engines: {node: '>=20.10.0', npm: '>=10.2.3'} hasBin: true - langium-railroad@3.5.0: - resolution: {integrity: sha512-80Enc6bOR6oHZD18IQlVTdfCh07rbrM5SOsPUPc1kyh7n3zQbmLs5P2E9hV4SoWlkhOjGKL1I9Z6uORpgy+jTQ==} + langium-railroad@4.2.0: + resolution: {integrity: sha512-LYR22GV14iz0GTUtR91pZY6yNImYRk1HbAuHBP8k8GESMcSkCJh+iewJooH9k8H8dJOteXvvI7aFkz6rfU9oLQ==} - langium@3.5.0: - resolution: {integrity: sha512-tnqVzWOkUcoiY0bWlyE8diFrZjmGBCF7MesC1bjUaZM+YGQSfdPC+KkhmHM0DWFG+uLcPxidKaPP1SYGtg3J0Q==} - engines: {node: '>=18.0.0'} + langium@4.2.4: + resolution: {integrity: sha512-J0M9BkTOZ9Izee2YL0eXkUKFdWhrmTYYVLgiZTz6Z3KvK03ooM+vjVrfphKcdGVPWmM2cFYtDnNyIsIIZPrHNA==} + engines: {node: '>=20.10.0', npm: '>=10.2.3'} language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -6752,8 +7044,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.23: - resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} @@ -6770,8 +7062,8 @@ packages: lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -7006,6 +7298,10 @@ packages: resolution: {integrity: sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw==} engines: {node: ^20.0.0 || >=22.0.0} + nanostores@1.3.0: + resolution: {integrity: sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==} + engines: {node: ^20.0.0 || >=22.0.0} + nanotar@0.2.0: resolution: {integrity: sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==} @@ -7342,9 +7638,6 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - perfect-debounce@2.0.0: - resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} - perfect-debounce@2.1.0: resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} @@ -8598,8 +8891,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.20.3: - resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + tsx@4.22.4: + resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} engines: {node: '>=18.0.0'} hasBin: true @@ -9120,9 +9413,6 @@ packages: resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} hasBin: true - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} @@ -9771,19 +10061,91 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/cli@1.4.19(@better-fetch/fetch@1.1.21)(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-call@1.1.8(zod@4.3.6))(bun-types@1.3.3)(jose@6.1.2)(kysely@0.29.0)(magicast@0.5.1)(mysql2@3.16.1)(nanostores@1.0.1)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sql.js@1.13.0)(svelte@5.53.5)(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@better-auth/cli@1.4.19(@better-fetch/fetch@1.1.21)(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-call@1.1.8(zod@4.3.6))(bun-types@1.3.3)(jose@6.2.3)(kysely@0.29.0)(magicast@0.5.1)(mysql2@3.16.1)(nanostores@1.3.0)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sql.js@1.13.0)(svelte@5.53.5)(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.0 + '@babel/preset-react': 7.28.5(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/telemetry': 1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0)) + '@better-auth/utils': 0.3.0 + '@clack/prompts': 0.11.0 + '@mrleebo/prisma-ast': 0.13.1 + '@prisma/client': 5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) + '@types/pg': 8.16.0 + better-auth: 1.4.19(aa46c218c2111ba42e6bbb995c1cdf9e) + better-sqlite3: 12.5.0 + c12: 3.3.3(magicast@0.5.1) + chalk: 5.6.2 + commander: 12.1.0 + dotenv: 17.2.3 + drizzle-orm: 0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0) + open: 10.2.0 + pg: 8.16.3 + prettier: 3.8.1 + prompts: 2.4.2 + semver: 7.7.4 + yocto-spinner: 0.2.3 + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/client-rds-data' + - '@better-fetch/fetch' + - '@cloudflare/workers-types' + - '@electric-sql/pglite' + - '@libsql/client' + - '@libsql/client-wasm' + - '@lynx-js/react' + - '@neondatabase/serverless' + - '@op-engineering/op-sqlite' + - '@opentelemetry/api' + - '@planetscale/database' + - '@sveltejs/kit' + - '@tanstack/react-start' + - '@tanstack/solid-start' + - '@tidbcloud/serverless' + - '@types/better-sqlite3' + - '@types/sql.js' + - '@vercel/postgres' + - '@xata.io/client' + - better-call + - bun-types + - drizzle-kit + - expo-sqlite + - gel + - jose + - knex + - kysely + - magicast + - mongodb + - mysql2 + - nanostores + - next + - pg-native + - postgres + - prisma + - react + - react-dom + - solid-js + - sql.js + - sqlite3 + - supports-color + - svelte + - vitest + - vue + + '@better-auth/cli@1.4.21(@better-fetch/fetch@1.1.21)(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(@types/better-sqlite3@7.6.13)(@types/sql.js@1.4.9)(better-call@1.1.8(zod@4.3.6))(bun-types@1.3.3)(jose@6.2.3)(kysely@0.29.0)(magicast@0.5.1)(mysql2@3.16.1)(nanostores@1.3.0)(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sql.js@1.13.0)(svelte@5.53.5)(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: '@babel/core': 7.29.0 '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1) - '@better-auth/telemetry': 1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1)) + '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0)) '@better-auth/utils': 0.3.0 '@clack/prompts': 0.11.0 '@mrleebo/prisma-ast': 0.13.1 '@prisma/client': 5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) '@types/pg': 8.16.0 - better-auth: 1.4.19(1495ef1d827113360533150571904e77) + better-auth: 1.4.21(261c4d1149703dc38709f1a99ea83b5c) better-sqlite3: 12.5.0 c12: 3.3.3(magicast@0.5.1) chalk: 5.6.2 @@ -9854,25 +10216,95 @@ snapshots: nanostores: 1.0.1 zod: 4.3.6 - '@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1)': + '@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.28.16)(nanostores@1.3.0)': dependencies: '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.21 '@standard-schema/spec': 1.0.0 better-call: 1.1.8(zod@4.3.6) - jose: 6.1.2 + jose: 6.2.3 + kysely: 0.28.16 + nanostores: 1.3.0 + zod: 4.3.6 + + '@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0)': + dependencies: + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + '@standard-schema/spec': 1.0.0 + better-call: 1.1.8(zod@4.3.6) + jose: 6.2.3 kysely: 0.29.0 - nanostores: 1.0.1 + nanostores: 1.3.0 + zod: 4.3.6 + + '@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0)': + dependencies: + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.1.21 + '@standard-schema/spec': 1.0.0 + better-call: 1.1.8(zod@4.3.6) + jose: 6.2.3 + kysely: 0.29.0 + nanostores: 1.3.0 zod: 4.3.6 - '@better-auth/telemetry@1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1))': + '@better-auth/drizzle-adapter@1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 + optionalDependencies: + drizzle-orm: 0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0) + + '@better-auth/kysely-adapter@1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(kysely@0.29.0)': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 + optionalDependencies: + kysely: 0.29.0 + + '@better-auth/memory-adapter@1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 + + '@better-auth/mongo-adapter@1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 + + '@better-auth/prisma-adapter@1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 + optionalDependencies: + '@prisma/client': 5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) + prisma: 6.19.0(magicast@0.5.1)(typescript@5.9.3) + + '@better-auth/telemetry@1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + + '@better-auth/telemetry@1.4.21(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))': dependencies: - '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1) + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.21 + '@better-auth/telemetry@1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)': + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.1.21 + '@better-auth/utils@0.3.0': {} + '@better-auth/utils@0.4.1': + dependencies: + '@noble/hashes': 2.0.1 + '@better-fetch/fetch@1.1.21': {} '@bomb.sh/tab@0.0.12(cac@6.7.14)(citty@0.2.1)': @@ -9886,33 +10318,31 @@ snapshots: dependencies: '@chevrotain/gast': 10.5.0 '@chevrotain/types': 10.5.0 - lodash: 4.17.23 + lodash: 4.18.1 - '@chevrotain/cst-dts-gen@11.0.3': + '@chevrotain/cst-dts-gen@12.0.0': dependencies: - '@chevrotain/gast': 11.0.3 - '@chevrotain/types': 11.0.3 - lodash-es: 4.17.23 + '@chevrotain/gast': 12.0.0 + '@chevrotain/types': 12.0.0 '@chevrotain/gast@10.5.0': dependencies: '@chevrotain/types': 10.5.0 - lodash: 4.17.23 + lodash: 4.18.1 - '@chevrotain/gast@11.0.3': + '@chevrotain/gast@12.0.0': dependencies: - '@chevrotain/types': 11.0.3 - lodash-es: 4.17.23 + '@chevrotain/types': 12.0.0 - '@chevrotain/regexp-to-ast@11.0.3': {} + '@chevrotain/regexp-to-ast@12.0.0': {} '@chevrotain/types@10.5.0': {} - '@chevrotain/types@11.0.3': {} + '@chevrotain/types@12.0.0': {} '@chevrotain/utils@10.5.0': {} - '@chevrotain/utils@11.0.3': {} + '@chevrotain/utils@12.0.0': {} '@clack/core@0.5.0': dependencies: @@ -10024,16 +10454,13 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.5': - optional: true - '@esbuild/aix-ppc64@0.27.2': optional: true '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/android-arm64@0.25.5': + '@esbuild/aix-ppc64@0.28.0': optional: true '@esbuild/android-arm64@0.27.2': @@ -10042,7 +10469,7 @@ snapshots: '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm@0.25.5': + '@esbuild/android-arm64@0.28.0': optional: true '@esbuild/android-arm@0.27.2': @@ -10051,7 +10478,7 @@ snapshots: '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/android-x64@0.25.5': + '@esbuild/android-arm@0.28.0': optional: true '@esbuild/android-x64@0.27.2': @@ -10060,7 +10487,7 @@ snapshots: '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.25.5': + '@esbuild/android-x64@0.28.0': optional: true '@esbuild/darwin-arm64@0.27.2': @@ -10069,7 +10496,7 @@ snapshots: '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/darwin-x64@0.25.5': + '@esbuild/darwin-arm64@0.28.0': optional: true '@esbuild/darwin-x64@0.27.2': @@ -10078,7 +10505,7 @@ snapshots: '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.25.5': + '@esbuild/darwin-x64@0.28.0': optional: true '@esbuild/freebsd-arm64@0.27.2': @@ -10087,7 +10514,7 @@ snapshots: '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.25.5': + '@esbuild/freebsd-arm64@0.28.0': optional: true '@esbuild/freebsd-x64@0.27.2': @@ -10096,7 +10523,7 @@ snapshots: '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/linux-arm64@0.25.5': + '@esbuild/freebsd-x64@0.28.0': optional: true '@esbuild/linux-arm64@0.27.2': @@ -10105,7 +10532,7 @@ snapshots: '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/linux-arm@0.25.5': + '@esbuild/linux-arm64@0.28.0': optional: true '@esbuild/linux-arm@0.27.2': @@ -10114,7 +10541,7 @@ snapshots: '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/linux-ia32@0.25.5': + '@esbuild/linux-arm@0.28.0': optional: true '@esbuild/linux-ia32@0.27.2': @@ -10123,7 +10550,7 @@ snapshots: '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/linux-loong64@0.25.5': + '@esbuild/linux-ia32@0.28.0': optional: true '@esbuild/linux-loong64@0.27.2': @@ -10132,7 +10559,7 @@ snapshots: '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/linux-mips64el@0.25.5': + '@esbuild/linux-loong64@0.28.0': optional: true '@esbuild/linux-mips64el@0.27.2': @@ -10141,7 +10568,7 @@ snapshots: '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/linux-ppc64@0.25.5': + '@esbuild/linux-mips64el@0.28.0': optional: true '@esbuild/linux-ppc64@0.27.2': @@ -10150,7 +10577,7 @@ snapshots: '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/linux-riscv64@0.25.5': + '@esbuild/linux-ppc64@0.28.0': optional: true '@esbuild/linux-riscv64@0.27.2': @@ -10159,7 +10586,7 @@ snapshots: '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/linux-s390x@0.25.5': + '@esbuild/linux-riscv64@0.28.0': optional: true '@esbuild/linux-s390x@0.27.2': @@ -10168,7 +10595,7 @@ snapshots: '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/linux-x64@0.25.5': + '@esbuild/linux-s390x@0.28.0': optional: true '@esbuild/linux-x64@0.27.2': @@ -10177,7 +10604,7 @@ snapshots: '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.25.5': + '@esbuild/linux-x64@0.28.0': optional: true '@esbuild/netbsd-arm64@0.27.2': @@ -10186,7 +10613,7 @@ snapshots: '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/netbsd-x64@0.25.5': + '@esbuild/netbsd-arm64@0.28.0': optional: true '@esbuild/netbsd-x64@0.27.2': @@ -10195,7 +10622,7 @@ snapshots: '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.25.5': + '@esbuild/netbsd-x64@0.28.0': optional: true '@esbuild/openbsd-arm64@0.27.2': @@ -10204,7 +10631,7 @@ snapshots: '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.25.5': + '@esbuild/openbsd-arm64@0.28.0': optional: true '@esbuild/openbsd-x64@0.27.2': @@ -10213,13 +10640,16 @@ snapshots: '@esbuild/openbsd-x64@0.27.3': optional: true + '@esbuild/openbsd-x64@0.28.0': + optional: true + '@esbuild/openharmony-arm64@0.27.2': optional: true '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/sunos-x64@0.25.5': + '@esbuild/openharmony-arm64@0.28.0': optional: true '@esbuild/sunos-x64@0.27.2': @@ -10228,7 +10658,7 @@ snapshots: '@esbuild/sunos-x64@0.27.3': optional: true - '@esbuild/win32-arm64@0.25.5': + '@esbuild/sunos-x64@0.28.0': optional: true '@esbuild/win32-arm64@0.27.2': @@ -10237,7 +10667,7 @@ snapshots: '@esbuild/win32-arm64@0.27.3': optional: true - '@esbuild/win32-ia32@0.25.5': + '@esbuild/win32-arm64@0.28.0': optional: true '@esbuild/win32-ia32@0.27.2': @@ -10246,7 +10676,7 @@ snapshots: '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/win32-x64@0.25.5': + '@esbuild/win32-ia32@0.28.0': optional: true '@esbuild/win32-x64@0.27.2': @@ -10255,6 +10685,9 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true + '@esbuild/win32-x64@0.28.0': + optional: true + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@2.6.1))': dependencies: eslint: 9.29.0(jiti@2.6.1) @@ -10557,6 +10990,8 @@ snapshots: '@noble/ciphers@2.0.1': {} + '@noble/ciphers@2.2.0': {} + '@noble/hashes@1.7.1': {} '@noble/hashes@2.0.1': {} @@ -10615,11 +11050,11 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@3.1.1(magicast@0.5.1)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@nuxt/devtools-kit@3.1.1(magicast@0.5.1)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@nuxt/kit': 4.3.1(magicast@0.5.1) execa: 8.0.1 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: - magicast @@ -10634,12 +11069,12 @@ snapshots: prompts: 2.4.2 semver: 7.7.4 - '@nuxt/devtools@3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@nuxt/devtools@3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@nuxt/devtools-wizard': 3.1.1 '@nuxt/kit': 4.3.1(magicast@0.5.1) - '@vue/devtools-core': 8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + '@vue/devtools-core': 8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) '@vue/devtools-kit': 8.0.5 birpc: 2.9.0 consola: 3.4.2 @@ -10664,9 +11099,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 1.0.0 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-plugin-inspect: 11.3.3(@nuxt/kit@4.3.1(magicast@0.5.1))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) - vite-plugin-vue-tracer: 1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-plugin-inspect: 11.3.3(@nuxt/kit@4.3.1(magicast@0.5.1))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + vite-plugin-vue-tracer: 1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) which: 5.0.0 ws: 8.20.0 transitivePeerDependencies: @@ -10675,12 +11110,12 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': + '@nuxt/devtools@3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': dependencies: - '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@nuxt/devtools-wizard': 3.1.1 '@nuxt/kit': 4.3.1(magicast@0.5.1) - '@vue/devtools-core': 8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) + '@vue/devtools-core': 8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) '@vue/devtools-kit': 8.0.5 birpc: 2.9.0 consola: 3.4.2 @@ -10705,9 +11140,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 1.0.0 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-plugin-inspect: 11.3.3(@nuxt/kit@4.3.1(magicast@0.5.1))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) - vite-plugin-vue-tracer: 1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-plugin-inspect: 11.3.3(@nuxt/kit@4.3.1(magicast@0.5.1))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + vite-plugin-vue-tracer: 1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) which: 5.0.0 ws: 8.20.0 transitivePeerDependencies: @@ -10741,7 +11176,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/nitro-server@4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2))(rolldown@1.0.0-rc.15)(typescript@5.9.3)': + '@nuxt/nitro-server@4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(526b065509e6a4d54781a1030a9e863f))(rolldown@1.0.0-rc.15)(typescript@5.9.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.3.1(magicast@0.5.1) @@ -10759,7 +11194,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.13.1(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(mysql2@3.16.1)(rolldown@1.0.0-rc.15) - nuxt: 4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2) + nuxt: 4.3.1(526b065509e6a4d54781a1030a9e863f) ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.0 @@ -10806,7 +11241,7 @@ snapshots: - uploadthing - xml2js - '@nuxt/nitro-server@4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190))(rolldown@1.0.0-rc.15)(typescript@6.0.3)': + '@nuxt/nitro-server@4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(95266d6f061a460ce208abe1df07947b))(rolldown@1.0.0-rc.15)(typescript@6.0.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.3.1(magicast@0.5.1) @@ -10824,7 +11259,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.13.1(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(mysql2@3.16.1)(rolldown@1.0.0-rc.15) - nuxt: 4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190) + nuxt: 4.3.1(95266d6f061a460ce208abe1df07947b) ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.0 @@ -10888,12 +11323,12 @@ snapshots: rc9: 3.0.0 std-env: 3.10.0 - '@nuxt/vite-builder@4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.20.3)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))(yaml@2.8.2)': + '@nuxt/vite-builder@4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(526b065509e6a4d54781a1030a9e863f))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.22.4)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))(yaml@2.8.2)': dependencies: '@nuxt/kit': 4.3.1(magicast@0.5.1) '@rollup/plugin-replace': 6.0.3(rollup@4.59.0) - '@vitejs/plugin-vue': 6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': 5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': 5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) autoprefixer: 10.4.27(postcss@8.5.6) consola: 3.4.2 cssnano: 7.1.2(postcss@8.5.6) @@ -10907,7 +11342,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.0 mocked-exports: 0.1.1 - nuxt: 4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2) + nuxt: 4.3.1(526b065509e6a4d54781a1030a9e863f) pathe: 2.0.3 pkg-types: 2.3.0 postcss: 8.5.6 @@ -10916,9 +11351,9 @@ snapshots: std-env: 3.10.0 ufo: 1.6.3 unenv: 2.0.0-rc.24 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-node: 5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-plugin-checker: 0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-node: 5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-plugin-checker: 0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)) vue: 3.5.29(typescript@5.9.3) vue-bundle-renderer: 2.2.0 optionalDependencies: @@ -10948,12 +11383,12 @@ snapshots: - vue-tsc - yaml - '@nuxt/vite-builder@4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.20.3)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.29(typescript@6.0.3))(yaml@2.8.2)': + '@nuxt/vite-builder@4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(95266d6f061a460ce208abe1df07947b))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.22.4)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.29(typescript@6.0.3))(yaml@2.8.2)': dependencies: '@nuxt/kit': 4.3.1(magicast@0.5.1) '@rollup/plugin-replace': 6.0.3(rollup@4.59.0) - '@vitejs/plugin-vue': 6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) - '@vitejs/plugin-vue-jsx': 5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) + '@vitejs/plugin-vue': 6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) + '@vitejs/plugin-vue-jsx': 5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) autoprefixer: 10.4.27(postcss@8.5.6) consola: 3.4.2 cssnano: 7.1.2(postcss@8.5.6) @@ -10967,7 +11402,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.0 mocked-exports: 0.1.1 - nuxt: 4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190) + nuxt: 4.3.1(95266d6f061a460ce208abe1df07947b) pathe: 2.0.3 pkg-types: 2.3.0 postcss: 8.5.6 @@ -10976,9 +11411,9 @@ snapshots: std-env: 3.10.0 ufo: 1.6.3 unenv: 2.0.0-rc.24 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-node: 5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-plugin-checker: 0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@6.0.3)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-node: 5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-plugin-checker: 0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@6.0.3)) vue: 3.5.29(typescript@6.0.3) vue-bundle-renderer: 2.2.0 optionalDependencies: @@ -11581,15 +12016,35 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))': + '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))': + dependencies: + '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + + '@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: - '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.9(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 1.1.1 + devalue: 5.6.3 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.0.1 + sirv: 3.0.2 + svelte: 5.53.5 + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + optionalDependencies: + typescript: 5.9.3 - '@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 1.1.1 @@ -11601,15 +12056,16 @@ snapshots: set-cookie-parser: 3.0.1 sirv: 3.0.2 svelte: 5.53.5 - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) optionalDependencies: typescript: 5.9.3 + optional: true - '@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 1.1.1 @@ -11621,7 +12077,7 @@ snapshots: set-cookie-parser: 3.0.1 sirv: 3.0.2 svelte: 5.53.5 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) optionalDependencies: typescript: 5.9.3 @@ -11636,45 +12092,68 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + debug: 4.4.3 + svelte: 5.53.5 + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + debug: 4.4.3 + svelte: 5.53.5 + vite: 7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) debug: 4.4.3 svelte: 5.53.5 - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) debug: 4.4.3 + deepmerge: 4.3.1 + magic-string: 0.30.21 svelte: 5.53.5 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vitefu: 1.1.1(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) debug: 4.4.3 deepmerge: 4.3.1 magic-string: 0.30.21 svelte: 5.53.5 - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vitefu: 1.1.1(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + vite: 7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vitefu: 1.1.1(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) transitivePeerDependencies: - supports-color + optional: true - '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) debug: 4.4.3 deepmerge: 4.3.1 magic-string: 0.30.21 svelte: 5.53.5 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vitefu: 1.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vitefu: 1.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -11812,19 +12291,19 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.1.16 - '@tailwindcss/vite@4.1.18(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) - '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) '@tanstack/match-sorter-utils@8.19.4': dependencies: @@ -11892,7 +12371,7 @@ snapshots: '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 20.19.24 + '@types/node': 25.5.2 '@types/body-parser@1.19.6': dependencies: @@ -11913,7 +12392,7 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 20.19.24 + '@types/node': 25.5.2 '@types/deep-eql@4.0.2': {} @@ -11968,7 +12447,7 @@ snapshots: '@types/pg@8.16.0': dependencies: - '@types/node': 20.19.24 + '@types/node': 25.5.2 pg-protocol: 1.10.3 pg-types: 2.2.0 @@ -12311,43 +12790,43 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) '@rolldown/pluginutils': 1.0.0-rc.5 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0) - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@5.9.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': + '@vitejs/plugin-vue-jsx@5.1.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) '@rolldown/pluginutils': 1.0.0-rc.5 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0) - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@6.0.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': + '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@6.0.3) - '@vitest/coverage-v8@4.0.16(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0))': + '@vitest/coverage-v8@4.0.16(vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.16 @@ -12360,7 +12839,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0) + vitest: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -12373,21 +12852,30 @@ snapshots: chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.14(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0))': + '@vitest/mocker@4.0.14(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 4.0.14 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0) + + '@vitest/mocker@4.0.14(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.14 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + optional: true - '@vitest/mocker@4.0.14(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))': + '@vitest/mocker@4.0.14(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.14 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) optional: true '@vitest/pretty-format@4.0.14': @@ -12574,26 +13062,26 @@ snapshots: '@vue/devtools-api@6.6.4': {} - '@vue/devtools-core@8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@vue/devtools-core@8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 8.0.5 '@vue/devtools-shared': 8.0.5 mitt: 3.0.1 nanoid: 5.1.6 pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + vite-hot-client: 2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) vue: 3.5.29(typescript@5.9.3) transitivePeerDependencies: - vite - '@vue/devtools-core@8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': + '@vue/devtools-core@8.0.5(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3))': dependencies: '@vue/devtools-kit': 8.0.5 '@vue/devtools-shared': 8.0.5 mitt: 3.0.1 nanoid: 5.1.6 pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + vite-hot-client: 2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) vue: 3.5.29(typescript@6.0.3) transitivePeerDependencies: - vite @@ -12766,7 +13254,7 @@ snapshots: graceful-fs: 4.2.11 is-stream: 2.0.1 lazystream: 1.0.1 - lodash: 4.17.23 + lodash: 4.18.1 normalize-path: 3.0.0 readable-stream: 4.7.0 @@ -12934,10 +13422,10 @@ snapshots: baseline-browser-mapping@2.9.11: {} - better-auth@1.4.19(1495ef1d827113360533150571904e77): + better-auth@1.4.19(aa46c218c2111ba42e6bbb995c1cdf9e): dependencies: '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.28.16)(nanostores@1.0.1) - '@better-auth/telemetry': 1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.1.2)(kysely@0.29.0)(nanostores@1.0.1)) + '@better-auth/telemetry': 1.4.19(@better-auth/core@1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0)) '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.21 '@noble/ciphers': 2.0.1 @@ -12950,7 +13438,70 @@ snapshots: zod: 4.3.6 optionalDependencies: '@prisma/client': 5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) - '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + better-sqlite3: 12.5.0 + drizzle-orm: 0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0) + mysql2: 3.16.1 + next: 16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + pg: 8.16.3 + prisma: 6.19.0(magicast@0.5.1)(typescript@5.9.3) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + svelte: 5.53.5 + vitest: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vue: 3.5.29(typescript@5.9.3) + + better-auth@1.4.21(261c4d1149703dc38709f1a99ea83b5c): + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.28.16)(nanostores@1.3.0) + '@better-auth/telemetry': 1.4.21(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0)) + '@better-auth/utils': 0.3.0 + '@better-fetch/fetch': 1.1.21 + '@noble/ciphers': 2.2.0 + '@noble/hashes': 2.0.1 + better-call: 1.1.8(zod@4.3.6) + defu: 6.1.7 + jose: 6.2.3 + kysely: 0.28.16 + nanostores: 1.3.0 + zod: 4.3.6 + optionalDependencies: + '@prisma/client': 5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) + '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) + better-sqlite3: 12.5.0 + drizzle-orm: 0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0) + mysql2: 3.16.1 + next: 16.1.6(@babel/core@7.29.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + pg: 8.16.3 + prisma: 6.19.0(magicast@0.5.1)(typescript@5.9.3) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + svelte: 5.53.5 + vitest: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vue: 3.5.29(typescript@5.9.3) + + better-auth@1.6.15(261c4d1149703dc38709f1a99ea83b5c): + dependencies: + '@better-auth/core': 1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0) + '@better-auth/drizzle-adapter': 1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0)) + '@better-auth/kysely-adapter': 1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(kysely@0.29.0) + '@better-auth/memory-adapter': 1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1) + '@better-auth/mongo-adapter': 1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1) + '@better-auth/prisma-adapter': 1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) + '@better-auth/telemetry': 1.6.15(@better-auth/core@1.4.19(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.3.6))(jose@6.2.3)(kysely@0.29.0)(nanostores@1.3.0))(@better-auth/utils@0.4.1)(@better-fetch/fetch@1.1.21) + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.1.21 + '@noble/ciphers': 2.2.0 + '@noble/hashes': 2.0.1 + better-call: 1.3.5(zod@4.3.6) + defu: 6.1.7 + jose: 6.2.3 + kysely: 0.29.0 + nanostores: 1.3.0 + zod: 4.3.6 + optionalDependencies: + '@prisma/client': 5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)) + '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.53.5)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) better-sqlite3: 12.5.0 drizzle-orm: 0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0) mysql2: 3.16.1 @@ -12960,7 +13511,7 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) svelte: 5.53.5 - vitest: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vitest: 4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@5.9.3) better-call@1.1.8(zod@4.3.6): @@ -12972,6 +13523,15 @@ snapshots: optionalDependencies: zod: 4.3.6 + better-call@1.3.5(zod@4.3.6): + dependencies: + '@better-auth/utils': 0.4.1 + '@better-fetch/fetch': 1.1.21 + rou3: 0.7.12 + set-cookie-parser: 3.0.1 + optionalDependencies: + zod: 4.3.6 + better-sqlite3@12.5.0: dependencies: bindings: 1.5.0 @@ -13073,7 +13633,7 @@ snapshots: dependencies: chokidar: 4.0.3 confbox: 0.2.2 - defu: 6.1.4 + defu: 6.1.7 dotenv: 16.6.1 exsolve: 1.0.7 giget: 2.0.0 @@ -13090,7 +13650,7 @@ snapshots: dependencies: chokidar: 4.0.3 confbox: 0.2.2 - defu: 6.1.4 + defu: 6.1.7 dotenv: 16.6.1 exsolve: 1.0.7 giget: 2.0.0 @@ -13107,15 +13667,15 @@ snapshots: c12@3.3.3(magicast@0.5.1): dependencies: chokidar: 5.0.0 - confbox: 0.2.2 - defu: 6.1.4 + confbox: 0.2.4 + defu: 6.1.7 dotenv: 17.2.3 exsolve: 1.0.8 giget: 2.0.0 jiti: 2.6.1 ohash: 2.0.11 pathe: 2.0.3 - perfect-debounce: 2.0.0 + perfect-debounce: 2.1.0 pkg-types: 2.3.0 rc9: 2.1.2 optionalDependencies: @@ -13168,14 +13728,12 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.3.0: {} - chalk@5.6.2: {} - chevrotain-allstar@0.3.1(chevrotain@11.0.3): + chevrotain-allstar@0.4.3(chevrotain@12.0.0): dependencies: - chevrotain: 11.0.3 - lodash-es: 4.17.23 + chevrotain: 12.0.0 + lodash-es: 4.18.1 chevrotain@10.5.0: dependencies: @@ -13183,17 +13741,16 @@ snapshots: '@chevrotain/gast': 10.5.0 '@chevrotain/types': 10.5.0 '@chevrotain/utils': 10.5.0 - lodash: 4.17.23 + lodash: 4.18.1 regexp-to-ast: 0.5.0 - chevrotain@11.0.3: + chevrotain@12.0.0: dependencies: - '@chevrotain/cst-dts-gen': 11.0.3 - '@chevrotain/gast': 11.0.3 - '@chevrotain/regexp-to-ast': 11.0.3 - '@chevrotain/types': 11.0.3 - '@chevrotain/utils': 11.0.3 - lodash-es: 4.17.23 + '@chevrotain/cst-dts-gen': 12.0.0 + '@chevrotain/gast': 12.0.0 + '@chevrotain/regexp-to-ast': 12.0.0 + '@chevrotain/types': 12.0.0 + '@chevrotain/utils': 12.0.0 chokidar@4.0.3: dependencies: @@ -13259,12 +13816,12 @@ snapshots: dependencies: delayed-stream: 1.0.0 - commander@11.0.0: {} - commander@11.1.0: {} commander@12.1.0: {} + commander@14.0.3: {} + commander@2.20.3: {} commander@8.3.0: {} @@ -13804,34 +14361,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.25.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.5 - '@esbuild/android-arm': 0.25.5 - '@esbuild/android-arm64': 0.25.5 - '@esbuild/android-x64': 0.25.5 - '@esbuild/darwin-arm64': 0.25.5 - '@esbuild/darwin-x64': 0.25.5 - '@esbuild/freebsd-arm64': 0.25.5 - '@esbuild/freebsd-x64': 0.25.5 - '@esbuild/linux-arm': 0.25.5 - '@esbuild/linux-arm64': 0.25.5 - '@esbuild/linux-ia32': 0.25.5 - '@esbuild/linux-loong64': 0.25.5 - '@esbuild/linux-mips64el': 0.25.5 - '@esbuild/linux-ppc64': 0.25.5 - '@esbuild/linux-riscv64': 0.25.5 - '@esbuild/linux-s390x': 0.25.5 - '@esbuild/linux-x64': 0.25.5 - '@esbuild/netbsd-arm64': 0.25.5 - '@esbuild/netbsd-x64': 0.25.5 - '@esbuild/openbsd-arm64': 0.25.5 - '@esbuild/openbsd-x64': 0.25.5 - '@esbuild/sunos-x64': 0.25.5 - '@esbuild/win32-arm64': 0.25.5 - '@esbuild/win32-ia32': 0.25.5 - '@esbuild/win32-x64': 0.25.5 - esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -13890,6 +14419,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + esbuild@0.28.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -14365,7 +14923,7 @@ snapshots: fs-constants@1.0.0: {} - fs-extra@11.1.1: + fs-extra@11.3.5: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 @@ -14446,7 +15004,7 @@ snapshots: dependencies: citty: 0.1.6 consola: 3.4.2 - defu: 6.1.4 + defu: 6.1.7 node-fetch-native: 1.6.7 nypm: 0.6.2 pathe: 2.0.3 @@ -14930,6 +15488,8 @@ snapshots: jose@6.1.2: {} + jose@6.2.3: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -15008,7 +15568,7 @@ snapshots: jsonpointer@5.0.1: {} - jsonschema@1.4.1: {} + jsonschema@1.5.0: {} jsx-ast-utils@3.3.5: dependencies: @@ -15038,28 +15598,32 @@ snapshots: kysely@0.29.0: {} - langium-cli@3.5.0: + langium-cli@4.2.1: dependencies: - chalk: 5.3.0 - commander: 11.0.0 - fs-extra: 11.1.1 - jsonschema: 1.4.1 - langium: 3.5.0 - langium-railroad: 3.5.0 - lodash: 4.17.23 + chalk: 5.6.2 + commander: 14.0.3 + fs-extra: 11.3.5 + jsonschema: 1.5.0 + langium: 4.2.4 + langium-railroad: 4.2.0 + lodash: 4.18.1 - langium-railroad@3.5.0: + langium-railroad@4.2.0: dependencies: - langium: 3.5.0 + langium: 4.2.4 railroad-diagrams: 1.0.0 - langium@3.5.0: + langium@4.2.4: dependencies: - chevrotain: 11.0.3 - chevrotain-allstar: 0.3.1(chevrotain@11.0.3) + '@chevrotain/regexp-to-ast': 12.0.0 + chevrotain: 12.0.0 + chevrotain-allstar: 0.4.3(chevrotain@12.0.0) + vscode-jsonrpc: 8.2.0 vscode-languageserver: 9.0.1 + vscode-languageserver-protocol: 3.17.5 vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.0.8 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 language-subtag-registry@0.3.23: {} @@ -15150,7 +15714,7 @@ snapshots: clipboardy: 4.0.0 consola: 3.4.2 crossws: 0.3.5 - defu: 6.1.4 + defu: 6.1.7 get-port-please: 3.2.0 h3: 1.15.5 http-shutdown: 1.2.2 @@ -15182,7 +15746,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.23: {} + lodash-es@4.18.1: {} lodash.defaults@4.2.0: {} @@ -15194,7 +15758,7 @@ snapshots: lodash.uniq@4.5.0: {} - lodash@4.17.23: {} + lodash@4.18.1: {} log-symbols@4.1.0: dependencies: @@ -15396,6 +15960,8 @@ snapshots: nanostores@1.0.1: {} + nanostores@1.3.0: {} + nanotar@0.2.0: {} napi-build-utils@2.0.0: {} @@ -15478,10 +16044,10 @@ snapshots: croner: 9.1.0 crossws: 0.3.5 db0: 0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(mysql2@3.16.1) - defu: 6.1.4 + defu: 6.1.7 destr: 2.0.5 dot-prop: 10.1.0 - esbuild: 0.27.2 + esbuild: 0.27.3 escape-string-regexp: 5.0.0 etag: 1.8.1 exsolve: 1.0.8 @@ -15580,10 +16146,10 @@ snapshots: croner: 9.1.0 crossws: 0.3.5 db0: 0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(mysql2@3.16.1) - defu: 6.1.4 + defu: 6.1.7 destr: 2.0.5 dot-prop: 10.1.0 - esbuild: 0.27.2 + esbuild: 0.27.3 escape-string-regexp: 5.0.0 etag: 1.8.1 exsolve: 1.0.8 @@ -15724,16 +16290,16 @@ snapshots: dependencies: boolbase: 1.0.0 - nuxt@4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2): + nuxt@4.3.1(526b065509e6a4d54781a1030a9e863f): dependencies: '@dxup/nuxt': 0.3.2(magicast@0.5.1) '@nuxt/cli': 3.33.1(@nuxt/schema@4.3.1)(cac@6.7.14)(magicast@0.5.1) - '@nuxt/devtools': 3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + '@nuxt/devtools': 3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) '@nuxt/kit': 4.3.1(magicast@0.5.1) - '@nuxt/nitro-server': 4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2))(rolldown@1.0.0-rc.15)(typescript@5.9.3) + '@nuxt/nitro-server': 4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@5.9.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(526b065509e6a4d54781a1030a9e863f))(rolldown@1.0.0-rc.15)(typescript@5.9.3) '@nuxt/schema': 4.3.1 '@nuxt/telemetry': 2.7.0(@nuxt/kit@4.3.1(magicast@0.5.1)) - '@nuxt/vite-builder': 4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(20bb9c9cac3d4d3ad27d57e07c1eb4f2))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.20.3)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))(yaml@2.8.2) + '@nuxt/vite-builder': 4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(526b065509e6a4d54781a1030a9e863f))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.22.4)(typescript@5.9.3)(vue-tsc@3.2.5(typescript@5.9.3))(vue@3.5.29(typescript@5.9.3))(yaml@2.8.2) '@unhead/vue': 2.1.7(vue@3.5.29(typescript@5.9.3)) '@vue/shared': 3.5.29 c12: 3.3.3(magicast@0.5.1) @@ -15847,16 +16413,16 @@ snapshots: - xml2js - yaml - nuxt@4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190): + nuxt@4.3.1(95266d6f061a460ce208abe1df07947b): dependencies: '@dxup/nuxt': 0.3.2(magicast@0.5.1) '@nuxt/cli': 3.33.1(@nuxt/schema@4.3.1)(cac@6.7.14)(magicast@0.5.1) - '@nuxt/devtools': 3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) + '@nuxt/devtools': 3.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)) '@nuxt/kit': 4.3.1(magicast@0.5.1) - '@nuxt/nitro-server': 4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190))(rolldown@1.0.0-rc.15)(typescript@6.0.3) + '@nuxt/nitro-server': 4.3.1(better-sqlite3@12.5.0)(db0@0.3.4(better-sqlite3@12.5.0)(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(mysql2@3.16.1))(drizzle-orm@0.41.0(@prisma/client@5.22.0(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3)))(@types/better-sqlite3@7.6.13)(@types/pg@8.16.0)(@types/sql.js@1.4.9)(better-sqlite3@12.5.0)(bun-types@1.3.3)(kysely@0.29.0)(mysql2@3.16.1)(pg@8.16.3)(prisma@6.19.0(magicast@0.5.1)(typescript@6.0.3))(sql.js@1.13.0))(ioredis@5.9.3)(magicast@0.5.1)(mysql2@3.16.1)(nuxt@4.3.1(95266d6f061a460ce208abe1df07947b))(rolldown@1.0.0-rc.15)(typescript@6.0.3) '@nuxt/schema': 4.3.1 '@nuxt/telemetry': 2.7.0(@nuxt/kit@4.3.1(magicast@0.5.1)) - '@nuxt/vite-builder': 4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(f6c7dcf4eb9de64f6cb5e0db74766190))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.20.3)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.29(typescript@6.0.3))(yaml@2.8.2) + '@nuxt/vite-builder': 4.3.1(@types/node@25.5.2)(eslint@9.29.0(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.3.1(95266d6f061a460ce208abe1df07947b))(optionator@0.9.4)(rolldown@1.0.0-rc.15)(rollup@4.59.0)(terser@5.44.0)(tsx@4.22.4)(typescript@6.0.3)(vue-tsc@3.2.5(typescript@6.0.3))(vue@3.5.29(typescript@6.0.3))(yaml@2.8.2) '@unhead/vue': 2.1.7(vue@3.5.29(typescript@6.0.3)) '@vue/shared': 3.5.29 c12: 3.3.3(magicast@0.5.1) @@ -16249,8 +16815,6 @@ snapshots: perfect-debounce@1.0.0: {} - perfect-debounce@2.0.0: {} - perfect-debounce@2.1.0: {} pg-cloudflare@1.2.7: @@ -16670,12 +17234,12 @@ snapshots: rc9@2.1.2: dependencies: - defu: 6.1.4 + defu: 6.1.7 destr: 2.0.5 rc9@3.0.0: dependencies: - defu: 6.1.4 + defu: 6.1.7 destr: 2.0.5 rc@1.2.8: @@ -16989,7 +17553,7 @@ snapshots: serve-placeholder@2.0.2: dependencies: - defu: 6.1.4 + defu: 6.1.7 serve-static@2.2.0: dependencies: @@ -17598,10 +18162,9 @@ snapshots: tslib@2.8.1: {} - tsx@4.20.3: + tsx@4.22.4: dependencies: - esbuild: 0.25.5 - get-tsconfig: 4.10.1 + esbuild: 0.28.0 optionalDependencies: fsevents: 2.3.3 @@ -17963,23 +18526,23 @@ snapshots: vary@1.1.2: {} - vite-dev-rpc@1.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)): + vite-dev-rpc@1.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)): dependencies: birpc: 2.9.0 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-hot-client: 2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-hot-client: 2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) - vite-hot-client@2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)): + vite-hot-client@2.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)): dependencies: - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) - vite-node@5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2): + vite-node@5.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: cac: 6.7.14 es-module-lexer: 2.0.0 obug: 2.1.1 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -17993,7 +18556,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)): + vite-plugin-checker@0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@5.9.3)): dependencies: '@babel/code-frame': 7.28.6 chokidar: 4.0.3 @@ -18002,7 +18565,7 @@ snapshots: picomatch: 4.0.3 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.29.0(jiti@2.6.1) @@ -18010,7 +18573,7 @@ snapshots: typescript: 5.9.3 vue-tsc: 3.2.5(typescript@5.9.3) - vite-plugin-checker@0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@6.0.3)): + vite-plugin-checker@0.12.0(eslint@9.29.0(jiti@2.6.1))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue-tsc@3.2.5(typescript@6.0.3)): dependencies: '@babel/code-frame': 7.28.6 chokidar: 4.0.3 @@ -18019,7 +18582,7 @@ snapshots: picomatch: 4.0.3 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.29.0(jiti@2.6.1) @@ -18027,7 +18590,7 @@ snapshots: typescript: 6.0.3 vue-tsc: 3.2.5(typescript@6.0.3) - vite-plugin-inspect@11.3.3(@nuxt/kit@4.3.1(magicast@0.5.1))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)): + vite-plugin-inspect@11.3.3(@nuxt/kit@4.3.1(magicast@0.5.1))(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)): dependencies: ansis: 4.2.0 debug: 4.4.3 @@ -18037,34 +18600,34 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) - vite-dev-rpc: 1.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + vite-dev-rpc: 1.1.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) optionalDependencies: '@nuxt/kit': 4.3.1(magicast@0.5.1) transitivePeerDependencies: - supports-color - vite-plugin-vue-tracer@1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)): + vite-plugin-vue-tracer@1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@5.9.3) - vite-plugin-vue-tracer@1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)): + vite-plugin-vue-tracer@1.2.0(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2))(vue@3.5.29(typescript@6.0.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) vue: 3.5.29(typescript@6.0.3) - vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0): + vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -18078,10 +18641,10 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.0 - tsx: 4.20.3 + tsx: 4.22.4 yaml: 2.8.0 - vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2): + vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -18095,10 +18658,10 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.0 - tsx: 4.20.3 + tsx: 4.22.4 yaml: 2.8.2 - vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2): + vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -18112,11 +18675,29 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.0 - tsx: 4.20.3 + tsx: 4.22.4 + yaml: 2.8.2 + optional: true + + vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.24 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + terser: 5.44.0 + tsx: 4.22.4 yaml: 2.8.2 optional: true - vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2): + vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -18130,21 +18711,66 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.0 - tsx: 4.20.3 + tsx: 4.22.4 yaml: 2.8.2 - vitefu@1.1.1(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)): + vitefu@1.1.1(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)): + optionalDependencies: + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + + vitefu@1.1.1(vite@7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)): optionalDependencies: - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) + optional: true + + vitefu@1.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)): + optionalDependencies: + vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) - vitefu@1.1.1(vite@7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)): + vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0): + dependencies: + '@vitest/expect': 4.0.14 + '@vitest/mocker': 4.0.14(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0)) + '@vitest/pretty-format': 4.0.14 + '@vitest/runner': 4.0.14 + '@vitest/snapshot': 4.0.14 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.0) + why-is-node-running: 2.3.0 optionalDependencies: - vite: 7.3.1(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + '@edge-runtime/vm': 5.0.0 + '@types/node': 20.19.24 + happy-dom: 20.8.9 + jsdom: 27.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml - vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0): + vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@20.19.24)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/mocker': 4.0.14(vite@7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.14 '@vitest/runner': 4.0.14 '@vitest/snapshot': 4.0.14 @@ -18161,7 +18787,7 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.3.0(@types/node@20.19.24)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@edge-runtime/vm': 5.0.0 @@ -18180,11 +18806,12 @@ snapshots: - terser - tsx - yaml + optional: true - vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2): + vitest@4.0.14(@edge-runtime/vm@5.0.0)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2)) + '@vitest/mocker': 4.0.14(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.14 '@vitest/runner': 4.0.14 '@vitest/snapshot': 4.0.14 @@ -18201,7 +18828,7 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.3)(yaml@2.8.2) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.22.4)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@edge-runtime/vm': 5.0.0 @@ -18243,8 +18870,6 @@ snapshots: dependencies: vscode-languageserver-protocol: 3.17.5 - vscode-uri@3.0.8: {} - vscode-uri@3.1.0: {} vue-bundle-renderer@2.2.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 20e094cd8..f15d2e997 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,8 +16,8 @@ catalog: better-sqlite3: ^12.5.0 decimal.js: ^10.4.3 kysely: ~0.29.0 - langium: 3.5.0 - langium-cli: 3.5.0 + langium: 4.2.4 + langium-cli: 4.2.1 next: 16.1.6 nuxt: 4.3.1 '@sveltejs/kit': 2.53.2 @@ -34,3 +34,4 @@ catalog: vue: 3.5.22 zod: ^4.0.0 zod-validation-error: ^4.0.1 + tsx: ^4.22.0 diff --git a/samples/orm/package.json b/samples/orm/package.json index 0368c8902..f91960e54 100644 --- a/samples/orm/package.json +++ b/samples/orm/package.json @@ -1,6 +1,6 @@ { "name": "sample-orm", - "version": "3.7.2", + "version": "3.8.0", "description": "", "main": "index.js", "private": true, diff --git a/samples/sveltekit/package.json b/samples/sveltekit/package.json index 0d1c56779..8782715b8 100644 --- a/samples/sveltekit/package.json +++ b/samples/sveltekit/package.json @@ -34,7 +34,7 @@ "svelte": "catalog:", "svelte-check": "^4.3.4", "tailwindcss": "^4.1.17", - "tsx": "^4.19.2", + "tsx": "catalog:", "typescript": "^5.9.3", "vite": "^7.2.6" }, diff --git a/samples/taskforge/README.md b/samples/taskforge/README.md new file mode 100644 index 000000000..92fcffdbc --- /dev/null +++ b/samples/taskforge/README.md @@ -0,0 +1,100 @@ +# TaskForge + +A command-line client for a **team collaboration / project-tracking** platform +(think Linear/Jira), built to demonstrate a complete **[ZenStack v3](https://zenstack.dev)** +(ORM) + **[Better-Auth](https://better-auth.com)** stack on top of SQLite. + +- **34 models** across tenancy, projects/issues, collaboration, automation and billing +- Every relation flavour: 1-1, 1-many, implicit & explicit many-to-many, self-relations +- **Polymorphism** (`@@delegate`), **typed JSON** (`@json`), **computed fields** (`@computed`), + reusable field **mixins** (`with`), and **enums** +- Better-Auth wired to the ZenStack ORM via the official adapter (setup only — no HTTP flow) + +## Stack + +| Concern | Choice | +| ------------ | ------------------------------------------------------ | +| ORM / schema | ZenStack v3 (`@zenstackhq/orm`, ZModel schema) | +| Database | SQLite (via `better-sqlite3`) — zero external services | +| Auth | Better-Auth + `@zenstackhq/better-auth` adapter | +| CLI | `commander`, run with `tsx` | + +## Quick start + +```bash +pnpm install # native builds (better-sqlite3, prisma) are pre-approved in package.json +pnpm zen:generate # compile zenstack/schema.zmodel -> zenstack/schema.ts (the typed client) +pnpm db:push # create/sync the SQLite database (zenstack/taskforge.db) +pnpm seed # populate a demo workspace (users via Better-Auth, domain via the ORM) +pnpm cli orgs # try the CLI +``` + +## CLI commands + +```bash +pnpm cli orgs # organizations + member/team/project counts +pnpm cli projects [orgSlug] # projects + open-issue count (computed field) +pnpm cli issues [--status S] # issues + comment count (computed field) +pnpm cli issue # deep read: comments, polymorphic attachments, watchers +pnpm cli stats # groupBy + aggregate over issues and invoices +pnpm cli add-issue # auto-numbered create inside a $transaction +pnpm cli signup # register a user through Better-Auth +pnpm cli activity [-n N] # recent activity-log entries +``` + +(`taskforge ` also works once the package is linked/installed globally.) + +## Project layout + +``` +zenstack/ + schema.zmodel # the source of truth — 34 models, 13 enums, 5 custom types + schema.ts # generated typed schema (do not edit) — input.ts / models.ts also generated + taskforge.db # SQLite database (created by `zen db push`) +src/ + db.ts # ZenStackClient: SQLite dialect + computed-field implementations + auth.ts # Better-Auth config, bound to the ORM via zenstackAdapter + seed.ts # demo data + cli.ts # the commander CLI +``` + +## Schema highlights + +The domain is organized into five areas, all rooted at a multi-tenant `Organization`: + +- **Tenancy** — `Organization`, `Membership` (explicit M2M user↔org with role), `Invitation`, + `Team`, `TeamMembership` (explicit M2M). +- **Projects & planning** — `Project` (computed `openIssueCount`), `ProjectMember` (explicit M2M), + `Issue` (self-relation sub-issues; implicit M2M `labels` and `watchers`; computed `commentCount`; + typed-JSON `metadata`), `Label`, `Milestone`, `Sprint`. +- **Collaboration** — `Comment` (self-relation threads), `Reaction` (two optional FK targets), + `Attachment` **polymorphic base** with `FileAttachment` / `ImageAttachment` / `LinkAttachment` + concrete subtypes, `Document` (self-relation tree + implicit M2M collaborators, typed-JSON body). +- **Customization & automation** — `CustomField` + `CustomFieldValue` (explicit M2M), `TimeEntry`, + `Webhook`, `Integration`, `ApiToken`, `ActivityLog`, `Notification`. Where SQLite forbids scalar + lists, list-like config is stored as typed JSON. +- **Billing** — `BillingCustomer` (1-1 with org), `Plan`, `Subscription` (1-1 with org), `Invoice`. + +A `Timestamps` mixin (`type Timestamps { createdAt … updatedAt … }`) is applied to most models with +`model X with Timestamps { … }`. + +### Better-Auth integration + +`src/auth.ts` configures Better-Auth with the ZenStack adapter: + +```ts +export const auth = betterAuth({ + database: zenstackAdapter(db, { provider: 'sqlite' }), + emailAndPassword: { enabled: true }, +}); +``` + +The `User`, `Session`, `Account` and `Verification` models in `schema.zmodel` are Better-Auth's +core schema. Only the configuration is provided — no routes or sign-in UI are mounted — but +`auth.api.signUpEmail(...)` is fully functional (see `pnpm cli signup`). + +## Notes on SQLite + +The migration engine wraps Prisma Migrate. With the bundled Prisma 6 + ZenStack v3, SQLite supports +enums, `Json`/typed-JSON, and polymorphism. It does **not** support scalar lists (`String[]`), so +list data is modeled as relations or typed JSON. diff --git a/samples/taskforge/package.json b/samples/taskforge/package.json new file mode 100644 index 000000000..776eca99b --- /dev/null +++ b/samples/taskforge/package.json @@ -0,0 +1,31 @@ +{ + "name": "taskforge", + "version": "3.8.0", + "type": "module", + "private": true, + "description": "A CLI for a team collaboration / project-tracking platform, built on ZenStack v3 (ORM) and better-auth.", + "bin": { + "taskforge": "./src/cli.ts" + }, + "scripts": { + "zen:generate": "zen generate", + "db:push": "zen db push", + "build": "tsc --noEmit", + "cli": "tsx src/cli.ts", + "seed": "tsx src/seed.ts" + }, + "dependencies": { + "@zenstackhq/better-auth": "workspace:*", + "@zenstackhq/orm": "workspace:*", + "@zenstackhq/schema": "workspace:*", + "better-auth": "^1.4.21", + "better-sqlite3": "catalog:", + "commander": "^14.0.2" + }, + "devDependencies": { + "@better-auth/cli": "^1.4.21", + "@types/better-sqlite3": "catalog:", + "@types/node": "catalog:", + "@zenstackhq/cli": "workspace:*" + } +} diff --git a/samples/taskforge/src/auth.ts b/samples/taskforge/src/auth.ts new file mode 100644 index 000000000..de7f4f2e5 --- /dev/null +++ b/samples/taskforge/src/auth.ts @@ -0,0 +1,36 @@ +import { betterAuth, type BetterAuthOptions } from 'better-auth'; +import { zenstackAdapter } from '@zenstackhq/better-auth'; +import { db } from './db'; + +/** + * Better-Auth configuration. + * + * This wires Better-Auth's data layer to the ZenStack ORM client via + * `@zenstackhq/better-auth`'s adapter — the `User`, `Session`, `Account` and + * `Verification` models in `schema.zmodel` are Better-Auth's core schema. + * + * Only the *setup* is provided here, as requested: no HTTP routes, framework + * handlers, or sign-in/sign-up flow are mounted. `auth.api.*` is fully usable + * (see `taskforge auth:signup` in the CLI for a demonstration), but there is no + * server exposing it. + * + * The Better-Auth CLI can read this file to (re)generate the auth models into + * the ZModel schema: `pnpm auth:generate`. + */ +const options = { + appName: 'TaskForge', + // CLI usage never serves HTTP; a placeholder base URL silences the warning. + baseURL: process.env.BETTER_AUTH_URL ?? 'http://localhost:3000', + secret: process.env.BETTER_AUTH_SECRET ?? 'taskforge-dev-secret-0123456789abcdef', + database: zenstackAdapter(db, { + provider: 'sqlite', + }), + emailAndPassword: { + enabled: true, + }, + plugins: [], +} satisfies BetterAuthOptions; + +export const auth = betterAuth(options); + +export type Auth = typeof auth; diff --git a/samples/taskforge/src/cli.ts b/samples/taskforge/src/cli.ts new file mode 100755 index 000000000..dde6f59c0 --- /dev/null +++ b/samples/taskforge/src/cli.ts @@ -0,0 +1,264 @@ +#!/usr/bin/env -S npx tsx +/** + * TaskForge CLI. + * + * A small command-line client over the ZenStack v3 ORM (and Better-Auth for + * user creation). Run `taskforge --help` for the command list, or during + * development: `pnpm cli `. + */ +import { Command } from 'commander'; +import { db } from './db'; +import { auth } from './auth'; + +const program = new Command(); + +program + .name('taskforge') + .description('CLI for the TaskForge collaboration platform (ZenStack v3 + Better-Auth)') + .version('0.1.0'); + +// --------------------------------------------------------------------------- +// organizations +// --------------------------------------------------------------------------- +program + .command('orgs') + .description('List organizations with member, team and project counts') + .action(async () => { + const orgs = await db.organization.findMany({ + include: { + owner: { select: { name: true } }, + _count: { select: { members: true, teams: true, projects: true } }, + subscription: { include: { plan: true } }, + }, + orderBy: { createdAt: 'asc' }, + }); + if (orgs.length === 0) return console.log('No organizations. Run `pnpm seed` first.'); + for (const o of orgs) { + const plan = o.subscription?.plan.name ?? 'no plan'; + console.log( + `• ${o.name} (@${o.slug}) — owner ${o.owner.name} · ` + + `${o._count.members} members, ${o._count.teams} teams, ${o._count.projects} projects · ${plan}`, + ); + } + }); + +// --------------------------------------------------------------------------- +// projects (demonstrates the `openIssueCount` computed field) +// --------------------------------------------------------------------------- +program + .command('projects') + .argument('[orgSlug]', 'filter by organization slug') + .description('List projects and their open-issue counts (computed field)') + .action(async (orgSlug?: string) => { + const projects = await db.project.findMany({ + where: orgSlug ? { organization: { slug: orgSlug } } : undefined, + include: { + team: { select: { key: true } }, + lead: { select: { name: true } }, + _count: { select: { issues: true, members: true } }, + }, + orderBy: { name: 'asc' }, + }); + if (projects.length === 0) return console.log('No projects found.'); + for (const p of projects) { + console.log( + `• ${p.name} (${p.slug}) — team ${p.team?.key ?? '—'}, lead ${p.lead?.name ?? '—'} · ` + + `${p.openIssueCount}/${p._count.issues} issues open · ${p._count.members} members`, + ); + } + }); + +// --------------------------------------------------------------------------- +// issues (demonstrates relation include + the `commentCount` computed field) +// --------------------------------------------------------------------------- +program + .command('issues') + .argument('', 'project slug, e.g. "platform"') + .option('-s, --status ', 'filter by status (e.g. IN_PROGRESS)') + .description('List issues in a project') + .action(async (projectSlug: string, opts: { status?: string }) => { + const issues = await db.issue.findMany({ + where: { + project: { slug: projectSlug }, + ...(opts.status ? { status: opts.status as never } : {}), + }, + include: { + assignee: { select: { name: true } }, + labels: { select: { name: true } }, + _count: { select: { children: true, attachments: true } }, + }, + orderBy: { number: 'asc' }, + }); + if (issues.length === 0) return console.log('No issues found.'); + for (const i of issues) { + const labels = i.labels.map((l) => `#${l.name}`).join(' '); + console.log( + `#${i.number} [${i.status}/${i.priority}] ${i.title} — ` + + `@${i.assignee?.name ?? 'unassigned'} · ${i.commentCount} comments · ` + + `${i._count.children} subtasks · ${i._count.attachments} files ${labels}`, + ); + } + }); + +// --------------------------------------------------------------------------- +// issue (deep relation read incl. polymorphic attachments) +// --------------------------------------------------------------------------- +program + .command('issue') + .argument('') + .argument('', 'issue number within the project', Number) + .description('Show a single issue in detail') + .action(async (projectSlug: string, number: number) => { + const issue = await db.issue.findFirst({ + where: { project: { slug: projectSlug }, number }, + include: { + author: { select: { name: true } }, + assignee: { select: { name: true } }, + watchers: { select: { name: true } }, + milestone: { select: { title: true } }, + sprint: { select: { name: true } }, + comments: { include: { author: { select: { name: true } } }, orderBy: { createdAt: 'asc' } }, + attachments: true, // polymorphic — each row includes its concrete fields + timeEntries: true, + }, + }); + if (!issue) return console.log('Issue not found.'); + console.log(`#${issue.number} ${issue.title}`); + console.log(` status : ${issue.status} / ${issue.priority}`); + console.log(` author : ${issue.author.name}`); + console.log(` assignee : ${issue.assignee?.name ?? '—'}`); + console.log(` milestone: ${issue.milestone?.title ?? '—'} sprint: ${issue.sprint?.name ?? '—'}`); + console.log(` watchers : ${issue.watchers.map((w) => w.name).join(', ') || '—'}`); + console.log(` metadata : ${JSON.stringify(issue.metadata ?? {})}`); + const mins = issue.timeEntries.reduce((s, t) => s + t.minutes, 0); + console.log(` time : ${mins} minutes logged`); + console.log(' attachments:'); + for (const a of issue.attachments) { + // `kind` is the polymorphic discriminator; concrete fields are present. + const detail = + a.kind === 'FileAttachment' + ? `${(a as { fileName: string }).fileName}` + : `${(a as { url: string }).url}`; + console.log(` - [${a.kind}] ${detail}`); + } + console.log(' comments:'); + for (const c of issue.comments) { + console.log(` - ${c.author.name}: ${c.body}`); + } + }); + +// --------------------------------------------------------------------------- +// stats (groupBy + aggregate) +// --------------------------------------------------------------------------- +program + .command('stats') + .description('Aggregate issue statistics across all projects') + .action(async () => { + const byStatus = await db.issue.groupBy({ + by: ['status'], + _count: { _all: true }, + _avg: { estimate: true }, + }); + console.log('Issues by status:'); + for (const row of byStatus) { + const avg = row._avg.estimate?.toFixed(1) ?? '—'; + console.log(` ${row.status.padEnd(12)} ${row._count._all} (avg estimate ${avg})`); + } + + const totals = await db.issue.aggregate({ _count: { _all: true }, _sum: { estimate: true } }); + console.log(`\nTotal issues: ${totals._count._all}, total points: ${totals._sum.estimate ?? 0}`); + + const revenue = await db.invoice.aggregate({ + where: { status: 'PAID' }, + _sum: { amountCents: true }, + }); + console.log(`Paid revenue: $${((revenue._sum.amountCents ?? 0) / 100).toFixed(2)}`); + }); + +// --------------------------------------------------------------------------- +// add-issue (interactive-free create in a transaction + activity log) +// --------------------------------------------------------------------------- +program + .command('add-issue') + .argument('') + .argument('', 'issue title') + .option('-p, --priority ', 'NONE|LOW|MEDIUM|HIGH|URGENT', 'NONE') + .description('Create a new issue (auto-numbered) inside a transaction') + .action(async (projectSlug: string, titleParts: string[], opts: { priority: string }) => { + const title = titleParts.join(' '); + const project = await db.project.findFirst({ + where: { slug: projectSlug }, + include: { organization: { select: { id: true, ownerId: true } } }, + }); + if (!project) return console.log(`Project "${projectSlug}" not found.`); + + const created = await db.$transaction(async (tx) => { + const max = await tx.issue.aggregate({ + where: { projectId: project.id }, + _max: { number: true }, + }); + const next = (max._max.number ?? 0) + 1; + const issue = await tx.issue.create({ + data: { + number: next, + title, + priority: opts.priority as never, + project: { connect: { id: project.id } }, + author: { connect: { id: project.organization.ownerId } }, + }, + }); + await tx.activityLog.create({ + data: { + action: 'issue.created', + targetType: 'Issue', + targetId: issue.id, + organization: { connect: { id: project.organization.id } }, + actor: { connect: { id: project.organization.ownerId } }, + }, + }); + return issue; + }); + console.log(`Created issue #${created.number}: ${created.title}`); + }); + +// --------------------------------------------------------------------------- +// signup (Better-Auth — demonstrates the auth integration) +// --------------------------------------------------------------------------- +program + .command('signup') + .argument('') + .argument('') + .option('--password ', 'account password', 'Password123!') + .description('Register a user through Better-Auth (writes user + account rows)') + .action(async (email: string, nameParts: string[], opts: { password: string }) => { + const res: any = await auth.api.signUpEmail({ + body: { email, name: nameParts.join(' '), password: opts.password }, + }); + console.log(`Registered ${res.user.name} <${res.user.email}> (id: ${res.user.id})`); + }); + +// --------------------------------------------------------------------------- +// activity (recent audit log) +// --------------------------------------------------------------------------- +program + .command('activity') + .option('-n, --limit ', 'number of entries', '10') + .description('Show the most recent activity-log entries') + .action(async (opts: { limit: string }) => { + const entries = await db.activityLog.findMany({ + take: Number(opts.limit), + orderBy: { createdAt: 'desc' }, + include: { actor: { select: { name: true } } }, + }); + for (const e of entries) { + console.log(`${e.createdAt.toISOString()} ${e.actor.name} ${e.action} (${e.targetType})`); + } + }); + +program + .parseAsync() + .then(() => process.exit(0)) + .catch((err) => { + console.error(err instanceof Error ? err.message : err); + process.exit(1); + }); diff --git a/samples/taskforge/src/db.ts b/samples/taskforge/src/db.ts new file mode 100644 index 000000000..172bb966a --- /dev/null +++ b/samples/taskforge/src/db.ts @@ -0,0 +1,57 @@ +import { fileURLToPath } from 'node:url'; +import { ZenStackClient, type ClientContract } from '@zenstackhq/orm'; +import { SqliteDialect } from '@zenstackhq/orm/dialects/sqlite'; +import { sql } from '@zenstackhq/orm/helpers'; +import SQLite from 'better-sqlite3'; +import { schema, type SchemaType } from '../zenstack/schema'; + +/** + * The SQLite file is created by `zen db push` next to the schema (the migration + * engine resolves `file:./taskforge.db` relative to the schema directory). + * Resolve it as an absolute path so the runtime opens the same file regardless + * of the current working directory. + */ +const databaseFile = fileURLToPath( + new URL('../zenstack/taskforge.db', import.meta.url), +); + +/** Explicit, fully-inferred client type for use in function signatures. */ +export type DB = ClientContract; + +export function createClient(): DB { + return new ZenStackClient(schema, { + dialect: new SqliteDialect({ + database: new SQLite(databaseFile), + }), + // Implementations for the `@computed` fields declared in the ZModel schema. + // Each is a Kysely sub-query correlated to the owning row via `whereRef`. + computedFields: { + project: { + openIssueCount: (eb, { modelAlias }) => + eb + .selectFrom('Issue') + .whereRef( + 'Issue.projectId', + '=', + sql.ref(`${modelAlias}.id`), + ) + .where('Issue.status', 'not in', ['DONE', 'CANCELED']) + .select(({ fn }) => fn.countAll().as('c')), + }, + issue: { + commentCount: (eb, { modelAlias }) => + eb + .selectFrom('Comment') + .whereRef( + 'Comment.issueId', + '=', + sql.ref(`${modelAlias}.id`), + ) + .select(({ fn }) => fn.countAll().as('c')), + }, + }, + }); +} + +/** Shared client instance used by the CLI. */ +export const db: DB = createClient(); diff --git a/samples/taskforge/src/seed.ts b/samples/taskforge/src/seed.ts new file mode 100644 index 000000000..97b8c157e --- /dev/null +++ b/samples/taskforge/src/seed.ts @@ -0,0 +1,305 @@ +/** + * Seed script — populates the database with a realistic TaskForge workspace. + * + * It demonstrates the two halves of the stack working together: + * 1. Users are created through Better-Auth (`auth.api.signUpEmail`), which + * writes `user` + `account` rows through the ZenStack adapter. + * 2. The whole domain graph (orgs, teams, projects, issues, …) is created with + * the ZenStack ORM, using nested writes and relation connects. + * + * Run with: pnpm seed + */ +import { db } from './db'; +import { auth } from './auth'; + +async function resetDatabase() { + // ZenStack emulates referential actions, so deleting the aggregate roots + // cascades to everything they own. Order: domain first, then auth users. + await db.organization.deleteMany(); + await db.notification.deleteMany(); + await db.user.deleteMany(); + await db.plan.deleteMany(); + await db.verification.deleteMany(); +} + +/** Create a user via Better-Auth and return its id. */ +async function signUp(name: string, email: string): Promise { + const res = await auth.api.signUpEmail({ + body: { name, email, password: 'Password123!' }, + }); + return (res as any).user.id; +} + +async function main() { + console.log('Resetting database…'); + await resetDatabase(); + + console.log('Creating users via Better-Auth…'); + const aliceId = await signUp('Alice Anders', 'alice@taskforge.dev'); + const bobId = await signUp('Bob Burns', 'bob@taskforge.dev'); + const carolId = await signUp('Carol Chen', 'carol@taskforge.dev'); + const daveId = await signUp('Dave Diaz', 'dave@taskforge.dev'); + + console.log('Creating billing plans…'); + await db.plan.createMany({ + data: [ + { name: 'Free', tier: 'FREE', priceCents: 0, seatLimit: 5 }, + { name: 'Pro', tier: 'PRO', priceCents: 1200, seatLimit: 25 }, + { name: 'Business', tier: 'BUSINESS', priceCents: 4900, seatLimit: 100 }, + { name: 'Enterprise', tier: 'ENTERPRISE', priceCents: 0 }, + ], + }); + const proPlan = await db.plan.findUniqueOrThrow({ where: { tier: 'PRO' } }); + + console.log('Creating organization with nested members, billing & teams…'); + const org = await db.organization.create({ + data: { + name: 'Acme Inc.', + slug: 'acme', + owner: { connect: { id: aliceId } }, + members: { + create: [ + { role: 'OWNER', user: { connect: { id: aliceId } } }, + { role: 'ADMIN', user: { connect: { id: bobId } } }, + { role: 'MEMBER', user: { connect: { id: carolId } } }, + { role: 'MEMBER', user: { connect: { id: daveId } } }, + ], + }, + billingCustomer: { + create: { stripeCustomerId: 'cus_acme_001', email: 'billing@acme.test' }, + }, + subscription: { + create: { + status: 'ACTIVE', + plan: { connect: { id: proPlan.id } }, + invoices: { + create: [ + { number: 'INV-0001', amountCents: 1200, status: 'PAID', paidAt: new Date() }, + { number: 'INV-0002', amountCents: 1200, status: 'OPEN' }, + ], + }, + }, + }, + labels: { + create: [ + { name: 'bug', color: '#d73a4a' }, + { name: 'feature', color: '#0e8a16' }, + { name: 'chore', color: '#fbca04' }, + ], + }, + customFields: { + create: [ + { name: 'Severity', type: 'SELECT' }, + { name: 'Reproducible', type: 'BOOLEAN' }, + ], + }, + }, + }); + + console.log('Creating a team and its members…'); + const team = await db.team.create({ + data: { + name: 'Engineering', + key: 'ENG', + description: 'Core product engineering', + organization: { connect: { id: org.id } }, + lead: { connect: { id: bobId } }, + members: { + create: [ + { user: { connect: { id: bobId } } }, + { user: { connect: { id: carolId } } }, + { user: { connect: { id: daveId } } }, + ], + }, + }, + }); + + console.log('Creating a project with members, milestone & sprint…'); + const project = await db.project.create({ + data: { + name: 'Platform', + slug: 'platform', + description: 'The core TaskForge platform', + organization: { connect: { id: org.id } }, + team: { connect: { id: team.id } }, + lead: { connect: { id: bobId } }, + members: { + create: [ + { role: 'LEAD', user: { connect: { id: bobId } } }, + { role: 'CONTRIBUTOR', user: { connect: { id: carolId } } }, + { role: 'CONTRIBUTOR', user: { connect: { id: daveId } } }, + ], + }, + milestones: { + create: [{ title: 'v1.0 launch', status: 'OPEN', dueDate: new Date(Date.now() + 30 * 864e5) }], + }, + sprints: { + create: [{ name: 'Sprint 1', status: 'ACTIVE', goal: 'Ship auth + projects' }], + }, + }, + }); + + const milestone = await db.milestone.findFirstOrThrow({ where: { projectId: project.id } }); + const sprint = await db.sprint.findFirstOrThrow({ where: { projectId: project.id } }); + const bugLabel = await db.label.findFirstOrThrow({ where: { organizationId: org.id, name: 'bug' } }); + const featureLabel = await db.label.findFirstOrThrow({ where: { organizationId: org.id, name: 'feature' } }); + + console.log('Creating issues with labels, watchers, comments & attachments…'); + const issueSpecs = [ + { title: 'Set up authentication', status: 'DONE', priority: 'HIGH', assignee: carolId }, + { title: 'Design project schema', status: 'IN_PROGRESS', priority: 'URGENT', assignee: bobId }, + { title: 'Implement issue board', status: 'TODO', priority: 'MEDIUM', assignee: daveId }, + { title: 'Add billing integration', status: 'BACKLOG', priority: 'LOW', assignee: null }, + { title: 'Flaky CI pipeline', status: 'IN_REVIEW', priority: 'HIGH', assignee: carolId }, + ] as const; + + let n = 0; + let parentIssueId: string | undefined; + for (const spec of issueSpecs) { + n += 1; + const issue = await db.issue.create({ + data: { + number: n, + title: spec.title, + status: spec.status, + priority: spec.priority, + estimate: (n % 3) + 1, + project: { connect: { id: project.id } }, + author: { connect: { id: aliceId } }, + ...(spec.assignee ? { assignee: { connect: { id: spec.assignee } } } : {}), + milestone: { connect: { id: milestone.id } }, + sprint: { connect: { id: sprint.id } }, + ...(parentIssueId ? { parent: { connect: { id: parentIssueId } } } : {}), + metadata: { externalId: `EXT-${100 + n}`, storyPoints: (n % 3) + 1 }, + labels: { connect: n % 2 === 0 ? [{ id: featureLabel.id }] : [{ id: bugLabel.id }] }, + watchers: { connect: [{ id: bobId }, { id: carolId }] }, + comments: { + create: [ + { body: 'Taking a look at this now.', author: { connect: { id: carolId } } }, + { body: 'Thanks, ping me if blocked.', author: { connect: { id: bobId } } }, + ], + }, + timeEntries: { + create: [ + { minutes: 30 * n, note: 'Initial work', user: { connect: { id: spec.assignee ?? aliceId } } }, + ], + }, + }, + }); + + // Polymorphic attachments are created through their concrete models + // (`@@delegate` base entities can't be created directly). The `kind` + // discriminator is set automatically. + await db.linkAttachment.create({ + data: { + url: 'https://example.com/design', + title: 'Design doc', + issue: { connect: { id: issue.id } }, + uploadedBy: { connect: { id: aliceId } }, + }, + }); + if (n % 2 === 0) { + await db.fileAttachment.create({ + data: { + fileName: `notes-${n}.pdf`, + fileSize: 1024 * n, + mimeType: 'application/pdf', + issue: { connect: { id: issue.id } }, + uploadedBy: { connect: { id: bobId } }, + }, + }); + } + + if (n === 1) parentIssueId = issue.id; // later issues become sub-issues of #1 + } + + console.log('Creating a document tree…'); + const rootDoc = await db.document.create({ + data: { + title: 'Engineering Handbook', + content: { format: 'markdown', body: '# Handbook\nWelcome to the team.', revision: 1 }, + project: { connect: { id: project.id } }, + author: { connect: { id: bobId } }, + collaborators: { connect: [{ id: carolId }, { id: daveId }] }, + }, + }); + await db.document.create({ + data: { + title: 'Onboarding', + content: { format: 'markdown', body: '## Day 1\nGet your laptop.', revision: 1 }, + project: { connect: { id: project.id } }, + author: { connect: { id: bobId } }, + parent: { connect: { id: rootDoc.id } }, + }, + }); + + console.log('Creating integrations, webhook, API token & notifications…'); + await db.integration.create({ + data: { + provider: 'GITHUB', + config: { accessToken: 'ghp_xxx', accountName: 'acme', scopes: 'repo,read:org' }, + organization: { connect: { id: org.id } }, + installedBy: { connect: { id: aliceId } }, + }, + }); + await db.webhook.create({ + data: { + url: 'https://hooks.acme.test/taskforge', + secret: 'whsec_123', + config: { events: 'issue.created,issue.updated', contentType: 'application/json' }, + organization: { connect: { id: org.id } }, + }, + }); + await db.apiToken.create({ + data: { + name: 'CI token', + tokenHash: 'hash_ci_token', + scopes: 'issues:read issues:write', + organization: { connect: { id: org.id } }, + user: { connect: { id: bobId } }, + }, + }); + await db.notification.createMany({ + data: [ + { + type: 'ISSUE_ASSIGNED', + message: 'You were assigned "Set up authentication"', + recipientId: carolId, + actorId: aliceId, + }, + { type: 'COMMENT_ADDED', message: 'Bob commented on an issue', recipientId: carolId, actorId: bobId }, + ], + }); + await db.activityLog.createMany({ + data: [ + { + action: 'project.created', + targetType: 'Project', + targetId: project.id, + organizationId: org.id, + actorId: aliceId, + }, + { action: 'team.created', targetType: 'Team', targetId: team.id, organizationId: org.id, actorId: aliceId }, + ], + }); + + console.log('\n✅ Seed complete.'); + const counts = { + users: await db.user.count(), + organizations: await db.organization.count(), + teams: await db.team.count(), + projects: await db.project.count(), + issues: await db.issue.count(), + comments: await db.comment.count(), + attachments: await db.attachment.count(), + documents: await db.document.count(), + }; + console.table(counts); +} + +main() + .then(() => process.exit(0)) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/samples/taskforge/tsconfig.json b/samples/taskforge/tsconfig.json new file mode 100644 index 000000000..7b797d55d --- /dev/null +++ b/samples/taskforge/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "declaration": true, + "outDir": "dist", + "rootDir": ".", + "types": ["node"] + }, + "include": ["src/**/*.ts", "zenstack/**/*.ts"] +} diff --git a/samples/taskforge/zenstack/input.ts b/samples/taskforge/zenstack/input.ts new file mode 100644 index 000000000..84e8b5a5e --- /dev/null +++ b/samples/taskforge/zenstack/input.ts @@ -0,0 +1,860 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { type SchemaType as $Schema } from "./schema"; +import type { FindManyArgs as $FindManyArgs, FindUniqueArgs as $FindUniqueArgs, FindFirstArgs as $FindFirstArgs, ExistsArgs as $ExistsArgs, CreateArgs as $CreateArgs, CreateManyArgs as $CreateManyArgs, CreateManyAndReturnArgs as $CreateManyAndReturnArgs, UpdateArgs as $UpdateArgs, UpdateManyArgs as $UpdateManyArgs, UpdateManyAndReturnArgs as $UpdateManyAndReturnArgs, UpsertArgs as $UpsertArgs, DeleteArgs as $DeleteArgs, DeleteManyArgs as $DeleteManyArgs, CountArgs as $CountArgs, AggregateArgs as $AggregateArgs, GroupByArgs as $GroupByArgs, WhereInput as $WhereInput, SelectInput as $SelectInput, IncludeInput as $IncludeInput, OmitInput as $OmitInput, UncheckedCreateInput as $UncheckedCreateInput, CheckedCreateInput as $CheckedCreateInput, UncheckedUpdateInput as $UncheckedUpdateInput, CheckedUpdateInput as $CheckedUpdateInput, QueryOptions as $QueryOptions } from "@zenstackhq/orm"; +import type { SimplifiedPlainResult as $Result, SelectIncludeOmit as $SelectIncludeOmit } from "@zenstackhq/orm"; +export type UserFindManyArgs = $FindManyArgs<$Schema, "User">; +export type UserFindUniqueArgs = $FindUniqueArgs<$Schema, "User">; +export type UserFindFirstArgs = $FindFirstArgs<$Schema, "User">; +export type UserExistsArgs = $ExistsArgs<$Schema, "User">; +export type UserCreateArgs = $CreateArgs<$Schema, "User">; +export type UserCreateManyArgs = $CreateManyArgs<$Schema, "User">; +export type UserCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "User">; +export type UserUpdateArgs = $UpdateArgs<$Schema, "User">; +export type UserUpdateManyArgs = $UpdateManyArgs<$Schema, "User">; +export type UserUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "User">; +export type UserUpsertArgs = $UpsertArgs<$Schema, "User">; +export type UserDeleteArgs = $DeleteArgs<$Schema, "User">; +export type UserDeleteManyArgs = $DeleteManyArgs<$Schema, "User">; +export type UserCountArgs = $CountArgs<$Schema, "User">; +export type UserAggregateArgs = $AggregateArgs<$Schema, "User">; +export type UserGroupByArgs = $GroupByArgs<$Schema, "User">; +export type UserWhereInput = $WhereInput<$Schema, "User">; +export type UserSelect = $SelectInput<$Schema, "User">; +export type UserInclude = $IncludeInput<$Schema, "User">; +export type UserOmit = $OmitInput<$Schema, "User">; +export type UserUncheckedCreateInput = $UncheckedCreateInput<$Schema, "User">; +export type UserCheckedCreateInput = $CheckedCreateInput<$Schema, "User">; +export type UserUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "User">; +export type UserCheckedUpdateInput = $CheckedUpdateInput<$Schema, "User">; +export type UserGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "User", Args, Options>; +export type AccountFindManyArgs = $FindManyArgs<$Schema, "Account">; +export type AccountFindUniqueArgs = $FindUniqueArgs<$Schema, "Account">; +export type AccountFindFirstArgs = $FindFirstArgs<$Schema, "Account">; +export type AccountExistsArgs = $ExistsArgs<$Schema, "Account">; +export type AccountCreateArgs = $CreateArgs<$Schema, "Account">; +export type AccountCreateManyArgs = $CreateManyArgs<$Schema, "Account">; +export type AccountCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Account">; +export type AccountUpdateArgs = $UpdateArgs<$Schema, "Account">; +export type AccountUpdateManyArgs = $UpdateManyArgs<$Schema, "Account">; +export type AccountUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Account">; +export type AccountUpsertArgs = $UpsertArgs<$Schema, "Account">; +export type AccountDeleteArgs = $DeleteArgs<$Schema, "Account">; +export type AccountDeleteManyArgs = $DeleteManyArgs<$Schema, "Account">; +export type AccountCountArgs = $CountArgs<$Schema, "Account">; +export type AccountAggregateArgs = $AggregateArgs<$Schema, "Account">; +export type AccountGroupByArgs = $GroupByArgs<$Schema, "Account">; +export type AccountWhereInput = $WhereInput<$Schema, "Account">; +export type AccountSelect = $SelectInput<$Schema, "Account">; +export type AccountInclude = $IncludeInput<$Schema, "Account">; +export type AccountOmit = $OmitInput<$Schema, "Account">; +export type AccountUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Account">; +export type AccountCheckedCreateInput = $CheckedCreateInput<$Schema, "Account">; +export type AccountUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Account">; +export type AccountCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Account">; +export type AccountGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Account", Args, Options>; +export type SessionFindManyArgs = $FindManyArgs<$Schema, "Session">; +export type SessionFindUniqueArgs = $FindUniqueArgs<$Schema, "Session">; +export type SessionFindFirstArgs = $FindFirstArgs<$Schema, "Session">; +export type SessionExistsArgs = $ExistsArgs<$Schema, "Session">; +export type SessionCreateArgs = $CreateArgs<$Schema, "Session">; +export type SessionCreateManyArgs = $CreateManyArgs<$Schema, "Session">; +export type SessionCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Session">; +export type SessionUpdateArgs = $UpdateArgs<$Schema, "Session">; +export type SessionUpdateManyArgs = $UpdateManyArgs<$Schema, "Session">; +export type SessionUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Session">; +export type SessionUpsertArgs = $UpsertArgs<$Schema, "Session">; +export type SessionDeleteArgs = $DeleteArgs<$Schema, "Session">; +export type SessionDeleteManyArgs = $DeleteManyArgs<$Schema, "Session">; +export type SessionCountArgs = $CountArgs<$Schema, "Session">; +export type SessionAggregateArgs = $AggregateArgs<$Schema, "Session">; +export type SessionGroupByArgs = $GroupByArgs<$Schema, "Session">; +export type SessionWhereInput = $WhereInput<$Schema, "Session">; +export type SessionSelect = $SelectInput<$Schema, "Session">; +export type SessionInclude = $IncludeInput<$Schema, "Session">; +export type SessionOmit = $OmitInput<$Schema, "Session">; +export type SessionUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Session">; +export type SessionCheckedCreateInput = $CheckedCreateInput<$Schema, "Session">; +export type SessionUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Session">; +export type SessionCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Session">; +export type SessionGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Session", Args, Options>; +export type VerificationFindManyArgs = $FindManyArgs<$Schema, "Verification">; +export type VerificationFindUniqueArgs = $FindUniqueArgs<$Schema, "Verification">; +export type VerificationFindFirstArgs = $FindFirstArgs<$Schema, "Verification">; +export type VerificationExistsArgs = $ExistsArgs<$Schema, "Verification">; +export type VerificationCreateArgs = $CreateArgs<$Schema, "Verification">; +export type VerificationCreateManyArgs = $CreateManyArgs<$Schema, "Verification">; +export type VerificationCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Verification">; +export type VerificationUpdateArgs = $UpdateArgs<$Schema, "Verification">; +export type VerificationUpdateManyArgs = $UpdateManyArgs<$Schema, "Verification">; +export type VerificationUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Verification">; +export type VerificationUpsertArgs = $UpsertArgs<$Schema, "Verification">; +export type VerificationDeleteArgs = $DeleteArgs<$Schema, "Verification">; +export type VerificationDeleteManyArgs = $DeleteManyArgs<$Schema, "Verification">; +export type VerificationCountArgs = $CountArgs<$Schema, "Verification">; +export type VerificationAggregateArgs = $AggregateArgs<$Schema, "Verification">; +export type VerificationGroupByArgs = $GroupByArgs<$Schema, "Verification">; +export type VerificationWhereInput = $WhereInput<$Schema, "Verification">; +export type VerificationSelect = $SelectInput<$Schema, "Verification">; +export type VerificationInclude = $IncludeInput<$Schema, "Verification">; +export type VerificationOmit = $OmitInput<$Schema, "Verification">; +export type VerificationUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Verification">; +export type VerificationCheckedCreateInput = $CheckedCreateInput<$Schema, "Verification">; +export type VerificationUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Verification">; +export type VerificationCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Verification">; +export type VerificationGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Verification", Args, Options>; +export type OrganizationFindManyArgs = $FindManyArgs<$Schema, "Organization">; +export type OrganizationFindUniqueArgs = $FindUniqueArgs<$Schema, "Organization">; +export type OrganizationFindFirstArgs = $FindFirstArgs<$Schema, "Organization">; +export type OrganizationExistsArgs = $ExistsArgs<$Schema, "Organization">; +export type OrganizationCreateArgs = $CreateArgs<$Schema, "Organization">; +export type OrganizationCreateManyArgs = $CreateManyArgs<$Schema, "Organization">; +export type OrganizationCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Organization">; +export type OrganizationUpdateArgs = $UpdateArgs<$Schema, "Organization">; +export type OrganizationUpdateManyArgs = $UpdateManyArgs<$Schema, "Organization">; +export type OrganizationUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Organization">; +export type OrganizationUpsertArgs = $UpsertArgs<$Schema, "Organization">; +export type OrganizationDeleteArgs = $DeleteArgs<$Schema, "Organization">; +export type OrganizationDeleteManyArgs = $DeleteManyArgs<$Schema, "Organization">; +export type OrganizationCountArgs = $CountArgs<$Schema, "Organization">; +export type OrganizationAggregateArgs = $AggregateArgs<$Schema, "Organization">; +export type OrganizationGroupByArgs = $GroupByArgs<$Schema, "Organization">; +export type OrganizationWhereInput = $WhereInput<$Schema, "Organization">; +export type OrganizationSelect = $SelectInput<$Schema, "Organization">; +export type OrganizationInclude = $IncludeInput<$Schema, "Organization">; +export type OrganizationOmit = $OmitInput<$Schema, "Organization">; +export type OrganizationUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Organization">; +export type OrganizationCheckedCreateInput = $CheckedCreateInput<$Schema, "Organization">; +export type OrganizationUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Organization">; +export type OrganizationCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Organization">; +export type OrganizationGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Organization", Args, Options>; +export type MembershipFindManyArgs = $FindManyArgs<$Schema, "Membership">; +export type MembershipFindUniqueArgs = $FindUniqueArgs<$Schema, "Membership">; +export type MembershipFindFirstArgs = $FindFirstArgs<$Schema, "Membership">; +export type MembershipExistsArgs = $ExistsArgs<$Schema, "Membership">; +export type MembershipCreateArgs = $CreateArgs<$Schema, "Membership">; +export type MembershipCreateManyArgs = $CreateManyArgs<$Schema, "Membership">; +export type MembershipCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Membership">; +export type MembershipUpdateArgs = $UpdateArgs<$Schema, "Membership">; +export type MembershipUpdateManyArgs = $UpdateManyArgs<$Schema, "Membership">; +export type MembershipUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Membership">; +export type MembershipUpsertArgs = $UpsertArgs<$Schema, "Membership">; +export type MembershipDeleteArgs = $DeleteArgs<$Schema, "Membership">; +export type MembershipDeleteManyArgs = $DeleteManyArgs<$Schema, "Membership">; +export type MembershipCountArgs = $CountArgs<$Schema, "Membership">; +export type MembershipAggregateArgs = $AggregateArgs<$Schema, "Membership">; +export type MembershipGroupByArgs = $GroupByArgs<$Schema, "Membership">; +export type MembershipWhereInput = $WhereInput<$Schema, "Membership">; +export type MembershipSelect = $SelectInput<$Schema, "Membership">; +export type MembershipInclude = $IncludeInput<$Schema, "Membership">; +export type MembershipOmit = $OmitInput<$Schema, "Membership">; +export type MembershipUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Membership">; +export type MembershipCheckedCreateInput = $CheckedCreateInput<$Schema, "Membership">; +export type MembershipUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Membership">; +export type MembershipCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Membership">; +export type MembershipGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Membership", Args, Options>; +export type InvitationFindManyArgs = $FindManyArgs<$Schema, "Invitation">; +export type InvitationFindUniqueArgs = $FindUniqueArgs<$Schema, "Invitation">; +export type InvitationFindFirstArgs = $FindFirstArgs<$Schema, "Invitation">; +export type InvitationExistsArgs = $ExistsArgs<$Schema, "Invitation">; +export type InvitationCreateArgs = $CreateArgs<$Schema, "Invitation">; +export type InvitationCreateManyArgs = $CreateManyArgs<$Schema, "Invitation">; +export type InvitationCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Invitation">; +export type InvitationUpdateArgs = $UpdateArgs<$Schema, "Invitation">; +export type InvitationUpdateManyArgs = $UpdateManyArgs<$Schema, "Invitation">; +export type InvitationUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Invitation">; +export type InvitationUpsertArgs = $UpsertArgs<$Schema, "Invitation">; +export type InvitationDeleteArgs = $DeleteArgs<$Schema, "Invitation">; +export type InvitationDeleteManyArgs = $DeleteManyArgs<$Schema, "Invitation">; +export type InvitationCountArgs = $CountArgs<$Schema, "Invitation">; +export type InvitationAggregateArgs = $AggregateArgs<$Schema, "Invitation">; +export type InvitationGroupByArgs = $GroupByArgs<$Schema, "Invitation">; +export type InvitationWhereInput = $WhereInput<$Schema, "Invitation">; +export type InvitationSelect = $SelectInput<$Schema, "Invitation">; +export type InvitationInclude = $IncludeInput<$Schema, "Invitation">; +export type InvitationOmit = $OmitInput<$Schema, "Invitation">; +export type InvitationUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Invitation">; +export type InvitationCheckedCreateInput = $CheckedCreateInput<$Schema, "Invitation">; +export type InvitationUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Invitation">; +export type InvitationCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Invitation">; +export type InvitationGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Invitation", Args, Options>; +export type TeamFindManyArgs = $FindManyArgs<$Schema, "Team">; +export type TeamFindUniqueArgs = $FindUniqueArgs<$Schema, "Team">; +export type TeamFindFirstArgs = $FindFirstArgs<$Schema, "Team">; +export type TeamExistsArgs = $ExistsArgs<$Schema, "Team">; +export type TeamCreateArgs = $CreateArgs<$Schema, "Team">; +export type TeamCreateManyArgs = $CreateManyArgs<$Schema, "Team">; +export type TeamCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Team">; +export type TeamUpdateArgs = $UpdateArgs<$Schema, "Team">; +export type TeamUpdateManyArgs = $UpdateManyArgs<$Schema, "Team">; +export type TeamUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Team">; +export type TeamUpsertArgs = $UpsertArgs<$Schema, "Team">; +export type TeamDeleteArgs = $DeleteArgs<$Schema, "Team">; +export type TeamDeleteManyArgs = $DeleteManyArgs<$Schema, "Team">; +export type TeamCountArgs = $CountArgs<$Schema, "Team">; +export type TeamAggregateArgs = $AggregateArgs<$Schema, "Team">; +export type TeamGroupByArgs = $GroupByArgs<$Schema, "Team">; +export type TeamWhereInput = $WhereInput<$Schema, "Team">; +export type TeamSelect = $SelectInput<$Schema, "Team">; +export type TeamInclude = $IncludeInput<$Schema, "Team">; +export type TeamOmit = $OmitInput<$Schema, "Team">; +export type TeamUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Team">; +export type TeamCheckedCreateInput = $CheckedCreateInput<$Schema, "Team">; +export type TeamUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Team">; +export type TeamCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Team">; +export type TeamGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Team", Args, Options>; +export type TeamMembershipFindManyArgs = $FindManyArgs<$Schema, "TeamMembership">; +export type TeamMembershipFindUniqueArgs = $FindUniqueArgs<$Schema, "TeamMembership">; +export type TeamMembershipFindFirstArgs = $FindFirstArgs<$Schema, "TeamMembership">; +export type TeamMembershipExistsArgs = $ExistsArgs<$Schema, "TeamMembership">; +export type TeamMembershipCreateArgs = $CreateArgs<$Schema, "TeamMembership">; +export type TeamMembershipCreateManyArgs = $CreateManyArgs<$Schema, "TeamMembership">; +export type TeamMembershipCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "TeamMembership">; +export type TeamMembershipUpdateArgs = $UpdateArgs<$Schema, "TeamMembership">; +export type TeamMembershipUpdateManyArgs = $UpdateManyArgs<$Schema, "TeamMembership">; +export type TeamMembershipUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "TeamMembership">; +export type TeamMembershipUpsertArgs = $UpsertArgs<$Schema, "TeamMembership">; +export type TeamMembershipDeleteArgs = $DeleteArgs<$Schema, "TeamMembership">; +export type TeamMembershipDeleteManyArgs = $DeleteManyArgs<$Schema, "TeamMembership">; +export type TeamMembershipCountArgs = $CountArgs<$Schema, "TeamMembership">; +export type TeamMembershipAggregateArgs = $AggregateArgs<$Schema, "TeamMembership">; +export type TeamMembershipGroupByArgs = $GroupByArgs<$Schema, "TeamMembership">; +export type TeamMembershipWhereInput = $WhereInput<$Schema, "TeamMembership">; +export type TeamMembershipSelect = $SelectInput<$Schema, "TeamMembership">; +export type TeamMembershipInclude = $IncludeInput<$Schema, "TeamMembership">; +export type TeamMembershipOmit = $OmitInput<$Schema, "TeamMembership">; +export type TeamMembershipUncheckedCreateInput = $UncheckedCreateInput<$Schema, "TeamMembership">; +export type TeamMembershipCheckedCreateInput = $CheckedCreateInput<$Schema, "TeamMembership">; +export type TeamMembershipUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "TeamMembership">; +export type TeamMembershipCheckedUpdateInput = $CheckedUpdateInput<$Schema, "TeamMembership">; +export type TeamMembershipGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "TeamMembership", Args, Options>; +export type ProjectFindManyArgs = $FindManyArgs<$Schema, "Project">; +export type ProjectFindUniqueArgs = $FindUniqueArgs<$Schema, "Project">; +export type ProjectFindFirstArgs = $FindFirstArgs<$Schema, "Project">; +export type ProjectExistsArgs = $ExistsArgs<$Schema, "Project">; +export type ProjectCreateArgs = $CreateArgs<$Schema, "Project">; +export type ProjectCreateManyArgs = $CreateManyArgs<$Schema, "Project">; +export type ProjectCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Project">; +export type ProjectUpdateArgs = $UpdateArgs<$Schema, "Project">; +export type ProjectUpdateManyArgs = $UpdateManyArgs<$Schema, "Project">; +export type ProjectUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Project">; +export type ProjectUpsertArgs = $UpsertArgs<$Schema, "Project">; +export type ProjectDeleteArgs = $DeleteArgs<$Schema, "Project">; +export type ProjectDeleteManyArgs = $DeleteManyArgs<$Schema, "Project">; +export type ProjectCountArgs = $CountArgs<$Schema, "Project">; +export type ProjectAggregateArgs = $AggregateArgs<$Schema, "Project">; +export type ProjectGroupByArgs = $GroupByArgs<$Schema, "Project">; +export type ProjectWhereInput = $WhereInput<$Schema, "Project">; +export type ProjectSelect = $SelectInput<$Schema, "Project">; +export type ProjectInclude = $IncludeInput<$Schema, "Project">; +export type ProjectOmit = $OmitInput<$Schema, "Project">; +export type ProjectUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Project">; +export type ProjectCheckedCreateInput = $CheckedCreateInput<$Schema, "Project">; +export type ProjectUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Project">; +export type ProjectCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Project">; +export type ProjectGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Project", Args, Options>; +export type ProjectMemberFindManyArgs = $FindManyArgs<$Schema, "ProjectMember">; +export type ProjectMemberFindUniqueArgs = $FindUniqueArgs<$Schema, "ProjectMember">; +export type ProjectMemberFindFirstArgs = $FindFirstArgs<$Schema, "ProjectMember">; +export type ProjectMemberExistsArgs = $ExistsArgs<$Schema, "ProjectMember">; +export type ProjectMemberCreateArgs = $CreateArgs<$Schema, "ProjectMember">; +export type ProjectMemberCreateManyArgs = $CreateManyArgs<$Schema, "ProjectMember">; +export type ProjectMemberCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "ProjectMember">; +export type ProjectMemberUpdateArgs = $UpdateArgs<$Schema, "ProjectMember">; +export type ProjectMemberUpdateManyArgs = $UpdateManyArgs<$Schema, "ProjectMember">; +export type ProjectMemberUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "ProjectMember">; +export type ProjectMemberUpsertArgs = $UpsertArgs<$Schema, "ProjectMember">; +export type ProjectMemberDeleteArgs = $DeleteArgs<$Schema, "ProjectMember">; +export type ProjectMemberDeleteManyArgs = $DeleteManyArgs<$Schema, "ProjectMember">; +export type ProjectMemberCountArgs = $CountArgs<$Schema, "ProjectMember">; +export type ProjectMemberAggregateArgs = $AggregateArgs<$Schema, "ProjectMember">; +export type ProjectMemberGroupByArgs = $GroupByArgs<$Schema, "ProjectMember">; +export type ProjectMemberWhereInput = $WhereInput<$Schema, "ProjectMember">; +export type ProjectMemberSelect = $SelectInput<$Schema, "ProjectMember">; +export type ProjectMemberInclude = $IncludeInput<$Schema, "ProjectMember">; +export type ProjectMemberOmit = $OmitInput<$Schema, "ProjectMember">; +export type ProjectMemberUncheckedCreateInput = $UncheckedCreateInput<$Schema, "ProjectMember">; +export type ProjectMemberCheckedCreateInput = $CheckedCreateInput<$Schema, "ProjectMember">; +export type ProjectMemberUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "ProjectMember">; +export type ProjectMemberCheckedUpdateInput = $CheckedUpdateInput<$Schema, "ProjectMember">; +export type ProjectMemberGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "ProjectMember", Args, Options>; +export type IssueFindManyArgs = $FindManyArgs<$Schema, "Issue">; +export type IssueFindUniqueArgs = $FindUniqueArgs<$Schema, "Issue">; +export type IssueFindFirstArgs = $FindFirstArgs<$Schema, "Issue">; +export type IssueExistsArgs = $ExistsArgs<$Schema, "Issue">; +export type IssueCreateArgs = $CreateArgs<$Schema, "Issue">; +export type IssueCreateManyArgs = $CreateManyArgs<$Schema, "Issue">; +export type IssueCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Issue">; +export type IssueUpdateArgs = $UpdateArgs<$Schema, "Issue">; +export type IssueUpdateManyArgs = $UpdateManyArgs<$Schema, "Issue">; +export type IssueUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Issue">; +export type IssueUpsertArgs = $UpsertArgs<$Schema, "Issue">; +export type IssueDeleteArgs = $DeleteArgs<$Schema, "Issue">; +export type IssueDeleteManyArgs = $DeleteManyArgs<$Schema, "Issue">; +export type IssueCountArgs = $CountArgs<$Schema, "Issue">; +export type IssueAggregateArgs = $AggregateArgs<$Schema, "Issue">; +export type IssueGroupByArgs = $GroupByArgs<$Schema, "Issue">; +export type IssueWhereInput = $WhereInput<$Schema, "Issue">; +export type IssueSelect = $SelectInput<$Schema, "Issue">; +export type IssueInclude = $IncludeInput<$Schema, "Issue">; +export type IssueOmit = $OmitInput<$Schema, "Issue">; +export type IssueUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Issue">; +export type IssueCheckedCreateInput = $CheckedCreateInput<$Schema, "Issue">; +export type IssueUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Issue">; +export type IssueCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Issue">; +export type IssueGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Issue", Args, Options>; +export type LabelFindManyArgs = $FindManyArgs<$Schema, "Label">; +export type LabelFindUniqueArgs = $FindUniqueArgs<$Schema, "Label">; +export type LabelFindFirstArgs = $FindFirstArgs<$Schema, "Label">; +export type LabelExistsArgs = $ExistsArgs<$Schema, "Label">; +export type LabelCreateArgs = $CreateArgs<$Schema, "Label">; +export type LabelCreateManyArgs = $CreateManyArgs<$Schema, "Label">; +export type LabelCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Label">; +export type LabelUpdateArgs = $UpdateArgs<$Schema, "Label">; +export type LabelUpdateManyArgs = $UpdateManyArgs<$Schema, "Label">; +export type LabelUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Label">; +export type LabelUpsertArgs = $UpsertArgs<$Schema, "Label">; +export type LabelDeleteArgs = $DeleteArgs<$Schema, "Label">; +export type LabelDeleteManyArgs = $DeleteManyArgs<$Schema, "Label">; +export type LabelCountArgs = $CountArgs<$Schema, "Label">; +export type LabelAggregateArgs = $AggregateArgs<$Schema, "Label">; +export type LabelGroupByArgs = $GroupByArgs<$Schema, "Label">; +export type LabelWhereInput = $WhereInput<$Schema, "Label">; +export type LabelSelect = $SelectInput<$Schema, "Label">; +export type LabelInclude = $IncludeInput<$Schema, "Label">; +export type LabelOmit = $OmitInput<$Schema, "Label">; +export type LabelUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Label">; +export type LabelCheckedCreateInput = $CheckedCreateInput<$Schema, "Label">; +export type LabelUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Label">; +export type LabelCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Label">; +export type LabelGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Label", Args, Options>; +export type MilestoneFindManyArgs = $FindManyArgs<$Schema, "Milestone">; +export type MilestoneFindUniqueArgs = $FindUniqueArgs<$Schema, "Milestone">; +export type MilestoneFindFirstArgs = $FindFirstArgs<$Schema, "Milestone">; +export type MilestoneExistsArgs = $ExistsArgs<$Schema, "Milestone">; +export type MilestoneCreateArgs = $CreateArgs<$Schema, "Milestone">; +export type MilestoneCreateManyArgs = $CreateManyArgs<$Schema, "Milestone">; +export type MilestoneCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Milestone">; +export type MilestoneUpdateArgs = $UpdateArgs<$Schema, "Milestone">; +export type MilestoneUpdateManyArgs = $UpdateManyArgs<$Schema, "Milestone">; +export type MilestoneUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Milestone">; +export type MilestoneUpsertArgs = $UpsertArgs<$Schema, "Milestone">; +export type MilestoneDeleteArgs = $DeleteArgs<$Schema, "Milestone">; +export type MilestoneDeleteManyArgs = $DeleteManyArgs<$Schema, "Milestone">; +export type MilestoneCountArgs = $CountArgs<$Schema, "Milestone">; +export type MilestoneAggregateArgs = $AggregateArgs<$Schema, "Milestone">; +export type MilestoneGroupByArgs = $GroupByArgs<$Schema, "Milestone">; +export type MilestoneWhereInput = $WhereInput<$Schema, "Milestone">; +export type MilestoneSelect = $SelectInput<$Schema, "Milestone">; +export type MilestoneInclude = $IncludeInput<$Schema, "Milestone">; +export type MilestoneOmit = $OmitInput<$Schema, "Milestone">; +export type MilestoneUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Milestone">; +export type MilestoneCheckedCreateInput = $CheckedCreateInput<$Schema, "Milestone">; +export type MilestoneUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Milestone">; +export type MilestoneCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Milestone">; +export type MilestoneGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Milestone", Args, Options>; +export type SprintFindManyArgs = $FindManyArgs<$Schema, "Sprint">; +export type SprintFindUniqueArgs = $FindUniqueArgs<$Schema, "Sprint">; +export type SprintFindFirstArgs = $FindFirstArgs<$Schema, "Sprint">; +export type SprintExistsArgs = $ExistsArgs<$Schema, "Sprint">; +export type SprintCreateArgs = $CreateArgs<$Schema, "Sprint">; +export type SprintCreateManyArgs = $CreateManyArgs<$Schema, "Sprint">; +export type SprintCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Sprint">; +export type SprintUpdateArgs = $UpdateArgs<$Schema, "Sprint">; +export type SprintUpdateManyArgs = $UpdateManyArgs<$Schema, "Sprint">; +export type SprintUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Sprint">; +export type SprintUpsertArgs = $UpsertArgs<$Schema, "Sprint">; +export type SprintDeleteArgs = $DeleteArgs<$Schema, "Sprint">; +export type SprintDeleteManyArgs = $DeleteManyArgs<$Schema, "Sprint">; +export type SprintCountArgs = $CountArgs<$Schema, "Sprint">; +export type SprintAggregateArgs = $AggregateArgs<$Schema, "Sprint">; +export type SprintGroupByArgs = $GroupByArgs<$Schema, "Sprint">; +export type SprintWhereInput = $WhereInput<$Schema, "Sprint">; +export type SprintSelect = $SelectInput<$Schema, "Sprint">; +export type SprintInclude = $IncludeInput<$Schema, "Sprint">; +export type SprintOmit = $OmitInput<$Schema, "Sprint">; +export type SprintUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Sprint">; +export type SprintCheckedCreateInput = $CheckedCreateInput<$Schema, "Sprint">; +export type SprintUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Sprint">; +export type SprintCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Sprint">; +export type SprintGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Sprint", Args, Options>; +export type CommentFindManyArgs = $FindManyArgs<$Schema, "Comment">; +export type CommentFindUniqueArgs = $FindUniqueArgs<$Schema, "Comment">; +export type CommentFindFirstArgs = $FindFirstArgs<$Schema, "Comment">; +export type CommentExistsArgs = $ExistsArgs<$Schema, "Comment">; +export type CommentCreateArgs = $CreateArgs<$Schema, "Comment">; +export type CommentCreateManyArgs = $CreateManyArgs<$Schema, "Comment">; +export type CommentCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Comment">; +export type CommentUpdateArgs = $UpdateArgs<$Schema, "Comment">; +export type CommentUpdateManyArgs = $UpdateManyArgs<$Schema, "Comment">; +export type CommentUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Comment">; +export type CommentUpsertArgs = $UpsertArgs<$Schema, "Comment">; +export type CommentDeleteArgs = $DeleteArgs<$Schema, "Comment">; +export type CommentDeleteManyArgs = $DeleteManyArgs<$Schema, "Comment">; +export type CommentCountArgs = $CountArgs<$Schema, "Comment">; +export type CommentAggregateArgs = $AggregateArgs<$Schema, "Comment">; +export type CommentGroupByArgs = $GroupByArgs<$Schema, "Comment">; +export type CommentWhereInput = $WhereInput<$Schema, "Comment">; +export type CommentSelect = $SelectInput<$Schema, "Comment">; +export type CommentInclude = $IncludeInput<$Schema, "Comment">; +export type CommentOmit = $OmitInput<$Schema, "Comment">; +export type CommentUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Comment">; +export type CommentCheckedCreateInput = $CheckedCreateInput<$Schema, "Comment">; +export type CommentUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Comment">; +export type CommentCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Comment">; +export type CommentGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Comment", Args, Options>; +export type ReactionFindManyArgs = $FindManyArgs<$Schema, "Reaction">; +export type ReactionFindUniqueArgs = $FindUniqueArgs<$Schema, "Reaction">; +export type ReactionFindFirstArgs = $FindFirstArgs<$Schema, "Reaction">; +export type ReactionExistsArgs = $ExistsArgs<$Schema, "Reaction">; +export type ReactionCreateArgs = $CreateArgs<$Schema, "Reaction">; +export type ReactionCreateManyArgs = $CreateManyArgs<$Schema, "Reaction">; +export type ReactionCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Reaction">; +export type ReactionUpdateArgs = $UpdateArgs<$Schema, "Reaction">; +export type ReactionUpdateManyArgs = $UpdateManyArgs<$Schema, "Reaction">; +export type ReactionUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Reaction">; +export type ReactionUpsertArgs = $UpsertArgs<$Schema, "Reaction">; +export type ReactionDeleteArgs = $DeleteArgs<$Schema, "Reaction">; +export type ReactionDeleteManyArgs = $DeleteManyArgs<$Schema, "Reaction">; +export type ReactionCountArgs = $CountArgs<$Schema, "Reaction">; +export type ReactionAggregateArgs = $AggregateArgs<$Schema, "Reaction">; +export type ReactionGroupByArgs = $GroupByArgs<$Schema, "Reaction">; +export type ReactionWhereInput = $WhereInput<$Schema, "Reaction">; +export type ReactionSelect = $SelectInput<$Schema, "Reaction">; +export type ReactionInclude = $IncludeInput<$Schema, "Reaction">; +export type ReactionOmit = $OmitInput<$Schema, "Reaction">; +export type ReactionUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Reaction">; +export type ReactionCheckedCreateInput = $CheckedCreateInput<$Schema, "Reaction">; +export type ReactionUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Reaction">; +export type ReactionCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Reaction">; +export type ReactionGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Reaction", Args, Options>; +export type AttachmentFindManyArgs = $FindManyArgs<$Schema, "Attachment">; +export type AttachmentFindUniqueArgs = $FindUniqueArgs<$Schema, "Attachment">; +export type AttachmentFindFirstArgs = $FindFirstArgs<$Schema, "Attachment">; +export type AttachmentExistsArgs = $ExistsArgs<$Schema, "Attachment">; +export type AttachmentCreateArgs = $CreateArgs<$Schema, "Attachment">; +export type AttachmentCreateManyArgs = $CreateManyArgs<$Schema, "Attachment">; +export type AttachmentCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Attachment">; +export type AttachmentUpdateArgs = $UpdateArgs<$Schema, "Attachment">; +export type AttachmentUpdateManyArgs = $UpdateManyArgs<$Schema, "Attachment">; +export type AttachmentUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Attachment">; +export type AttachmentUpsertArgs = $UpsertArgs<$Schema, "Attachment">; +export type AttachmentDeleteArgs = $DeleteArgs<$Schema, "Attachment">; +export type AttachmentDeleteManyArgs = $DeleteManyArgs<$Schema, "Attachment">; +export type AttachmentCountArgs = $CountArgs<$Schema, "Attachment">; +export type AttachmentAggregateArgs = $AggregateArgs<$Schema, "Attachment">; +export type AttachmentGroupByArgs = $GroupByArgs<$Schema, "Attachment">; +export type AttachmentWhereInput = $WhereInput<$Schema, "Attachment">; +export type AttachmentSelect = $SelectInput<$Schema, "Attachment">; +export type AttachmentInclude = $IncludeInput<$Schema, "Attachment">; +export type AttachmentOmit = $OmitInput<$Schema, "Attachment">; +export type AttachmentUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Attachment">; +export type AttachmentCheckedCreateInput = $CheckedCreateInput<$Schema, "Attachment">; +export type AttachmentUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Attachment">; +export type AttachmentCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Attachment">; +export type AttachmentGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Attachment", Args, Options>; +export type FileAttachmentFindManyArgs = $FindManyArgs<$Schema, "FileAttachment">; +export type FileAttachmentFindUniqueArgs = $FindUniqueArgs<$Schema, "FileAttachment">; +export type FileAttachmentFindFirstArgs = $FindFirstArgs<$Schema, "FileAttachment">; +export type FileAttachmentExistsArgs = $ExistsArgs<$Schema, "FileAttachment">; +export type FileAttachmentCreateArgs = $CreateArgs<$Schema, "FileAttachment">; +export type FileAttachmentCreateManyArgs = $CreateManyArgs<$Schema, "FileAttachment">; +export type FileAttachmentCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "FileAttachment">; +export type FileAttachmentUpdateArgs = $UpdateArgs<$Schema, "FileAttachment">; +export type FileAttachmentUpdateManyArgs = $UpdateManyArgs<$Schema, "FileAttachment">; +export type FileAttachmentUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "FileAttachment">; +export type FileAttachmentUpsertArgs = $UpsertArgs<$Schema, "FileAttachment">; +export type FileAttachmentDeleteArgs = $DeleteArgs<$Schema, "FileAttachment">; +export type FileAttachmentDeleteManyArgs = $DeleteManyArgs<$Schema, "FileAttachment">; +export type FileAttachmentCountArgs = $CountArgs<$Schema, "FileAttachment">; +export type FileAttachmentAggregateArgs = $AggregateArgs<$Schema, "FileAttachment">; +export type FileAttachmentGroupByArgs = $GroupByArgs<$Schema, "FileAttachment">; +export type FileAttachmentWhereInput = $WhereInput<$Schema, "FileAttachment">; +export type FileAttachmentSelect = $SelectInput<$Schema, "FileAttachment">; +export type FileAttachmentInclude = $IncludeInput<$Schema, "FileAttachment">; +export type FileAttachmentOmit = $OmitInput<$Schema, "FileAttachment">; +export type FileAttachmentUncheckedCreateInput = $UncheckedCreateInput<$Schema, "FileAttachment">; +export type FileAttachmentCheckedCreateInput = $CheckedCreateInput<$Schema, "FileAttachment">; +export type FileAttachmentUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "FileAttachment">; +export type FileAttachmentCheckedUpdateInput = $CheckedUpdateInput<$Schema, "FileAttachment">; +export type FileAttachmentGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "FileAttachment", Args, Options>; +export type ImageAttachmentFindManyArgs = $FindManyArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentFindUniqueArgs = $FindUniqueArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentFindFirstArgs = $FindFirstArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentExistsArgs = $ExistsArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentCreateArgs = $CreateArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentCreateManyArgs = $CreateManyArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentUpdateArgs = $UpdateArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentUpdateManyArgs = $UpdateManyArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentUpsertArgs = $UpsertArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentDeleteArgs = $DeleteArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentDeleteManyArgs = $DeleteManyArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentCountArgs = $CountArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentAggregateArgs = $AggregateArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentGroupByArgs = $GroupByArgs<$Schema, "ImageAttachment">; +export type ImageAttachmentWhereInput = $WhereInput<$Schema, "ImageAttachment">; +export type ImageAttachmentSelect = $SelectInput<$Schema, "ImageAttachment">; +export type ImageAttachmentInclude = $IncludeInput<$Schema, "ImageAttachment">; +export type ImageAttachmentOmit = $OmitInput<$Schema, "ImageAttachment">; +export type ImageAttachmentUncheckedCreateInput = $UncheckedCreateInput<$Schema, "ImageAttachment">; +export type ImageAttachmentCheckedCreateInput = $CheckedCreateInput<$Schema, "ImageAttachment">; +export type ImageAttachmentUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "ImageAttachment">; +export type ImageAttachmentCheckedUpdateInput = $CheckedUpdateInput<$Schema, "ImageAttachment">; +export type ImageAttachmentGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "ImageAttachment", Args, Options>; +export type LinkAttachmentFindManyArgs = $FindManyArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentFindUniqueArgs = $FindUniqueArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentFindFirstArgs = $FindFirstArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentExistsArgs = $ExistsArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentCreateArgs = $CreateArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentCreateManyArgs = $CreateManyArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentUpdateArgs = $UpdateArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentUpdateManyArgs = $UpdateManyArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentUpsertArgs = $UpsertArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentDeleteArgs = $DeleteArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentDeleteManyArgs = $DeleteManyArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentCountArgs = $CountArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentAggregateArgs = $AggregateArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentGroupByArgs = $GroupByArgs<$Schema, "LinkAttachment">; +export type LinkAttachmentWhereInput = $WhereInput<$Schema, "LinkAttachment">; +export type LinkAttachmentSelect = $SelectInput<$Schema, "LinkAttachment">; +export type LinkAttachmentInclude = $IncludeInput<$Schema, "LinkAttachment">; +export type LinkAttachmentOmit = $OmitInput<$Schema, "LinkAttachment">; +export type LinkAttachmentUncheckedCreateInput = $UncheckedCreateInput<$Schema, "LinkAttachment">; +export type LinkAttachmentCheckedCreateInput = $CheckedCreateInput<$Schema, "LinkAttachment">; +export type LinkAttachmentUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "LinkAttachment">; +export type LinkAttachmentCheckedUpdateInput = $CheckedUpdateInput<$Schema, "LinkAttachment">; +export type LinkAttachmentGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "LinkAttachment", Args, Options>; +export type DocumentFindManyArgs = $FindManyArgs<$Schema, "Document">; +export type DocumentFindUniqueArgs = $FindUniqueArgs<$Schema, "Document">; +export type DocumentFindFirstArgs = $FindFirstArgs<$Schema, "Document">; +export type DocumentExistsArgs = $ExistsArgs<$Schema, "Document">; +export type DocumentCreateArgs = $CreateArgs<$Schema, "Document">; +export type DocumentCreateManyArgs = $CreateManyArgs<$Schema, "Document">; +export type DocumentCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Document">; +export type DocumentUpdateArgs = $UpdateArgs<$Schema, "Document">; +export type DocumentUpdateManyArgs = $UpdateManyArgs<$Schema, "Document">; +export type DocumentUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Document">; +export type DocumentUpsertArgs = $UpsertArgs<$Schema, "Document">; +export type DocumentDeleteArgs = $DeleteArgs<$Schema, "Document">; +export type DocumentDeleteManyArgs = $DeleteManyArgs<$Schema, "Document">; +export type DocumentCountArgs = $CountArgs<$Schema, "Document">; +export type DocumentAggregateArgs = $AggregateArgs<$Schema, "Document">; +export type DocumentGroupByArgs = $GroupByArgs<$Schema, "Document">; +export type DocumentWhereInput = $WhereInput<$Schema, "Document">; +export type DocumentSelect = $SelectInput<$Schema, "Document">; +export type DocumentInclude = $IncludeInput<$Schema, "Document">; +export type DocumentOmit = $OmitInput<$Schema, "Document">; +export type DocumentUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Document">; +export type DocumentCheckedCreateInput = $CheckedCreateInput<$Schema, "Document">; +export type DocumentUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Document">; +export type DocumentCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Document">; +export type DocumentGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Document", Args, Options>; +export type CustomFieldFindManyArgs = $FindManyArgs<$Schema, "CustomField">; +export type CustomFieldFindUniqueArgs = $FindUniqueArgs<$Schema, "CustomField">; +export type CustomFieldFindFirstArgs = $FindFirstArgs<$Schema, "CustomField">; +export type CustomFieldExistsArgs = $ExistsArgs<$Schema, "CustomField">; +export type CustomFieldCreateArgs = $CreateArgs<$Schema, "CustomField">; +export type CustomFieldCreateManyArgs = $CreateManyArgs<$Schema, "CustomField">; +export type CustomFieldCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "CustomField">; +export type CustomFieldUpdateArgs = $UpdateArgs<$Schema, "CustomField">; +export type CustomFieldUpdateManyArgs = $UpdateManyArgs<$Schema, "CustomField">; +export type CustomFieldUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "CustomField">; +export type CustomFieldUpsertArgs = $UpsertArgs<$Schema, "CustomField">; +export type CustomFieldDeleteArgs = $DeleteArgs<$Schema, "CustomField">; +export type CustomFieldDeleteManyArgs = $DeleteManyArgs<$Schema, "CustomField">; +export type CustomFieldCountArgs = $CountArgs<$Schema, "CustomField">; +export type CustomFieldAggregateArgs = $AggregateArgs<$Schema, "CustomField">; +export type CustomFieldGroupByArgs = $GroupByArgs<$Schema, "CustomField">; +export type CustomFieldWhereInput = $WhereInput<$Schema, "CustomField">; +export type CustomFieldSelect = $SelectInput<$Schema, "CustomField">; +export type CustomFieldInclude = $IncludeInput<$Schema, "CustomField">; +export type CustomFieldOmit = $OmitInput<$Schema, "CustomField">; +export type CustomFieldUncheckedCreateInput = $UncheckedCreateInput<$Schema, "CustomField">; +export type CustomFieldCheckedCreateInput = $CheckedCreateInput<$Schema, "CustomField">; +export type CustomFieldUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "CustomField">; +export type CustomFieldCheckedUpdateInput = $CheckedUpdateInput<$Schema, "CustomField">; +export type CustomFieldGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "CustomField", Args, Options>; +export type CustomFieldValueFindManyArgs = $FindManyArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueFindUniqueArgs = $FindUniqueArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueFindFirstArgs = $FindFirstArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueExistsArgs = $ExistsArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueCreateArgs = $CreateArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueCreateManyArgs = $CreateManyArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueUpdateArgs = $UpdateArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueUpdateManyArgs = $UpdateManyArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueUpsertArgs = $UpsertArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueDeleteArgs = $DeleteArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueDeleteManyArgs = $DeleteManyArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueCountArgs = $CountArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueAggregateArgs = $AggregateArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueGroupByArgs = $GroupByArgs<$Schema, "CustomFieldValue">; +export type CustomFieldValueWhereInput = $WhereInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueSelect = $SelectInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueInclude = $IncludeInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueOmit = $OmitInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueUncheckedCreateInput = $UncheckedCreateInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueCheckedCreateInput = $CheckedCreateInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueCheckedUpdateInput = $CheckedUpdateInput<$Schema, "CustomFieldValue">; +export type CustomFieldValueGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "CustomFieldValue", Args, Options>; +export type TimeEntryFindManyArgs = $FindManyArgs<$Schema, "TimeEntry">; +export type TimeEntryFindUniqueArgs = $FindUniqueArgs<$Schema, "TimeEntry">; +export type TimeEntryFindFirstArgs = $FindFirstArgs<$Schema, "TimeEntry">; +export type TimeEntryExistsArgs = $ExistsArgs<$Schema, "TimeEntry">; +export type TimeEntryCreateArgs = $CreateArgs<$Schema, "TimeEntry">; +export type TimeEntryCreateManyArgs = $CreateManyArgs<$Schema, "TimeEntry">; +export type TimeEntryCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "TimeEntry">; +export type TimeEntryUpdateArgs = $UpdateArgs<$Schema, "TimeEntry">; +export type TimeEntryUpdateManyArgs = $UpdateManyArgs<$Schema, "TimeEntry">; +export type TimeEntryUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "TimeEntry">; +export type TimeEntryUpsertArgs = $UpsertArgs<$Schema, "TimeEntry">; +export type TimeEntryDeleteArgs = $DeleteArgs<$Schema, "TimeEntry">; +export type TimeEntryDeleteManyArgs = $DeleteManyArgs<$Schema, "TimeEntry">; +export type TimeEntryCountArgs = $CountArgs<$Schema, "TimeEntry">; +export type TimeEntryAggregateArgs = $AggregateArgs<$Schema, "TimeEntry">; +export type TimeEntryGroupByArgs = $GroupByArgs<$Schema, "TimeEntry">; +export type TimeEntryWhereInput = $WhereInput<$Schema, "TimeEntry">; +export type TimeEntrySelect = $SelectInput<$Schema, "TimeEntry">; +export type TimeEntryInclude = $IncludeInput<$Schema, "TimeEntry">; +export type TimeEntryOmit = $OmitInput<$Schema, "TimeEntry">; +export type TimeEntryUncheckedCreateInput = $UncheckedCreateInput<$Schema, "TimeEntry">; +export type TimeEntryCheckedCreateInput = $CheckedCreateInput<$Schema, "TimeEntry">; +export type TimeEntryUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "TimeEntry">; +export type TimeEntryCheckedUpdateInput = $CheckedUpdateInput<$Schema, "TimeEntry">; +export type TimeEntryGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "TimeEntry", Args, Options>; +export type WebhookFindManyArgs = $FindManyArgs<$Schema, "Webhook">; +export type WebhookFindUniqueArgs = $FindUniqueArgs<$Schema, "Webhook">; +export type WebhookFindFirstArgs = $FindFirstArgs<$Schema, "Webhook">; +export type WebhookExistsArgs = $ExistsArgs<$Schema, "Webhook">; +export type WebhookCreateArgs = $CreateArgs<$Schema, "Webhook">; +export type WebhookCreateManyArgs = $CreateManyArgs<$Schema, "Webhook">; +export type WebhookCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Webhook">; +export type WebhookUpdateArgs = $UpdateArgs<$Schema, "Webhook">; +export type WebhookUpdateManyArgs = $UpdateManyArgs<$Schema, "Webhook">; +export type WebhookUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Webhook">; +export type WebhookUpsertArgs = $UpsertArgs<$Schema, "Webhook">; +export type WebhookDeleteArgs = $DeleteArgs<$Schema, "Webhook">; +export type WebhookDeleteManyArgs = $DeleteManyArgs<$Schema, "Webhook">; +export type WebhookCountArgs = $CountArgs<$Schema, "Webhook">; +export type WebhookAggregateArgs = $AggregateArgs<$Schema, "Webhook">; +export type WebhookGroupByArgs = $GroupByArgs<$Schema, "Webhook">; +export type WebhookWhereInput = $WhereInput<$Schema, "Webhook">; +export type WebhookSelect = $SelectInput<$Schema, "Webhook">; +export type WebhookInclude = $IncludeInput<$Schema, "Webhook">; +export type WebhookOmit = $OmitInput<$Schema, "Webhook">; +export type WebhookUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Webhook">; +export type WebhookCheckedCreateInput = $CheckedCreateInput<$Schema, "Webhook">; +export type WebhookUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Webhook">; +export type WebhookCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Webhook">; +export type WebhookGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Webhook", Args, Options>; +export type IntegrationFindManyArgs = $FindManyArgs<$Schema, "Integration">; +export type IntegrationFindUniqueArgs = $FindUniqueArgs<$Schema, "Integration">; +export type IntegrationFindFirstArgs = $FindFirstArgs<$Schema, "Integration">; +export type IntegrationExistsArgs = $ExistsArgs<$Schema, "Integration">; +export type IntegrationCreateArgs = $CreateArgs<$Schema, "Integration">; +export type IntegrationCreateManyArgs = $CreateManyArgs<$Schema, "Integration">; +export type IntegrationCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Integration">; +export type IntegrationUpdateArgs = $UpdateArgs<$Schema, "Integration">; +export type IntegrationUpdateManyArgs = $UpdateManyArgs<$Schema, "Integration">; +export type IntegrationUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Integration">; +export type IntegrationUpsertArgs = $UpsertArgs<$Schema, "Integration">; +export type IntegrationDeleteArgs = $DeleteArgs<$Schema, "Integration">; +export type IntegrationDeleteManyArgs = $DeleteManyArgs<$Schema, "Integration">; +export type IntegrationCountArgs = $CountArgs<$Schema, "Integration">; +export type IntegrationAggregateArgs = $AggregateArgs<$Schema, "Integration">; +export type IntegrationGroupByArgs = $GroupByArgs<$Schema, "Integration">; +export type IntegrationWhereInput = $WhereInput<$Schema, "Integration">; +export type IntegrationSelect = $SelectInput<$Schema, "Integration">; +export type IntegrationInclude = $IncludeInput<$Schema, "Integration">; +export type IntegrationOmit = $OmitInput<$Schema, "Integration">; +export type IntegrationUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Integration">; +export type IntegrationCheckedCreateInput = $CheckedCreateInput<$Schema, "Integration">; +export type IntegrationUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Integration">; +export type IntegrationCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Integration">; +export type IntegrationGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Integration", Args, Options>; +export type ApiTokenFindManyArgs = $FindManyArgs<$Schema, "ApiToken">; +export type ApiTokenFindUniqueArgs = $FindUniqueArgs<$Schema, "ApiToken">; +export type ApiTokenFindFirstArgs = $FindFirstArgs<$Schema, "ApiToken">; +export type ApiTokenExistsArgs = $ExistsArgs<$Schema, "ApiToken">; +export type ApiTokenCreateArgs = $CreateArgs<$Schema, "ApiToken">; +export type ApiTokenCreateManyArgs = $CreateManyArgs<$Schema, "ApiToken">; +export type ApiTokenCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "ApiToken">; +export type ApiTokenUpdateArgs = $UpdateArgs<$Schema, "ApiToken">; +export type ApiTokenUpdateManyArgs = $UpdateManyArgs<$Schema, "ApiToken">; +export type ApiTokenUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "ApiToken">; +export type ApiTokenUpsertArgs = $UpsertArgs<$Schema, "ApiToken">; +export type ApiTokenDeleteArgs = $DeleteArgs<$Schema, "ApiToken">; +export type ApiTokenDeleteManyArgs = $DeleteManyArgs<$Schema, "ApiToken">; +export type ApiTokenCountArgs = $CountArgs<$Schema, "ApiToken">; +export type ApiTokenAggregateArgs = $AggregateArgs<$Schema, "ApiToken">; +export type ApiTokenGroupByArgs = $GroupByArgs<$Schema, "ApiToken">; +export type ApiTokenWhereInput = $WhereInput<$Schema, "ApiToken">; +export type ApiTokenSelect = $SelectInput<$Schema, "ApiToken">; +export type ApiTokenInclude = $IncludeInput<$Schema, "ApiToken">; +export type ApiTokenOmit = $OmitInput<$Schema, "ApiToken">; +export type ApiTokenUncheckedCreateInput = $UncheckedCreateInput<$Schema, "ApiToken">; +export type ApiTokenCheckedCreateInput = $CheckedCreateInput<$Schema, "ApiToken">; +export type ApiTokenUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "ApiToken">; +export type ApiTokenCheckedUpdateInput = $CheckedUpdateInput<$Schema, "ApiToken">; +export type ApiTokenGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "ApiToken", Args, Options>; +export type ActivityLogFindManyArgs = $FindManyArgs<$Schema, "ActivityLog">; +export type ActivityLogFindUniqueArgs = $FindUniqueArgs<$Schema, "ActivityLog">; +export type ActivityLogFindFirstArgs = $FindFirstArgs<$Schema, "ActivityLog">; +export type ActivityLogExistsArgs = $ExistsArgs<$Schema, "ActivityLog">; +export type ActivityLogCreateArgs = $CreateArgs<$Schema, "ActivityLog">; +export type ActivityLogCreateManyArgs = $CreateManyArgs<$Schema, "ActivityLog">; +export type ActivityLogCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "ActivityLog">; +export type ActivityLogUpdateArgs = $UpdateArgs<$Schema, "ActivityLog">; +export type ActivityLogUpdateManyArgs = $UpdateManyArgs<$Schema, "ActivityLog">; +export type ActivityLogUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "ActivityLog">; +export type ActivityLogUpsertArgs = $UpsertArgs<$Schema, "ActivityLog">; +export type ActivityLogDeleteArgs = $DeleteArgs<$Schema, "ActivityLog">; +export type ActivityLogDeleteManyArgs = $DeleteManyArgs<$Schema, "ActivityLog">; +export type ActivityLogCountArgs = $CountArgs<$Schema, "ActivityLog">; +export type ActivityLogAggregateArgs = $AggregateArgs<$Schema, "ActivityLog">; +export type ActivityLogGroupByArgs = $GroupByArgs<$Schema, "ActivityLog">; +export type ActivityLogWhereInput = $WhereInput<$Schema, "ActivityLog">; +export type ActivityLogSelect = $SelectInput<$Schema, "ActivityLog">; +export type ActivityLogInclude = $IncludeInput<$Schema, "ActivityLog">; +export type ActivityLogOmit = $OmitInput<$Schema, "ActivityLog">; +export type ActivityLogUncheckedCreateInput = $UncheckedCreateInput<$Schema, "ActivityLog">; +export type ActivityLogCheckedCreateInput = $CheckedCreateInput<$Schema, "ActivityLog">; +export type ActivityLogUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "ActivityLog">; +export type ActivityLogCheckedUpdateInput = $CheckedUpdateInput<$Schema, "ActivityLog">; +export type ActivityLogGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "ActivityLog", Args, Options>; +export type NotificationFindManyArgs = $FindManyArgs<$Schema, "Notification">; +export type NotificationFindUniqueArgs = $FindUniqueArgs<$Schema, "Notification">; +export type NotificationFindFirstArgs = $FindFirstArgs<$Schema, "Notification">; +export type NotificationExistsArgs = $ExistsArgs<$Schema, "Notification">; +export type NotificationCreateArgs = $CreateArgs<$Schema, "Notification">; +export type NotificationCreateManyArgs = $CreateManyArgs<$Schema, "Notification">; +export type NotificationCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Notification">; +export type NotificationUpdateArgs = $UpdateArgs<$Schema, "Notification">; +export type NotificationUpdateManyArgs = $UpdateManyArgs<$Schema, "Notification">; +export type NotificationUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Notification">; +export type NotificationUpsertArgs = $UpsertArgs<$Schema, "Notification">; +export type NotificationDeleteArgs = $DeleteArgs<$Schema, "Notification">; +export type NotificationDeleteManyArgs = $DeleteManyArgs<$Schema, "Notification">; +export type NotificationCountArgs = $CountArgs<$Schema, "Notification">; +export type NotificationAggregateArgs = $AggregateArgs<$Schema, "Notification">; +export type NotificationGroupByArgs = $GroupByArgs<$Schema, "Notification">; +export type NotificationWhereInput = $WhereInput<$Schema, "Notification">; +export type NotificationSelect = $SelectInput<$Schema, "Notification">; +export type NotificationInclude = $IncludeInput<$Schema, "Notification">; +export type NotificationOmit = $OmitInput<$Schema, "Notification">; +export type NotificationUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Notification">; +export type NotificationCheckedCreateInput = $CheckedCreateInput<$Schema, "Notification">; +export type NotificationUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Notification">; +export type NotificationCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Notification">; +export type NotificationGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Notification", Args, Options>; +export type BillingCustomerFindManyArgs = $FindManyArgs<$Schema, "BillingCustomer">; +export type BillingCustomerFindUniqueArgs = $FindUniqueArgs<$Schema, "BillingCustomer">; +export type BillingCustomerFindFirstArgs = $FindFirstArgs<$Schema, "BillingCustomer">; +export type BillingCustomerExistsArgs = $ExistsArgs<$Schema, "BillingCustomer">; +export type BillingCustomerCreateArgs = $CreateArgs<$Schema, "BillingCustomer">; +export type BillingCustomerCreateManyArgs = $CreateManyArgs<$Schema, "BillingCustomer">; +export type BillingCustomerCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "BillingCustomer">; +export type BillingCustomerUpdateArgs = $UpdateArgs<$Schema, "BillingCustomer">; +export type BillingCustomerUpdateManyArgs = $UpdateManyArgs<$Schema, "BillingCustomer">; +export type BillingCustomerUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "BillingCustomer">; +export type BillingCustomerUpsertArgs = $UpsertArgs<$Schema, "BillingCustomer">; +export type BillingCustomerDeleteArgs = $DeleteArgs<$Schema, "BillingCustomer">; +export type BillingCustomerDeleteManyArgs = $DeleteManyArgs<$Schema, "BillingCustomer">; +export type BillingCustomerCountArgs = $CountArgs<$Schema, "BillingCustomer">; +export type BillingCustomerAggregateArgs = $AggregateArgs<$Schema, "BillingCustomer">; +export type BillingCustomerGroupByArgs = $GroupByArgs<$Schema, "BillingCustomer">; +export type BillingCustomerWhereInput = $WhereInput<$Schema, "BillingCustomer">; +export type BillingCustomerSelect = $SelectInput<$Schema, "BillingCustomer">; +export type BillingCustomerInclude = $IncludeInput<$Schema, "BillingCustomer">; +export type BillingCustomerOmit = $OmitInput<$Schema, "BillingCustomer">; +export type BillingCustomerUncheckedCreateInput = $UncheckedCreateInput<$Schema, "BillingCustomer">; +export type BillingCustomerCheckedCreateInput = $CheckedCreateInput<$Schema, "BillingCustomer">; +export type BillingCustomerUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "BillingCustomer">; +export type BillingCustomerCheckedUpdateInput = $CheckedUpdateInput<$Schema, "BillingCustomer">; +export type BillingCustomerGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "BillingCustomer", Args, Options>; +export type PlanFindManyArgs = $FindManyArgs<$Schema, "Plan">; +export type PlanFindUniqueArgs = $FindUniqueArgs<$Schema, "Plan">; +export type PlanFindFirstArgs = $FindFirstArgs<$Schema, "Plan">; +export type PlanExistsArgs = $ExistsArgs<$Schema, "Plan">; +export type PlanCreateArgs = $CreateArgs<$Schema, "Plan">; +export type PlanCreateManyArgs = $CreateManyArgs<$Schema, "Plan">; +export type PlanCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Plan">; +export type PlanUpdateArgs = $UpdateArgs<$Schema, "Plan">; +export type PlanUpdateManyArgs = $UpdateManyArgs<$Schema, "Plan">; +export type PlanUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Plan">; +export type PlanUpsertArgs = $UpsertArgs<$Schema, "Plan">; +export type PlanDeleteArgs = $DeleteArgs<$Schema, "Plan">; +export type PlanDeleteManyArgs = $DeleteManyArgs<$Schema, "Plan">; +export type PlanCountArgs = $CountArgs<$Schema, "Plan">; +export type PlanAggregateArgs = $AggregateArgs<$Schema, "Plan">; +export type PlanGroupByArgs = $GroupByArgs<$Schema, "Plan">; +export type PlanWhereInput = $WhereInput<$Schema, "Plan">; +export type PlanSelect = $SelectInput<$Schema, "Plan">; +export type PlanInclude = $IncludeInput<$Schema, "Plan">; +export type PlanOmit = $OmitInput<$Schema, "Plan">; +export type PlanUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Plan">; +export type PlanCheckedCreateInput = $CheckedCreateInput<$Schema, "Plan">; +export type PlanUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Plan">; +export type PlanCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Plan">; +export type PlanGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Plan", Args, Options>; +export type SubscriptionFindManyArgs = $FindManyArgs<$Schema, "Subscription">; +export type SubscriptionFindUniqueArgs = $FindUniqueArgs<$Schema, "Subscription">; +export type SubscriptionFindFirstArgs = $FindFirstArgs<$Schema, "Subscription">; +export type SubscriptionExistsArgs = $ExistsArgs<$Schema, "Subscription">; +export type SubscriptionCreateArgs = $CreateArgs<$Schema, "Subscription">; +export type SubscriptionCreateManyArgs = $CreateManyArgs<$Schema, "Subscription">; +export type SubscriptionCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Subscription">; +export type SubscriptionUpdateArgs = $UpdateArgs<$Schema, "Subscription">; +export type SubscriptionUpdateManyArgs = $UpdateManyArgs<$Schema, "Subscription">; +export type SubscriptionUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Subscription">; +export type SubscriptionUpsertArgs = $UpsertArgs<$Schema, "Subscription">; +export type SubscriptionDeleteArgs = $DeleteArgs<$Schema, "Subscription">; +export type SubscriptionDeleteManyArgs = $DeleteManyArgs<$Schema, "Subscription">; +export type SubscriptionCountArgs = $CountArgs<$Schema, "Subscription">; +export type SubscriptionAggregateArgs = $AggregateArgs<$Schema, "Subscription">; +export type SubscriptionGroupByArgs = $GroupByArgs<$Schema, "Subscription">; +export type SubscriptionWhereInput = $WhereInput<$Schema, "Subscription">; +export type SubscriptionSelect = $SelectInput<$Schema, "Subscription">; +export type SubscriptionInclude = $IncludeInput<$Schema, "Subscription">; +export type SubscriptionOmit = $OmitInput<$Schema, "Subscription">; +export type SubscriptionUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Subscription">; +export type SubscriptionCheckedCreateInput = $CheckedCreateInput<$Schema, "Subscription">; +export type SubscriptionUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Subscription">; +export type SubscriptionCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Subscription">; +export type SubscriptionGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Subscription", Args, Options>; +export type InvoiceFindManyArgs = $FindManyArgs<$Schema, "Invoice">; +export type InvoiceFindUniqueArgs = $FindUniqueArgs<$Schema, "Invoice">; +export type InvoiceFindFirstArgs = $FindFirstArgs<$Schema, "Invoice">; +export type InvoiceExistsArgs = $ExistsArgs<$Schema, "Invoice">; +export type InvoiceCreateArgs = $CreateArgs<$Schema, "Invoice">; +export type InvoiceCreateManyArgs = $CreateManyArgs<$Schema, "Invoice">; +export type InvoiceCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Invoice">; +export type InvoiceUpdateArgs = $UpdateArgs<$Schema, "Invoice">; +export type InvoiceUpdateManyArgs = $UpdateManyArgs<$Schema, "Invoice">; +export type InvoiceUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Invoice">; +export type InvoiceUpsertArgs = $UpsertArgs<$Schema, "Invoice">; +export type InvoiceDeleteArgs = $DeleteArgs<$Schema, "Invoice">; +export type InvoiceDeleteManyArgs = $DeleteManyArgs<$Schema, "Invoice">; +export type InvoiceCountArgs = $CountArgs<$Schema, "Invoice">; +export type InvoiceAggregateArgs = $AggregateArgs<$Schema, "Invoice">; +export type InvoiceGroupByArgs = $GroupByArgs<$Schema, "Invoice">; +export type InvoiceWhereInput = $WhereInput<$Schema, "Invoice">; +export type InvoiceSelect = $SelectInput<$Schema, "Invoice">; +export type InvoiceInclude = $IncludeInput<$Schema, "Invoice">; +export type InvoiceOmit = $OmitInput<$Schema, "Invoice">; +export type InvoiceUncheckedCreateInput = $UncheckedCreateInput<$Schema, "Invoice">; +export type InvoiceCheckedCreateInput = $CheckedCreateInput<$Schema, "Invoice">; +export type InvoiceUncheckedUpdateInput = $UncheckedUpdateInput<$Schema, "Invoice">; +export type InvoiceCheckedUpdateInput = $CheckedUpdateInput<$Schema, "Invoice">; +export type InvoiceGetPayload, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Invoice", Args, Options>; diff --git a/samples/taskforge/zenstack/models.ts b/samples/taskforge/zenstack/models.ts new file mode 100644 index 000000000..0504c8566 --- /dev/null +++ b/samples/taskforge/zenstack/models.ts @@ -0,0 +1,102 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { schema as $schema, type SchemaType as $Schema } from "./schema"; +import type { ModelResult as $ModelResult, TypeDefResult as $TypeDefResult } from "@zenstackhq/orm"; +export type User = $ModelResult<$Schema, "User">; +export type Account = $ModelResult<$Schema, "Account">; +export type Session = $ModelResult<$Schema, "Session">; +export type Verification = $ModelResult<$Schema, "Verification">; +export type Organization = $ModelResult<$Schema, "Organization">; +/** + * Explicit many-to-many join between User and Organization, carrying a role. + */ +export type Membership = $ModelResult<$Schema, "Membership">; +export type Invitation = $ModelResult<$Schema, "Invitation">; +export type Team = $ModelResult<$Schema, "Team">; +/** + * Explicit many-to-many join between User and Team. + */ +export type TeamMembership = $ModelResult<$Schema, "TeamMembership">; +export type Project = $ModelResult<$Schema, "Project">; +/** + * Explicit many-to-many join between User and Project, carrying a role. + */ +export type ProjectMember = $ModelResult<$Schema, "ProjectMember">; +export type Issue = $ModelResult<$Schema, "Issue">; +export type Label = $ModelResult<$Schema, "Label">; +export type Milestone = $ModelResult<$Schema, "Milestone">; +export type Sprint = $ModelResult<$Schema, "Sprint">; +export type Comment = $ModelResult<$Schema, "Comment">; +export type Reaction = $ModelResult<$Schema, "Reaction">; +/** + * Polymorphic base. Concrete subtypes are FileAttachment / ImageAttachment / LinkAttachment. + * Create the concrete models, never `Attachment` directly. + */ +export type Attachment = $ModelResult<$Schema, "Attachment">; +export type FileAttachment = $ModelResult<$Schema, "FileAttachment">; +export type ImageAttachment = $ModelResult<$Schema, "ImageAttachment">; +export type LinkAttachment = $ModelResult<$Schema, "LinkAttachment">; +export type Document = $ModelResult<$Schema, "Document">; +export type CustomField = $ModelResult<$Schema, "CustomField">; +/** + * Explicit many-to-many join between CustomField and Issue, carrying a value. + */ +export type CustomFieldValue = $ModelResult<$Schema, "CustomFieldValue">; +export type TimeEntry = $ModelResult<$Schema, "TimeEntry">; +export type Webhook = $ModelResult<$Schema, "Webhook">; +export type Integration = $ModelResult<$Schema, "Integration">; +export type ApiToken = $ModelResult<$Schema, "ApiToken">; +export type ActivityLog = $ModelResult<$Schema, "ActivityLog">; +export type Notification = $ModelResult<$Schema, "Notification">; +export type BillingCustomer = $ModelResult<$Schema, "BillingCustomer">; +export type Plan = $ModelResult<$Schema, "Plan">; +export type Subscription = $ModelResult<$Schema, "Subscription">; +export type Invoice = $ModelResult<$Schema, "Invoice">; +export type Timestamps = $TypeDefResult<$Schema, "Timestamps">; +/** + * Strongly-typed JSON payload for Issue.metadata. + */ +export type IssueMetadata = $TypeDefResult<$Schema, "IssueMetadata">; +/** + * Strongly-typed JSON payload for Document.content. + */ +export type DocContent = $TypeDefResult<$Schema, "DocContent">; +/** + * Strongly-typed JSON payload for Webhook.config. + */ +export type WebhookConfig = $TypeDefResult<$Schema, "WebhookConfig">; +/** + * Strongly-typed JSON payload for Integration.config. + */ +export type IntegrationConfig = $TypeDefResult<$Schema, "IntegrationConfig">; +export const OrgRole = $schema.enums.OrgRole.values; +export type OrgRole = (typeof OrgRole)[keyof typeof OrgRole]; +export const ProjectRole = $schema.enums.ProjectRole.values; +export type ProjectRole = (typeof ProjectRole)[keyof typeof ProjectRole]; +export const IssueStatus = $schema.enums.IssueStatus.values; +export type IssueStatus = (typeof IssueStatus)[keyof typeof IssueStatus]; +export const Priority = $schema.enums.Priority.values; +export type Priority = (typeof Priority)[keyof typeof Priority]; +export const SprintStatus = $schema.enums.SprintStatus.values; +export type SprintStatus = (typeof SprintStatus)[keyof typeof SprintStatus]; +export const MilestoneStatus = $schema.enums.MilestoneStatus.values; +export type MilestoneStatus = (typeof MilestoneStatus)[keyof typeof MilestoneStatus]; +export const InvitationStatus = $schema.enums.InvitationStatus.values; +export type InvitationStatus = (typeof InvitationStatus)[keyof typeof InvitationStatus]; +export const PlanTier = $schema.enums.PlanTier.values; +export type PlanTier = (typeof PlanTier)[keyof typeof PlanTier]; +export const SubscriptionStatus = $schema.enums.SubscriptionStatus.values; +export type SubscriptionStatus = (typeof SubscriptionStatus)[keyof typeof SubscriptionStatus]; +export const InvoiceStatus = $schema.enums.InvoiceStatus.values; +export type InvoiceStatus = (typeof InvoiceStatus)[keyof typeof InvoiceStatus]; +export const NotificationType = $schema.enums.NotificationType.values; +export type NotificationType = (typeof NotificationType)[keyof typeof NotificationType]; +export const IntegrationProvider = $schema.enums.IntegrationProvider.values; +export type IntegrationProvider = (typeof IntegrationProvider)[keyof typeof IntegrationProvider]; +export const CustomFieldType = $schema.enums.CustomFieldType.values; +export type CustomFieldType = (typeof CustomFieldType)[keyof typeof CustomFieldType]; diff --git a/samples/taskforge/zenstack/schema.ts b/samples/taskforge/zenstack/schema.ts new file mode 100644 index 000000000..0effd9be0 --- /dev/null +++ b/samples/taskforge/zenstack/schema.ts @@ -0,0 +1,3060 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { type SchemaDef, type AttributeApplication, type FieldDefault, ExpressionUtils } from "@zenstackhq/schema"; +export class SchemaType implements SchemaDef { + provider = { + type: "sqlite" + } as const; + models = { + User: { + name: "User", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }] as readonly AttributeApplication[] + }, + name: { + name: "name", + type: "String" + }, + email: { + name: "email", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + emailVerified: { + name: "emailVerified", + type: "Boolean", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(false) }] }] as readonly AttributeApplication[], + default: false as FieldDefault + }, + image: { + name: "image", + type: "String", + optional: true + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }, { name: "@updatedAt" }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + accounts: { + name: "accounts", + type: "Account", + array: true, + relation: { opposite: "user" } + }, + sessions: { + name: "sessions", + type: "Session", + array: true, + relation: { opposite: "user" } + }, + ownedOrganizations: { + name: "ownedOrganizations", + type: "Organization", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("OrgOwner") }] }] as readonly AttributeApplication[], + relation: { opposite: "owner", name: "OrgOwner" } + }, + memberships: { + name: "memberships", + type: "Membership", + array: true, + relation: { opposite: "user" } + }, + sentInvitations: { + name: "sentInvitations", + type: "Invitation", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("InvitationInviter") }] }] as readonly AttributeApplication[], + relation: { opposite: "inviter", name: "InvitationInviter" } + }, + teamMemberships: { + name: "teamMemberships", + type: "TeamMembership", + array: true, + relation: { opposite: "user" } + }, + ledTeams: { + name: "ledTeams", + type: "Team", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("TeamLead") }] }] as readonly AttributeApplication[], + relation: { opposite: "lead", name: "TeamLead" } + }, + projectMemberships: { + name: "projectMemberships", + type: "ProjectMember", + array: true, + relation: { opposite: "user" } + }, + ledProjects: { + name: "ledProjects", + type: "Project", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("ProjectLead") }] }] as readonly AttributeApplication[], + relation: { opposite: "lead", name: "ProjectLead" } + }, + authoredIssues: { + name: "authoredIssues", + type: "Issue", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("IssueAuthor") }] }] as readonly AttributeApplication[], + relation: { opposite: "author", name: "IssueAuthor" } + }, + assignedIssues: { + name: "assignedIssues", + type: "Issue", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("IssueAssignee") }] }] as readonly AttributeApplication[], + relation: { opposite: "assignee", name: "IssueAssignee" } + }, + watchedIssues: { + name: "watchedIssues", + type: "Issue", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("IssueWatchers") }] }] as readonly AttributeApplication[], + relation: { opposite: "watchers", name: "IssueWatchers" } + }, + comments: { + name: "comments", + type: "Comment", + array: true, + relation: { opposite: "author" } + }, + attachments: { + name: "attachments", + type: "Attachment", + array: true, + relation: { opposite: "uploadedBy" } + }, + reactions: { + name: "reactions", + type: "Reaction", + array: true, + relation: { opposite: "user" } + }, + authoredDocuments: { + name: "authoredDocuments", + type: "Document", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("DocAuthor") }] }] as readonly AttributeApplication[], + relation: { opposite: "author", name: "DocAuthor" } + }, + collaboratingDocuments: { + name: "collaboratingDocuments", + type: "Document", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("DocCollaborators") }] }] as readonly AttributeApplication[], + relation: { opposite: "collaborators", name: "DocCollaborators" } + }, + activities: { + name: "activities", + type: "ActivityLog", + array: true, + relation: { opposite: "actor" } + }, + notifications: { + name: "notifications", + type: "Notification", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("NotifRecipient") }] }] as readonly AttributeApplication[], + relation: { opposite: "recipient", name: "NotifRecipient" } + }, + triggeredNotifications: { + name: "triggeredNotifications", + type: "Notification", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("NotifActor") }] }] as readonly AttributeApplication[], + relation: { opposite: "actor", name: "NotifActor" } + }, + timeEntries: { + name: "timeEntries", + type: "TimeEntry", + array: true, + relation: { opposite: "user" } + }, + installedIntegrations: { + name: "installedIntegrations", + type: "Integration", + array: true, + relation: { opposite: "installedBy" } + }, + apiTokens: { + name: "apiTokens", + type: "ApiToken", + array: true, + relation: { opposite: "user" } + } + }, + attributes: [ + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("user") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + email: { type: "String" } + } + }, + Account: { + name: "Account", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }] as readonly AttributeApplication[] + }, + accountId: { + name: "accountId", + type: "String" + }, + providerId: { + name: "providerId", + type: "String" + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "accounts", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + accessToken: { + name: "accessToken", + type: "String", + optional: true + }, + refreshToken: { + name: "refreshToken", + type: "String", + optional: true + }, + idToken: { + name: "idToken", + type: "String", + optional: true + }, + accessTokenExpiresAt: { + name: "accessTokenExpiresAt", + type: "DateTime", + optional: true + }, + refreshTokenExpiresAt: { + name: "refreshTokenExpiresAt", + type: "DateTime", + optional: true + }, + scope: { + name: "scope", + type: "String", + optional: true + }, + password: { + name: "password", + type: "String", + optional: true + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("account") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Session: { + name: "Session", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }] as readonly AttributeApplication[] + }, + expiresAt: { + name: "expiresAt", + type: "DateTime" + }, + token: { + name: "token", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + ipAddress: { + name: "ipAddress", + type: "String", + optional: true + }, + userAgent: { + name: "userAgent", + type: "String", + optional: true + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "sessions", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + } + }, + attributes: [ + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("session") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + token: { type: "String" } + } + }, + Verification: { + name: "Verification", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }] as readonly AttributeApplication[] + }, + identifier: { + name: "identifier", + type: "String" + }, + value: { + name: "value", + type: "String" + }, + expiresAt: { + name: "expiresAt", + type: "DateTime" + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }, { name: "@updatedAt" }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("identifier")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("verification") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Organization: { + name: "Organization", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + slug: { + name: "slug", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + logoUrl: { + name: "logoUrl", + type: "String", + optional: true + }, + billingCustomer: { + name: "billingCustomer", + type: "BillingCustomer", + optional: true, + relation: { opposite: "organization" } + }, + subscription: { + name: "subscription", + type: "Subscription", + optional: true, + relation: { opposite: "organization" } + }, + owner: { + name: "owner", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("OrgOwner") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("ownerId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "ownedOrganizations", name: "OrgOwner", fields: ["ownerId"], references: ["id"] } + }, + ownerId: { + name: "ownerId", + type: "String", + foreignKeyFor: [ + "owner" + ] as readonly string[] + }, + members: { + name: "members", + type: "Membership", + array: true, + relation: { opposite: "organization" } + }, + invitations: { + name: "invitations", + type: "Invitation", + array: true, + relation: { opposite: "organization" } + }, + teams: { + name: "teams", + type: "Team", + array: true, + relation: { opposite: "organization" } + }, + projects: { + name: "projects", + type: "Project", + array: true, + relation: { opposite: "organization" } + }, + labels: { + name: "labels", + type: "Label", + array: true, + relation: { opposite: "organization" } + }, + customFields: { + name: "customFields", + type: "CustomField", + array: true, + relation: { opposite: "organization" } + }, + webhooks: { + name: "webhooks", + type: "Webhook", + array: true, + relation: { opposite: "organization" } + }, + integrations: { + name: "integrations", + type: "Integration", + array: true, + relation: { opposite: "organization" } + }, + apiTokens: { + name: "apiTokens", + type: "ApiToken", + array: true, + relation: { opposite: "organization" } + }, + activities: { + name: "activities", + type: "ActivityLog", + array: true, + relation: { opposite: "organization" } + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("ownerId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("organization") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + slug: { type: "String" } + } + }, + Membership: { + name: "Membership", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + role: { + name: "role", + type: "OrgRole", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("MEMBER") }] }] as readonly AttributeApplication[], + default: "MEMBER" as FieldDefault + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "memberships", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "members", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId"), ExpressionUtils.field("organizationId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("membership") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + userId_organizationId: { userId: { type: "String" }, organizationId: { type: "String" } } + } + }, + Invitation: { + name: "Invitation", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + email: { + name: "email", + type: "String" + }, + role: { + name: "role", + type: "OrgRole", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("MEMBER") }] }] as readonly AttributeApplication[], + default: "MEMBER" as FieldDefault + }, + status: { + name: "status", + type: "InvitationStatus", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("PENDING") }] }] as readonly AttributeApplication[], + default: "PENDING" as FieldDefault + }, + token: { + name: "token", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + expiresAt: { + name: "expiresAt", + type: "DateTime" + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "invitations", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + inviter: { + name: "inviter", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("InvitationInviter") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("inviterId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "sentInvitations", name: "InvitationInviter", fields: ["inviterId"], references: ["id"] } + }, + inviterId: { + name: "inviterId", + type: "String", + foreignKeyFor: [ + "inviter" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("email")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("invitation") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + token: { type: "String" } + } + }, + Team: { + name: "Team", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + key: { + name: "key", + type: "String" + }, + description: { + name: "description", + type: "String", + optional: true + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "teams", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + lead: { + name: "lead", + type: "User", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("TeamLead") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("leadId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "ledTeams", name: "TeamLead", fields: ["leadId"], references: ["id"] } + }, + leadId: { + name: "leadId", + type: "String", + optional: true, + foreignKeyFor: [ + "lead" + ] as readonly string[] + }, + members: { + name: "members", + type: "TeamMembership", + array: true, + relation: { opposite: "team" } + }, + projects: { + name: "projects", + type: "Project", + array: true, + relation: { opposite: "team" } + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId"), ExpressionUtils.field("key")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("team") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + organizationId_key: { organizationId: { type: "String" }, key: { type: "String" } } + } + }, + TeamMembership: { + name: "TeamMembership", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "teamMemberships", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + }, + team: { + name: "team", + type: "Team", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("teamId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "members", fields: ["teamId"], references: ["id"], onDelete: "Cascade" } + }, + teamId: { + name: "teamId", + type: "String", + foreignKeyFor: [ + "team" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId"), ExpressionUtils.field("teamId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("teamId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("team_membership") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + userId_teamId: { userId: { type: "String" }, teamId: { type: "String" } } + } + }, + Project: { + name: "Project", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + slug: { + name: "slug", + type: "String" + }, + description: { + name: "description", + type: "String", + optional: true + }, + archived: { + name: "archived", + type: "Boolean", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(false) }] }] as readonly AttributeApplication[], + default: false as FieldDefault + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "projects", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + team: { + name: "team", + type: "Team", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("teamId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "projects", fields: ["teamId"], references: ["id"], onDelete: "SetNull" } + }, + teamId: { + name: "teamId", + type: "String", + optional: true, + foreignKeyFor: [ + "team" + ] as readonly string[] + }, + lead: { + name: "lead", + type: "User", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("ProjectLead") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("leadId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "ledProjects", name: "ProjectLead", fields: ["leadId"], references: ["id"] } + }, + leadId: { + name: "leadId", + type: "String", + optional: true, + foreignKeyFor: [ + "lead" + ] as readonly string[] + }, + members: { + name: "members", + type: "ProjectMember", + array: true, + relation: { opposite: "project" } + }, + issues: { + name: "issues", + type: "Issue", + array: true, + relation: { opposite: "project" } + }, + milestones: { + name: "milestones", + type: "Milestone", + array: true, + relation: { opposite: "project" } + }, + sprints: { + name: "sprints", + type: "Sprint", + array: true, + relation: { opposite: "project" } + }, + documents: { + name: "documents", + type: "Document", + array: true, + relation: { opposite: "project" } + }, + openIssueCount: { + name: "openIssueCount", + type: "Int", + attributes: [{ name: "@computed" }] as readonly AttributeApplication[], + computed: true + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId"), ExpressionUtils.field("slug")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("teamId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("project") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + organizationId_slug: { organizationId: { type: "String" }, slug: { type: "String" } } + }, + computedFields: { + openIssueCount(_context: { + modelAlias: string; + }): number { + throw new Error("This is a stub for computed field"); + } + } + }, + ProjectMember: { + name: "ProjectMember", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + role: { + name: "role", + type: "ProjectRole", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("CONTRIBUTOR") }] }] as readonly AttributeApplication[], + default: "CONTRIBUTOR" as FieldDefault + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "projectMemberships", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + }, + project: { + name: "project", + type: "Project", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "members", fields: ["projectId"], references: ["id"], onDelete: "Cascade" } + }, + projectId: { + name: "projectId", + type: "String", + foreignKeyFor: [ + "project" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId"), ExpressionUtils.field("projectId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("project_member") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + userId_projectId: { userId: { type: "String" }, projectId: { type: "String" } } + } + }, + Issue: { + name: "Issue", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + number: { + name: "number", + type: "Int" + }, + title: { + name: "title", + type: "String" + }, + description: { + name: "description", + type: "String", + optional: true + }, + status: { + name: "status", + type: "IssueStatus", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("BACKLOG") }] }] as readonly AttributeApplication[], + default: "BACKLOG" as FieldDefault + }, + priority: { + name: "priority", + type: "Priority", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("NONE") }] }] as readonly AttributeApplication[], + default: "NONE" as FieldDefault + }, + estimate: { + name: "estimate", + type: "Int", + optional: true + }, + dueDate: { + name: "dueDate", + type: "DateTime", + optional: true + }, + metadata: { + name: "metadata", + type: "IssueMetadata", + optional: true, + attributes: [{ name: "@json" }] as readonly AttributeApplication[] + }, + project: { + name: "project", + type: "Project", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "issues", fields: ["projectId"], references: ["id"], onDelete: "Cascade" } + }, + projectId: { + name: "projectId", + type: "String", + foreignKeyFor: [ + "project" + ] as readonly string[] + }, + author: { + name: "author", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("IssueAuthor") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "authoredIssues", name: "IssueAuthor", fields: ["authorId"], references: ["id"] } + }, + authorId: { + name: "authorId", + type: "String", + foreignKeyFor: [ + "author" + ] as readonly string[] + }, + assignee: { + name: "assignee", + type: "User", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("IssueAssignee") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("assigneeId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "assignedIssues", name: "IssueAssignee", fields: ["assigneeId"], references: ["id"], onDelete: "SetNull" } + }, + assigneeId: { + name: "assigneeId", + type: "String", + optional: true, + foreignKeyFor: [ + "assignee" + ] as readonly string[] + }, + milestone: { + name: "milestone", + type: "Milestone", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("milestoneId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "issues", fields: ["milestoneId"], references: ["id"], onDelete: "SetNull" } + }, + milestoneId: { + name: "milestoneId", + type: "String", + optional: true, + foreignKeyFor: [ + "milestone" + ] as readonly string[] + }, + sprint: { + name: "sprint", + type: "Sprint", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("sprintId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "issues", fields: ["sprintId"], references: ["id"], onDelete: "SetNull" } + }, + sprintId: { + name: "sprintId", + type: "String", + optional: true, + foreignKeyFor: [ + "sprint" + ] as readonly string[] + }, + parent: { + name: "parent", + type: "Issue", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("SubIssues") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("parentId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "children", name: "SubIssues", fields: ["parentId"], references: ["id"], onDelete: "SetNull" } + }, + parentId: { + name: "parentId", + type: "String", + optional: true, + foreignKeyFor: [ + "parent" + ] as readonly string[] + }, + children: { + name: "children", + type: "Issue", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("SubIssues") }] }] as readonly AttributeApplication[], + relation: { opposite: "parent", name: "SubIssues" } + }, + labels: { + name: "labels", + type: "Label", + array: true, + relation: { opposite: "issues" } + }, + watchers: { + name: "watchers", + type: "User", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("IssueWatchers") }] }] as readonly AttributeApplication[], + relation: { opposite: "watchedIssues", name: "IssueWatchers" } + }, + comments: { + name: "comments", + type: "Comment", + array: true, + relation: { opposite: "issue" } + }, + attachments: { + name: "attachments", + type: "Attachment", + array: true, + relation: { opposite: "issue" } + }, + reactions: { + name: "reactions", + type: "Reaction", + array: true, + relation: { opposite: "issue" } + }, + timeEntries: { + name: "timeEntries", + type: "TimeEntry", + array: true, + relation: { opposite: "issue" } + }, + customFieldValues: { + name: "customFieldValues", + type: "CustomFieldValue", + array: true, + relation: { opposite: "issue" } + }, + commentCount: { + name: "commentCount", + type: "Int", + attributes: [{ name: "@computed" }] as readonly AttributeApplication[], + computed: true + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId"), ExpressionUtils.field("number")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId"), ExpressionUtils.field("status")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("assigneeId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("issue") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + projectId_number: { projectId: { type: "String" }, number: { type: "Int" } } + }, + computedFields: { + commentCount(_context: { + modelAlias: string; + }): number { + throw new Error("This is a stub for computed field"); + } + } + }, + Label: { + name: "Label", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + color: { + name: "color", + type: "String", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("#999999") }] }] as readonly AttributeApplication[], + default: "#999999" as FieldDefault + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "labels", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + issues: { + name: "issues", + type: "Issue", + array: true, + relation: { opposite: "labels" } + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId"), ExpressionUtils.field("name")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("label") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + organizationId_name: { organizationId: { type: "String" }, name: { type: "String" } } + } + }, + Milestone: { + name: "Milestone", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + title: { + name: "title", + type: "String" + }, + description: { + name: "description", + type: "String", + optional: true + }, + status: { + name: "status", + type: "MilestoneStatus", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("OPEN") }] }] as readonly AttributeApplication[], + default: "OPEN" as FieldDefault + }, + dueDate: { + name: "dueDate", + type: "DateTime", + optional: true + }, + project: { + name: "project", + type: "Project", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "milestones", fields: ["projectId"], references: ["id"], onDelete: "Cascade" } + }, + projectId: { + name: "projectId", + type: "String", + foreignKeyFor: [ + "project" + ] as readonly string[] + }, + issues: { + name: "issues", + type: "Issue", + array: true, + relation: { opposite: "milestone" } + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("milestone") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Sprint: { + name: "Sprint", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + status: { + name: "status", + type: "SprintStatus", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("PLANNED") }] }] as readonly AttributeApplication[], + default: "PLANNED" as FieldDefault + }, + startDate: { + name: "startDate", + type: "DateTime", + optional: true + }, + endDate: { + name: "endDate", + type: "DateTime", + optional: true + }, + goal: { + name: "goal", + type: "String", + optional: true + }, + project: { + name: "project", + type: "Project", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "sprints", fields: ["projectId"], references: ["id"], onDelete: "Cascade" } + }, + projectId: { + name: "projectId", + type: "String", + foreignKeyFor: [ + "project" + ] as readonly string[] + }, + issues: { + name: "issues", + type: "Issue", + array: true, + relation: { opposite: "sprint" } + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("sprint") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Comment: { + name: "Comment", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + body: { + name: "body", + type: "String" + }, + issue: { + name: "issue", + type: "Issue", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "comments", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + author: { + name: "author", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "comments", fields: ["authorId"], references: ["id"] } + }, + authorId: { + name: "authorId", + type: "String", + foreignKeyFor: [ + "author" + ] as readonly string[] + }, + parent: { + name: "parent", + type: "Comment", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("CommentThread") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("parentId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "replies", name: "CommentThread", fields: ["parentId"], references: ["id"], onDelete: "SetNull" } + }, + parentId: { + name: "parentId", + type: "String", + optional: true, + foreignKeyFor: [ + "parent" + ] as readonly string[] + }, + replies: { + name: "replies", + type: "Comment", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("CommentThread") }] }] as readonly AttributeApplication[], + relation: { opposite: "parent", name: "CommentThread" } + }, + reactions: { + name: "reactions", + type: "Reaction", + array: true, + relation: { opposite: "comment" } + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("comment") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Reaction: { + name: "Reaction", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + emoji: { + name: "emoji", + type: "String" + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "reactions", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + }, + issue: { + name: "issue", + type: "Issue", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "reactions", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + optional: true, + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + comment: { + name: "comment", + type: "Comment", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("commentId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "reactions", fields: ["commentId"], references: ["id"], onDelete: "Cascade" } + }, + commentId: { + name: "commentId", + type: "String", + optional: true, + foreignKeyFor: [ + "comment" + ] as readonly string[] + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId"), ExpressionUtils.field("issueId"), ExpressionUtils.field("emoji")]) }] }, + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId"), ExpressionUtils.field("commentId"), ExpressionUtils.field("emoji")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("reaction") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + userId_issueId_emoji: { userId: { type: "String" }, issueId: { type: "String" }, emoji: { type: "String" } }, + userId_commentId_emoji: { userId: { type: "String" }, commentId: { type: "String" }, emoji: { type: "String" } } + } + }, + Attachment: { + name: "Attachment", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + kind: { + name: "kind", + type: "String", + isDiscriminator: true + }, + issue: { + name: "issue", + type: "Issue", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + uploadedBy: { + name: "uploadedBy", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("uploadedById")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["uploadedById"], references: ["id"] } + }, + uploadedById: { + name: "uploadedById", + type: "String", + foreignKeyFor: [ + "uploadedBy" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@delegate", args: [{ name: "discriminator", value: ExpressionUtils.field("kind") }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("attachment") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + }, + isDelegate: true, + subModels: ["FileAttachment", "ImageAttachment", "LinkAttachment"] + }, + FileAttachment: { + name: "FileAttachment", + baseModel: "Attachment", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + originModel: "Attachment", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + originModel: "Attachment", + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + kind: { + name: "kind", + type: "String", + originModel: "Attachment", + isDiscriminator: true + }, + issue: { + name: "issue", + type: "Issue", + originModel: "Attachment", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + originModel: "Attachment", + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + uploadedBy: { + name: "uploadedBy", + type: "User", + originModel: "Attachment", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("uploadedById")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["uploadedById"], references: ["id"] } + }, + uploadedById: { + name: "uploadedById", + type: "String", + originModel: "Attachment", + foreignKeyFor: [ + "uploadedBy" + ] as readonly string[] + }, + fileName: { + name: "fileName", + type: "String" + }, + fileSize: { + name: "fileSize", + type: "Int" + }, + mimeType: { + name: "mimeType", + type: "String" + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + ImageAttachment: { + name: "ImageAttachment", + baseModel: "Attachment", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + originModel: "Attachment", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + originModel: "Attachment", + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + kind: { + name: "kind", + type: "String", + originModel: "Attachment", + isDiscriminator: true + }, + issue: { + name: "issue", + type: "Issue", + originModel: "Attachment", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + originModel: "Attachment", + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + uploadedBy: { + name: "uploadedBy", + type: "User", + originModel: "Attachment", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("uploadedById")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["uploadedById"], references: ["id"] } + }, + uploadedById: { + name: "uploadedById", + type: "String", + originModel: "Attachment", + foreignKeyFor: [ + "uploadedBy" + ] as readonly string[] + }, + url: { + name: "url", + type: "String" + }, + width: { + name: "width", + type: "Int", + optional: true + }, + height: { + name: "height", + type: "Int", + optional: true + }, + alt: { + name: "alt", + type: "String", + optional: true + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + LinkAttachment: { + name: "LinkAttachment", + baseModel: "Attachment", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + originModel: "Attachment", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + originModel: "Attachment", + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + kind: { + name: "kind", + type: "String", + originModel: "Attachment", + isDiscriminator: true + }, + issue: { + name: "issue", + type: "Issue", + originModel: "Attachment", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + originModel: "Attachment", + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + uploadedBy: { + name: "uploadedBy", + type: "User", + originModel: "Attachment", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("uploadedById")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "attachments", fields: ["uploadedById"], references: ["id"] } + }, + uploadedById: { + name: "uploadedById", + type: "String", + originModel: "Attachment", + foreignKeyFor: [ + "uploadedBy" + ] as readonly string[] + }, + url: { + name: "url", + type: "String" + }, + title: { + name: "title", + type: "String", + optional: true + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Document: { + name: "Document", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + title: { + name: "title", + type: "String" + }, + content: { + name: "content", + type: "DocContent", + optional: true, + attributes: [{ name: "@json" }] as readonly AttributeApplication[] + }, + project: { + name: "project", + type: "Project", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "documents", fields: ["projectId"], references: ["id"], onDelete: "Cascade" } + }, + projectId: { + name: "projectId", + type: "String", + foreignKeyFor: [ + "project" + ] as readonly string[] + }, + author: { + name: "author", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("DocAuthor") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "authoredDocuments", name: "DocAuthor", fields: ["authorId"], references: ["id"] } + }, + authorId: { + name: "authorId", + type: "String", + foreignKeyFor: [ + "author" + ] as readonly string[] + }, + parent: { + name: "parent", + type: "Document", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("DocTree") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("parentId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "children", name: "DocTree", fields: ["parentId"], references: ["id"], onDelete: "SetNull" } + }, + parentId: { + name: "parentId", + type: "String", + optional: true, + foreignKeyFor: [ + "parent" + ] as readonly string[] + }, + children: { + name: "children", + type: "Document", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("DocTree") }] }] as readonly AttributeApplication[], + relation: { opposite: "parent", name: "DocTree" } + }, + collaborators: { + name: "collaborators", + type: "User", + array: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("DocCollaborators") }] }] as readonly AttributeApplication[], + relation: { opposite: "collaboratingDocuments", name: "DocCollaborators" } + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("projectId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("document") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + CustomField: { + name: "CustomField", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + type: { + name: "type", + type: "CustomFieldType", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("TEXT") }] }] as readonly AttributeApplication[], + default: "TEXT" as FieldDefault + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "customFields", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + values: { + name: "values", + type: "CustomFieldValue", + array: true, + relation: { opposite: "field" } + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId"), ExpressionUtils.field("name")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("custom_field") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + organizationId_name: { organizationId: { type: "String" }, name: { type: "String" } } + } + }, + CustomFieldValue: { + name: "CustomFieldValue", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + value: { + name: "value", + type: "String" + }, + field: { + name: "field", + type: "CustomField", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("fieldId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "values", fields: ["fieldId"], references: ["id"], onDelete: "Cascade" } + }, + fieldId: { + name: "fieldId", + type: "String", + foreignKeyFor: [ + "field" + ] as readonly string[] + }, + issue: { + name: "issue", + type: "Issue", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "customFieldValues", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + foreignKeyFor: [ + "issue" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("fieldId"), ExpressionUtils.field("issueId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("custom_field_value") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + fieldId_issueId: { fieldId: { type: "String" }, issueId: { type: "String" } } + } + }, + TimeEntry: { + name: "TimeEntry", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + minutes: { + name: "minutes", + type: "Int" + }, + note: { + name: "note", + type: "String", + optional: true + }, + spentAt: { + name: "spentAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + issue: { + name: "issue", + type: "Issue", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "timeEntries", fields: ["issueId"], references: ["id"], onDelete: "Cascade" } + }, + issueId: { + name: "issueId", + type: "String", + foreignKeyFor: [ + "issue" + ] as readonly string[] + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "timeEntries", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("issueId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("time_entry") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Webhook: { + name: "Webhook", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + url: { + name: "url", + type: "String" + }, + secret: { + name: "secret", + type: "String" + }, + active: { + name: "active", + type: "Boolean", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(true) }] }] as readonly AttributeApplication[], + default: true as FieldDefault + }, + config: { + name: "config", + type: "WebhookConfig", + attributes: [{ name: "@json" }] as readonly AttributeApplication[] + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "webhooks", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("webhook") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Integration: { + name: "Integration", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + provider: { + name: "provider", + type: "IntegrationProvider" + }, + config: { + name: "config", + type: "IntegrationConfig", + attributes: [{ name: "@json" }] as readonly AttributeApplication[] + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "integrations", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + installedBy: { + name: "installedBy", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("installedById")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "installedIntegrations", fields: ["installedById"], references: ["id"] } + }, + installedById: { + name: "installedById", + type: "String", + foreignKeyFor: [ + "installedBy" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@unique", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId"), ExpressionUtils.field("provider")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("integration") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + organizationId_provider: { organizationId: { type: "String" }, provider: { type: "IntegrationProvider" } } + } + }, + ApiToken: { + name: "ApiToken", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + tokenHash: { + name: "tokenHash", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + scopes: { + name: "scopes", + type: "String" + }, + lastUsedAt: { + name: "lastUsedAt", + type: "DateTime", + optional: true + }, + expiresAt: { + name: "expiresAt", + type: "DateTime", + optional: true + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "apiTokens", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "apiTokens", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("api_token") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + tokenHash: { type: "String" } + } + }, + ActivityLog: { + name: "ActivityLog", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + action: { + name: "action", + type: "String" + }, + targetType: { + name: "targetType", + type: "String" + }, + targetId: { + name: "targetId", + type: "String" + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "activities", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + actor: { + name: "actor", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("actorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "activities", fields: ["actorId"], references: ["id"] } + }, + actorId: { + name: "actorId", + type: "String", + foreignKeyFor: [ + "actor" + ] as readonly string[] + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }] }, + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("actorId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("activity_log") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Notification: { + name: "Notification", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + type: { + name: "type", + type: "NotificationType" + }, + message: { + name: "message", + type: "String" + }, + read: { + name: "read", + type: "Boolean", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(false) }] }] as readonly AttributeApplication[], + default: false as FieldDefault + }, + recipient: { + name: "recipient", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("NotifRecipient") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("recipientId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "notifications", name: "NotifRecipient", fields: ["recipientId"], references: ["id"], onDelete: "Cascade" } + }, + recipientId: { + name: "recipientId", + type: "String", + foreignKeyFor: [ + "recipient" + ] as readonly string[] + }, + actor: { + name: "actor", + type: "User", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "name", value: ExpressionUtils.literal("NotifActor") }, { name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("actorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("SetNull") }] }] as readonly AttributeApplication[], + relation: { opposite: "triggeredNotifications", name: "NotifActor", fields: ["actorId"], references: ["id"], onDelete: "SetNull" } + }, + actorId: { + name: "actorId", + type: "String", + optional: true, + foreignKeyFor: [ + "actor" + ] as readonly string[] + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("recipientId"), ExpressionUtils.field("read")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("notification") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + BillingCustomer: { + name: "BillingCustomer", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + stripeCustomerId: { + name: "stripeCustomerId", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + email: { + name: "email", + type: "String" + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "billingCustomer", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[], + foreignKeyFor: [ + "organization" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("billing_customer") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + stripeCustomerId: { type: "String" }, + organizationId: { type: "String" } + } + }, + Plan: { + name: "Plan", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + tier: { + name: "tier", + type: "PlanTier", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + priceCents: { + name: "priceCents", + type: "Int", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[], + default: 0 as FieldDefault + }, + seatLimit: { + name: "seatLimit", + type: "Int", + optional: true + }, + subscriptions: { + name: "subscriptions", + type: "Subscription", + array: true, + relation: { opposite: "plan" } + } + }, + attributes: [ + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("plan") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + tier: { type: "PlanTier" } + } + }, + Subscription: { + name: "Subscription", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + status: { + name: "status", + type: "SubscriptionStatus", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("TRIALING") }] }] as readonly AttributeApplication[], + default: "TRIALING" as FieldDefault + }, + currentPeriodEnd: { + name: "currentPeriodEnd", + type: "DateTime", + optional: true + }, + cancelAtPeriodEnd: { + name: "cancelAtPeriodEnd", + type: "Boolean", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(false) }] }] as readonly AttributeApplication[], + default: false as FieldDefault + }, + organization: { + name: "organization", + type: "Organization", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("organizationId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "subscription", fields: ["organizationId"], references: ["id"], onDelete: "Cascade" } + }, + organizationId: { + name: "organizationId", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[], + foreignKeyFor: [ + "organization" + ] as readonly string[] + }, + plan: { + name: "plan", + type: "Plan", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("planId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "subscriptions", fields: ["planId"], references: ["id"] } + }, + planId: { + name: "planId", + type: "String", + foreignKeyFor: [ + "plan" + ] as readonly string[] + }, + invoices: { + name: "invoices", + type: "Invoice", + array: true, + relation: { opposite: "subscription" } + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("planId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("subscription") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + organizationId: { type: "String" } + } + }, + Invoice: { + name: "Invoice", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + }, + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + number: { + name: "number", + type: "String", + unique: true, + attributes: [{ name: "@unique" }] as readonly AttributeApplication[] + }, + amountCents: { + name: "amountCents", + type: "Int" + }, + status: { + name: "status", + type: "InvoiceStatus", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("DRAFT") }] }] as readonly AttributeApplication[], + default: "DRAFT" as FieldDefault + }, + issuedAt: { + name: "issuedAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + paidAt: { + name: "paidAt", + type: "DateTime", + optional: true + }, + subscription: { + name: "subscription", + type: "Subscription", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("subscriptionId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }] as readonly AttributeApplication[], + relation: { opposite: "invoices", fields: ["subscriptionId"], references: ["id"], onDelete: "Cascade" } + }, + subscriptionId: { + name: "subscriptionId", + type: "String", + foreignKeyFor: [ + "subscription" + ] as readonly string[] + } + }, + attributes: [ + { name: "@@index", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("subscriptionId")]) }] }, + { name: "@@map", args: [{ name: "name", value: ExpressionUtils.literal("invoice") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + number: { type: "String" } + } + } + } as const; + typeDefs = { + Timestamps: { + name: "Timestamps", + fields: { + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + updatedAt: { + name: "updatedAt", + type: "DateTime", + updatedAt: true, + attributes: [{ name: "@updatedAt" }] as readonly AttributeApplication[] + } + } + }, + IssueMetadata: { + name: "IssueMetadata", + fields: { + sourceUrl: { + name: "sourceUrl", + type: "String", + optional: true + }, + externalId: { + name: "externalId", + type: "String", + optional: true + }, + storyPoints: { + name: "storyPoints", + type: "Int", + optional: true + }, + blockedById: { + name: "blockedById", + type: "String", + optional: true + } + } + }, + DocContent: { + name: "DocContent", + fields: { + format: { + name: "format", + type: "String" + }, + body: { + name: "body", + type: "String" + }, + revision: { + name: "revision", + type: "Int", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(1) }] }] as readonly AttributeApplication[], + default: 1 as FieldDefault + } + } + }, + WebhookConfig: { + name: "WebhookConfig", + fields: { + events: { + name: "events", + type: "String" + }, + contentType: { + name: "contentType", + type: "String", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("application/json") }] }] as readonly AttributeApplication[], + default: "application/json" as FieldDefault + } + } + }, + IntegrationConfig: { + name: "IntegrationConfig", + fields: { + accessToken: { + name: "accessToken", + type: "String" + }, + accountName: { + name: "accountName", + type: "String", + optional: true + }, + scopes: { + name: "scopes", + type: "String", + optional: true + } + } + } + } as const; + enums = { + OrgRole: { + name: "OrgRole", + values: { + OWNER: "OWNER", + ADMIN: "ADMIN", + MEMBER: "MEMBER", + GUEST: "GUEST" + } + }, + ProjectRole: { + name: "ProjectRole", + values: { + LEAD: "LEAD", + CONTRIBUTOR: "CONTRIBUTOR", + VIEWER: "VIEWER" + } + }, + IssueStatus: { + name: "IssueStatus", + values: { + BACKLOG: "BACKLOG", + TODO: "TODO", + IN_PROGRESS: "IN_PROGRESS", + IN_REVIEW: "IN_REVIEW", + DONE: "DONE", + CANCELED: "CANCELED" + } + }, + Priority: { + name: "Priority", + values: { + NONE: "NONE", + LOW: "LOW", + MEDIUM: "MEDIUM", + HIGH: "HIGH", + URGENT: "URGENT" + } + }, + SprintStatus: { + name: "SprintStatus", + values: { + PLANNED: "PLANNED", + ACTIVE: "ACTIVE", + COMPLETED: "COMPLETED" + } + }, + MilestoneStatus: { + name: "MilestoneStatus", + values: { + OPEN: "OPEN", + CLOSED: "CLOSED" + } + }, + InvitationStatus: { + name: "InvitationStatus", + values: { + PENDING: "PENDING", + ACCEPTED: "ACCEPTED", + REVOKED: "REVOKED", + EXPIRED: "EXPIRED" + } + }, + PlanTier: { + name: "PlanTier", + values: { + FREE: "FREE", + PRO: "PRO", + BUSINESS: "BUSINESS", + ENTERPRISE: "ENTERPRISE" + } + }, + SubscriptionStatus: { + name: "SubscriptionStatus", + values: { + TRIALING: "TRIALING", + ACTIVE: "ACTIVE", + PAST_DUE: "PAST_DUE", + CANCELED: "CANCELED" + } + }, + InvoiceStatus: { + name: "InvoiceStatus", + values: { + DRAFT: "DRAFT", + OPEN: "OPEN", + PAID: "PAID", + VOID: "VOID" + } + }, + NotificationType: { + name: "NotificationType", + values: { + ISSUE_ASSIGNED: "ISSUE_ASSIGNED", + ISSUE_MENTIONED: "ISSUE_MENTIONED", + COMMENT_ADDED: "COMMENT_ADDED", + INVITATION: "INVITATION", + BILLING: "BILLING" + } + }, + IntegrationProvider: { + name: "IntegrationProvider", + values: { + GITHUB: "GITHUB", + GITLAB: "GITLAB", + SLACK: "SLACK", + DISCORD: "DISCORD", + FIGMA: "FIGMA" + } + }, + CustomFieldType: { + name: "CustomFieldType", + values: { + TEXT: "TEXT", + NUMBER: "NUMBER", + BOOLEAN: "BOOLEAN", + DATE: "DATE", + SELECT: "SELECT" + } + } + } as const; + authType = "User" as const; + plugins = {}; +} +export const schema = new SchemaType(); diff --git a/samples/taskforge/zenstack/schema.zmodel b/samples/taskforge/zenstack/schema.zmodel new file mode 100644 index 000000000..32114cf99 --- /dev/null +++ b/samples/taskforge/zenstack/schema.zmodel @@ -0,0 +1,805 @@ +// ============================================================================ +// TaskForge — a team collaboration / project-tracking SaaS +// ZenStack v3 schema (ZModel). Database: SQLite. +// +// Demonstrates: 1-1, 1-many, implicit & explicit many-to-many, self relations, +// polymorphism (@@delegate), typed JSON (@json), computed fields (@computed), +// reusable field mixins (`type ... with`), and enums. +// +// Workflow: +// pnpm zen:generate # compile this schema -> ./schema.ts (the typed client) +// pnpm db:push # sync the SQLite database to this schema +// ============================================================================ + +datasource db { + provider = 'sqlite' + url = env('DATABASE_URL') +} + +// ---------------------------------------------------------------------------- +// Mixins — reusable field bundles inlined into models with `with`. +// ---------------------------------------------------------------------------- + +type Timestamps { + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +// ---------------------------------------------------------------------------- +// Enums +// ---------------------------------------------------------------------------- + +enum OrgRole { + OWNER + ADMIN + MEMBER + GUEST +} + +enum ProjectRole { + LEAD + CONTRIBUTOR + VIEWER +} + +enum IssueStatus { + BACKLOG + TODO + IN_PROGRESS + IN_REVIEW + DONE + CANCELED +} + +enum Priority { + NONE + LOW + MEDIUM + HIGH + URGENT +} + +enum SprintStatus { + PLANNED + ACTIVE + COMPLETED +} + +enum MilestoneStatus { + OPEN + CLOSED +} + +enum InvitationStatus { + PENDING + ACCEPTED + REVOKED + EXPIRED +} + +enum PlanTier { + FREE + PRO + BUSINESS + ENTERPRISE +} + +enum SubscriptionStatus { + TRIALING + ACTIVE + PAST_DUE + CANCELED +} + +enum InvoiceStatus { + DRAFT + OPEN + PAID + VOID +} + +enum NotificationType { + ISSUE_ASSIGNED + ISSUE_MENTIONED + COMMENT_ADDED + INVITATION + BILLING +} + +enum IntegrationProvider { + GITHUB + GITLAB + SLACK + DISCORD + FIGMA +} + +enum CustomFieldType { + TEXT + NUMBER + BOOLEAN + DATE + SELECT +} + +// ============================================================================ +// Authentication models (better-auth core schema) +// Mapped to better-auth's expected lowercase table names. +// ============================================================================ + +model User { + id String @id + name String + email String @unique + emailVerified Boolean @default(false) + image String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + // --- auth relations --- + accounts Account[] + sessions Session[] + + // --- domain relations --- + ownedOrganizations Organization[] @relation("OrgOwner") + memberships Membership[] + sentInvitations Invitation[] @relation("InvitationInviter") + teamMemberships TeamMembership[] + ledTeams Team[] @relation("TeamLead") + projectMemberships ProjectMember[] + ledProjects Project[] @relation("ProjectLead") + authoredIssues Issue[] @relation("IssueAuthor") + assignedIssues Issue[] @relation("IssueAssignee") + watchedIssues Issue[] @relation("IssueWatchers") + comments Comment[] + attachments Attachment[] + reactions Reaction[] + authoredDocuments Document[] @relation("DocAuthor") + collaboratingDocuments Document[] @relation("DocCollaborators") + activities ActivityLog[] + notifications Notification[] @relation("NotifRecipient") + triggeredNotifications Notification[] @relation("NotifActor") + timeEntries TimeEntry[] + installedIntegrations Integration[] + apiTokens ApiToken[] + + @@map('user') +} + +model Account { + id String @id + accountId String + providerId String + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + accessToken String? + refreshToken String? + idToken String? + accessTokenExpiresAt DateTime? + refreshTokenExpiresAt DateTime? + scope String? + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([userId]) + @@map('account') +} + +model Session { + id String @id + expiresAt DateTime + token String @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + ipAddress String? + userAgent String? + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map('session') +} + +model Verification { + id String @id + identifier String + value String + expiresAt DateTime + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + @@index([identifier]) + @@map('verification') +} + +// ============================================================================ +// Tenancy — organizations, members, teams +// ============================================================================ + +model Organization with Timestamps { + id String @id @default(cuid()) + name String + slug String @unique + logoUrl String? + + // 1-1: an organization optionally has a billing customer & subscription + billingCustomer BillingCustomer? + subscription Subscription? + + // owner (1-many from the User's perspective, but conceptually one owner) + owner User @relation("OrgOwner", fields: [ownerId], references: [id]) + ownerId String + + members Membership[] + invitations Invitation[] + teams Team[] + projects Project[] + labels Label[] + customFields CustomField[] + webhooks Webhook[] + integrations Integration[] + apiTokens ApiToken[] + activities ActivityLog[] + + + @@index([ownerId]) + @@map('organization') +} + +/// Explicit many-to-many join between User and Organization, carrying a role. +model Membership with Timestamps { + id String @id @default(cuid()) + role OrgRole @default(MEMBER) + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + + + @@unique([userId, organizationId]) + @@index([organizationId]) + @@map('membership') +} + +model Invitation with Timestamps { + id String @id @default(cuid()) + email String + role OrgRole @default(MEMBER) + status InvitationStatus @default(PENDING) + token String @unique + expiresAt DateTime + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + inviter User @relation("InvitationInviter", fields: [inviterId], references: [id]) + inviterId String + + + @@index([organizationId]) + @@index([email]) + @@map('invitation') +} + +model Team with Timestamps { + id String @id @default(cuid()) + name String + key String // short prefix used for issue identifiers, e.g. "ENG" + description String? + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + lead User? @relation("TeamLead", fields: [leadId], references: [id]) + leadId String? + + members TeamMembership[] + projects Project[] + + + @@unique([organizationId, key]) + @@index([organizationId]) + @@map('team') +} + +/// Explicit many-to-many join between User and Team. +model TeamMembership with Timestamps { + id String @id @default(cuid()) + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) + teamId String + + + @@unique([userId, teamId]) + @@index([teamId]) + @@map('team_membership') +} + +// ============================================================================ +// Projects, issues & planning +// ============================================================================ + +model Project with Timestamps { + id String @id @default(cuid()) + name String + slug String + description String? + archived Boolean @default(false) + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + team Team? @relation(fields: [teamId], references: [id], onDelete: SetNull) + teamId String? + lead User? @relation("ProjectLead", fields: [leadId], references: [id]) + leadId String? + + members ProjectMember[] + issues Issue[] + milestones Milestone[] + sprints Sprint[] + documents Document[] + + // computed: number of issues not in a terminal state (implemented in client config) + openIssueCount Int @computed + + + @@unique([organizationId, slug]) + @@index([teamId]) + @@map('project') +} + +/// Explicit many-to-many join between User and Project, carrying a role. +model ProjectMember with Timestamps { + id String @id @default(cuid()) + role ProjectRole @default(CONTRIBUTOR) + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + + @@unique([userId, projectId]) + @@index([projectId]) + @@map('project_member') +} + +model Issue with Timestamps { + id String @id @default(cuid()) + number Int // per-project sequential number + title String + description String? + status IssueStatus @default(BACKLOG) + priority Priority @default(NONE) + estimate Int? // story points + dueDate DateTime? + + // Arbitrary, strongly-typed metadata bag stored as JSON. + metadata IssueMetadata? @json + + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + author User @relation("IssueAuthor", fields: [authorId], references: [id]) + authorId String + assignee User? @relation("IssueAssignee", fields: [assigneeId], references: [id], onDelete: SetNull) + assigneeId String? + + milestone Milestone? @relation(fields: [milestoneId], references: [id], onDelete: SetNull) + milestoneId String? + sprint Sprint? @relation(fields: [sprintId], references: [id], onDelete: SetNull) + sprintId String? + + // Self-relation: sub-issues / parent issue. + parent Issue? @relation("SubIssues", fields: [parentId], references: [id], onDelete: SetNull) + parentId String? + children Issue[] @relation("SubIssues") + + // Implicit many-to-many: labels and watchers. + labels Label[] + watchers User[] @relation("IssueWatchers") + + comments Comment[] + attachments Attachment[] + reactions Reaction[] + timeEntries TimeEntry[] + customFieldValues CustomFieldValue[] + + // computed: number of comments on this issue (implemented in client config) + commentCount Int @computed + + + @@unique([projectId, number]) + @@index([projectId, status]) + @@index([assigneeId]) + @@map('issue') +} + +/// Strongly-typed JSON payload for Issue.metadata. +type IssueMetadata { + sourceUrl String? + externalId String? + storyPoints Int? + blockedById String? +} + +model Label with Timestamps { + id String @id @default(cuid()) + name String + color String @default("#999999") + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + + issues Issue[] + + + @@unique([organizationId, name]) + @@map('label') +} + +model Milestone with Timestamps { + id String @id @default(cuid()) + title String + description String? + status MilestoneStatus @default(OPEN) + dueDate DateTime? + + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + issues Issue[] + + + @@index([projectId]) + @@map('milestone') +} + +model Sprint with Timestamps { + id String @id @default(cuid()) + name String + status SprintStatus @default(PLANNED) + startDate DateTime? + endDate DateTime? + goal String? + + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + issues Issue[] + + + @@index([projectId]) + @@map('sprint') +} + +// ============================================================================ +// Collaboration — comments, reactions, attachments, documents +// ============================================================================ + +model Comment with Timestamps { + id String @id @default(cuid()) + body String + + issue Issue @relation(fields: [issueId], references: [id], onDelete: Cascade) + issueId String + author User @relation(fields: [authorId], references: [id]) + authorId String + + // Self-relation: threaded replies. + parent Comment? @relation("CommentThread", fields: [parentId], references: [id], onDelete: SetNull) + parentId String? + replies Comment[] @relation("CommentThread") + + reactions Reaction[] + + + @@index([issueId]) + @@map('comment') +} + +model Reaction { + id String @id @default(cuid()) + emoji String + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + + // A reaction targets either an issue or a comment (both optional FKs). + issue Issue? @relation(fields: [issueId], references: [id], onDelete: Cascade) + issueId String? + comment Comment? @relation(fields: [commentId], references: [id], onDelete: Cascade) + commentId String? + + createdAt DateTime @default(now()) + + @@unique([userId, issueId, emoji]) + @@unique([userId, commentId, emoji]) + @@map('reaction') +} + +/// Polymorphic base. Concrete subtypes are FileAttachment / ImageAttachment / LinkAttachment. +/// Create the concrete models, never `Attachment` directly. +model Attachment with Timestamps { + id String @id @default(cuid()) + kind String // discriminator + + issue Issue @relation(fields: [issueId], references: [id], onDelete: Cascade) + issueId String + uploadedBy User @relation(fields: [uploadedById], references: [id]) + uploadedById String + + + @@delegate(kind) + @@index([issueId]) + @@map('attachment') +} + +model FileAttachment extends Attachment { + fileName String + fileSize Int + mimeType String +} + +model ImageAttachment extends Attachment { + url String + width Int? + height Int? + alt String? +} + +model LinkAttachment extends Attachment { + url String + title String? +} + +model Document with Timestamps { + id String @id @default(cuid()) + title String + + // Rich content stored as typed JSON. + content DocContent? @json + + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + author User @relation("DocAuthor", fields: [authorId], references: [id]) + authorId String + + // Self-relation: document tree (folders / nested pages). + parent Document? @relation("DocTree", fields: [parentId], references: [id], onDelete: SetNull) + parentId String? + children Document[] @relation("DocTree") + + // Implicit many-to-many: collaborators. + collaborators User[] @relation("DocCollaborators") + + + @@index([projectId]) + @@map('document') +} + +/// Strongly-typed JSON payload for Document.content. +type DocContent { + format String // e.g. "markdown" | "tiptap" + body String + revision Int @default(1) +} + +// ============================================================================ +// Custom fields, time tracking +// ============================================================================ + +model CustomField with Timestamps { + id String @id @default(cuid()) + name String + type CustomFieldType @default(TEXT) + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + + values CustomFieldValue[] + + + @@unique([organizationId, name]) + @@map('custom_field') +} + +/// Explicit many-to-many join between CustomField and Issue, carrying a value. +model CustomFieldValue { + id String @id @default(cuid()) + value String + + field CustomField @relation(fields: [fieldId], references: [id], onDelete: Cascade) + fieldId String + issue Issue @relation(fields: [issueId], references: [id], onDelete: Cascade) + issueId String + + @@unique([fieldId, issueId]) + @@index([issueId]) + @@map('custom_field_value') +} + +model TimeEntry { + id String @id @default(cuid()) + minutes Int + note String? + spentAt DateTime @default(now()) + + issue Issue @relation(fields: [issueId], references: [id], onDelete: Cascade) + issueId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + + @@index([issueId]) + @@index([userId]) + @@map('time_entry') +} + +// ============================================================================ +// Automation & integrations +// ============================================================================ + +model Webhook with Timestamps { + id String @id @default(cuid()) + url String + secret String + active Boolean @default(true) + + // The list of subscribed events, stored as typed JSON (SQLite has no scalar lists). + config WebhookConfig @json + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + + + @@index([organizationId]) + @@map('webhook') +} + +/// Strongly-typed JSON payload for Webhook.config. +type WebhookConfig { + events String // comma-separated event names + contentType String @default("application/json") +} + +model Integration with Timestamps { + id String @id @default(cuid()) + provider IntegrationProvider + config IntegrationConfig @json + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + installedBy User @relation(fields: [installedById], references: [id]) + installedById String + + + @@unique([organizationId, provider]) + @@map('integration') +} + +/// Strongly-typed JSON payload for Integration.config. +type IntegrationConfig { + accessToken String + accountName String? + scopes String? +} + +model ApiToken with Timestamps { + id String @id @default(cuid()) + name String + tokenHash String @unique + scopes String // space-separated scopes + lastUsedAt DateTime? + expiresAt DateTime? + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + + + @@index([organizationId]) + @@map('api_token') +} + +model ActivityLog { + id String @id @default(cuid()) + action String // e.g. "issue.created", "project.archived" + targetType String + targetId String + + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String + actor User @relation(fields: [actorId], references: [id]) + actorId String + + createdAt DateTime @default(now()) + + @@index([organizationId]) + @@index([actorId]) + @@map('activity_log') +} + +model Notification { + id String @id @default(cuid()) + type NotificationType + message String + read Boolean @default(false) + + recipient User @relation("NotifRecipient", fields: [recipientId], references: [id], onDelete: Cascade) + recipientId String + actor User? @relation("NotifActor", fields: [actorId], references: [id], onDelete: SetNull) + actorId String? + + createdAt DateTime @default(now()) + + @@index([recipientId, read]) + @@map('notification') +} + +// ============================================================================ +// Billing +// ============================================================================ + +model BillingCustomer with Timestamps { + id String @id @default(cuid()) + stripeCustomerId String @unique + email String + + // 1-1 with Organization. + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String @unique + + + @@map('billing_customer') +} + +model Plan { + id String @id @default(cuid()) + name String + tier PlanTier @unique + priceCents Int @default(0) + seatLimit Int? + + subscriptions Subscription[] + + @@map('plan') +} + +model Subscription with Timestamps { + id String @id @default(cuid()) + status SubscriptionStatus @default(TRIALING) + currentPeriodEnd DateTime? + cancelAtPeriodEnd Boolean @default(false) + + // 1-1 with Organization. + organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) + organizationId String @unique + + plan Plan @relation(fields: [planId], references: [id]) + planId String + + invoices Invoice[] + + + @@index([planId]) + @@map('subscription') +} + +model Invoice with Timestamps { + id String @id @default(cuid()) + number String @unique + amountCents Int + status InvoiceStatus @default(DRAFT) + issuedAt DateTime @default(now()) + paidAt DateTime? + + subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade) + subscriptionId String + + + @@index([subscriptionId]) + @@map('invoice') +} diff --git a/tests/e2e/orm/client-api/find.test.ts b/tests/e2e/orm/client-api/find.test.ts index 2f52eb1fa..69d9b1c0d 100644 --- a/tests/e2e/orm/client-api/find.test.ts +++ b/tests/e2e/orm/client-api/find.test.ts @@ -1189,6 +1189,57 @@ describe('Client find tests ', () => { expect(result3?._count.posts).toBe(1); }); + it('supports _count nested inside an include', async () => { + // regression for https://github.com/zenstackhq/zenstack/issues/2669 + const user = await createUser(client, 'u1@test.com'); + const [post1] = await createPosts(client, user.id); + await client.comment.createMany({ + data: [ + { content: 'c1', postId: post1.id }, + { content: 'c2', postId: post1.id }, + ], + }); + + // _count nested inside a relation's `include` (not `select`) + const result = await client.user.findFirst({ + include: { + posts: { + include: { + _count: { select: { comments: true } }, + }, + }, + }, + }); + + expect(result?.posts).toHaveLength(2); + const p1 = result?.posts.find((p) => p.id === post1.id); + const p2 = result?.posts.find((p) => p.id !== post1.id); + expect(p1?._count).toEqual({ comments: 2 }); + expect(p2?._count).toEqual({ comments: 0 }); + + // `_count: true` nested inside an include + const result2 = await client.user.findFirst({ + include: { + posts: { + include: { _count: true }, + }, + }, + }); + expect(result2?.posts.find((p) => p.id === post1.id)?._count).toEqual({ comments: 2 }); + + // _count nested inside an include, with a filter on the counted relation + const result3 = await client.user.findFirst({ + include: { + posts: { + include: { + _count: { select: { comments: { where: { content: 'c1' } } } }, + }, + }, + }, + }); + expect(result3?.posts.find((p) => p.id === post1.id)?._count).toEqual({ comments: 1 }); + }); + it('rejects orderBy array elements with multiple keys', async () => { await createUser(client, 'u1@test.com'); diff --git a/tests/e2e/orm/policy/isempty-function-pg.test.ts b/tests/e2e/orm/policy/isempty-function-pg.test.ts new file mode 100644 index 000000000..9af038d02 --- /dev/null +++ b/tests/e2e/orm/policy/isempty-function-pg.test.ts @@ -0,0 +1,36 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +describe('isEmpty function in policies using Postgres', () => { + it('does not throw an error', async () => { + const schema = ` +model User { + id Int @id @default(autoincrement()) + roles Role[] + + @@allow('all', true) + @@deny('create', isEmpty(roles)) +} + +enum Role { + AUTHOR + EDITOR +} + `; + + const client = await createPolicyTestClient(schema, { + usePrismaPush: true, + provider: 'postgresql', + }); + + await expect(client.user.create({ + data: { + id: 1, + roles: ['AUTHOR'], + }, + })).resolves.toMatchObject({ + id: 1, + roles: ['AUTHOR'], + }); + }); +}); diff --git a/tests/e2e/orm/validation/custom-validation.test.ts b/tests/e2e/orm/validation/custom-validation.test.ts index 905c99c92..47f12049d 100644 --- a/tests/e2e/orm/validation/custom-validation.test.ts +++ b/tests/e2e/orm/validation/custom-validation.test.ts @@ -13,6 +13,8 @@ describe('Custom validation tests', () => { str4 String? str5 String? str6 String? + str7 String? + str8 String? int1 Int? list1 Int[] list2 Int[] @@ -35,6 +37,10 @@ describe('Custom validation tests', () => { @@validate(str6 == null || isPhone(str6), 'invalid str6') + @@validate(str7 == null || isDate(str7), 'invalid str7') + + @@validate(str8 == null || isTime(str8), 'invalid str8') + @@validate(list1 == null || (has(list1, 1) && hasSome(list1, [2, 3]) && hasEvery(list1, [4, 5])), 'invalid list1') @@validate(list2 == null || isEmpty(list2), 'invalid list2', ['x', 'y']) @@ -83,6 +89,12 @@ describe('Custom validation tests', () => { // violates phone await expect(_t({ str6: 'not-a-phone' })).toBeRejectedByValidation(['invalid str6']); + // violates date + await expect(_t({ str7: 'not-a-date' })).toBeRejectedByValidation(['invalid str7']); + + // violates time + await expect(_t({ str8: 'not-a-time' })).toBeRejectedByValidation(['invalid str8']); + // violates has await expect(_t({ list1: [2, 3, 4, 5] })).toBeRejectedByValidation(['invalid list1']); @@ -114,6 +126,8 @@ describe('Custom validation tests', () => { str4: 'http://a.b.c', str5: new Date().toISOString(), str6: '+15555555555', + str7: '2000-01-01', + str8: '03:15:00', int1: 2, list1: [1, 2, 4, 5], list2: [], diff --git a/tests/e2e/orm/validation/toplevel.test.ts b/tests/e2e/orm/validation/toplevel.test.ts index 65927e192..fbfdbd92a 100644 --- a/tests/e2e/orm/validation/toplevel.test.ts +++ b/tests/e2e/orm/validation/toplevel.test.ts @@ -7,14 +7,17 @@ describe('Toplevel field validation tests', () => { const db = await createTestClient( ` model Foo { - id Int @id @default(autoincrement()) - str1 String? @length(2, 4) @startsWith('a') @endsWith('b') @contains('m') @regex('b{2}') - str2 String? @email - str3 String? @datetime - str4 String? @url - str5 String? @trim @lower - str6 String? @upper - str7 String? @phone + id Int @id @default(autoincrement()) + str1 String? @length(2, 4) @startsWith('a') @endsWith('b') @contains('m') @regex('b{2}') + str2 String? @email + str3 String? @datetime + str4 String? @url + str5 String? @trim @lower + str6 String? @upper + str7 String? @phone + str8 String? @date + str9 String? @time + str10 String? @time(-1) } `, ); @@ -90,6 +93,24 @@ describe('Toplevel field validation tests', () => { // satisfies @phone await expect(_t({ str7: '+15555555555' })).toResolveTruthy(); + + // violates @date + await expect(_t({ str8: 'not-a-date' })).toBeRejectedByValidation(['Invalid ISO date']); + + // satisfies @date + await expect(_t({ str8: '2000-01-01' })).toResolveTruthy(); + + // violates @time + await expect(_t({ str9: 'not-a-time' })).toBeRejectedByValidation(['Invalid ISO time']); + + // satisfies @time + await expect(_t({ str9: '03:15:00' })).toResolveTruthy(); + + // violates @time(-1) + await expect(_t({ str10: '03:15:00' })).toBeRejectedByValidation(['Invalid ISO time']); + + // satisfies @time(-1) + await expect(_t({ str10: '03:15' })).toResolveTruthy(); } }); diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 49f8aca55..9ba4aa54b 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.7.2", + "version": "3.8.0", "private": true, "type": "module", "scripts": { diff --git a/tests/regression/package.json b/tests/regression/package.json index 6f7b4945f..3019c41ab 100644 --- a/tests/regression/package.json +++ b/tests/regression/package.json @@ -1,6 +1,6 @@ { "name": "regression", - "version": "3.7.2", + "version": "3.8.0", "private": true, "type": "module", "scripts": { diff --git a/tests/regression/test/issue-2669.test.ts b/tests/regression/test/issue-2669.test.ts new file mode 100644 index 000000000..b7fee31d5 --- /dev/null +++ b/tests/regression/test/issue-2669.test.ts @@ -0,0 +1,62 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +// https://github.com/zenstackhq/zenstack/issues/2669 +describe('Regression for issue 2669', () => { + const schema = ` +model User { + id Int @id @default(autoincrement()) + email String @unique + posts Post[] +} + +model Post { + id Int @id @default(autoincrement()) + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int + comments Comment[] +} + +model Comment { + id Int @id @default(autoincrement()) + content String + post Post @relation(fields: [postId], references: [id]) + postId Int +} +`; + + it('supports _count inside a nested include', async () => { + const db = await createTestClient(schema); + + await db.user.create({ + data: { + email: 'user1@test.com', + posts: { + create: { + title: 'Post1', + comments: { + create: [{ content: 'c1' }, { content: 'c2' }], + }, + }, + }, + }, + }); + + const result = await db.user.findMany({ + include: { + posts: { + include: { + _count: { + select: { comments: true }, + }, + }, + }, + }, + }); + + expect(result).toHaveLength(1); + expect(result[0]!.posts).toHaveLength(1); + expect(result[0]!.posts[0]!._count).toEqual({ comments: 2 }); + }); +}); diff --git a/tests/regression/test/issue-2671.test.ts b/tests/regression/test/issue-2671.test.ts new file mode 100644 index 000000000..69d0a9dff --- /dev/null +++ b/tests/regression/test/issue-2671.test.ts @@ -0,0 +1,59 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +// https://github.com/zenstackhq/zenstack/issues/2671 +describe('Regression for issue 2671', () => { + const schema = ` +model User { + id String @id @default(cuid()) + email String @unique + passwordHash String @omit +} + `; + + it('allows selecting omitted fields when override is allowed (default)', async () => { + const db = await createTestClient(schema); + + await db.user.create({ + data: { email: 'user1@test.com', passwordHash: 'secret-hash' }, + }); + + // by default query-time override is allowed, so explicit select returns the field + const selected = await db.user.findFirst({ + where: { email: 'user1@test.com' }, + select: { passwordHash: true }, + }); + expect(selected?.passwordHash).toBe('secret-hash'); + }); + + it('forbids selecting omitted fields when allowQueryTimeOmitOverride is false', async () => { + const base = await createTestClient(schema); + const db = base.$setOptions({ ...base.$options, allowQueryTimeOmitOverride: false }); + + await db.user.create({ + data: { email: 'user1@test.com', passwordHash: 'secret-hash' }, + }); + + // explicitly selecting the omitted field must be rejected + await expect( + db.user.findFirst({ + where: { email: 'user1@test.com' }, + select: { passwordHash: true }, + }), + ).toBeRejectedByValidation(); + + // selecting non-omitted fields still works + const ok = await db.user.findFirst({ + where: { email: 'user1@test.com' }, + select: { email: true }, + }); + expect(ok).toEqual({ email: 'user1@test.com' }); + + // explicitly excluding the omitted field via select is fine + const excluded = await db.user.findFirst({ + where: { email: 'user1@test.com' }, + select: { email: true, passwordHash: false }, + }); + expect(excluded).toEqual({ email: 'user1@test.com' }); + }); +}); diff --git a/tests/regression/test/issue-2718.test.ts b/tests/regression/test/issue-2718.test.ts new file mode 100644 index 000000000..d4e91d87f --- /dev/null +++ b/tests/regression/test/issue-2718.test.ts @@ -0,0 +1,73 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +describe('Regression for issue 2718', () => { + it('handles `in` operator against a list of enum values with native PostgreSQL enums', async () => { + const db = await createPolicyTestClient( + ` +enum State { + IN_PROGRESS + DONE + REVIEWED +} + +model Post { + id String @id @default(cuid()) + title String + state State + + @@allow('read,create', true) + // only allow deleting posts whose state is DONE or REVIEWED + @@deny('delete', !(state in [DONE, REVIEWED])) + @@allow('delete', true) +} + `, + { usePrismaPush: true, provider: 'postgresql' }, + ); + + await db.post.create({ data: { id: '1', title: 'p1', state: 'IN_PROGRESS' } }); + await db.post.create({ data: { id: '2', title: 'p2', state: 'DONE' } }); + await db.post.create({ data: { id: '3', title: 'p3', state: 'REVIEWED' } }); + + // IN_PROGRESS is not in [DONE, REVIEWED] -> delete denied + await expect(db.post.delete({ where: { id: '1' } })).toBeRejectedNotFound(); + + // DONE and REVIEWED are in the list -> delete allowed + await expect(db.post.delete({ where: { id: '2' } })).toResolveTruthy(); + await expect(db.post.delete({ where: { id: '3' } })).toResolveTruthy(); + }); + + it('handles `in` operator against a list of enum values with SQLite', async () => { + const db = await createPolicyTestClient( + ` +enum State { + IN_PROGRESS + DONE + REVIEWED +} + +model Post { + id String @id @default(cuid()) + title String + state State + + @@allow('create', true) + // only readable when state is DONE or REVIEWED + @@allow('read', state in [DONE, REVIEWED]) +} + `, + ); + + await db.$unuseAll().post.createMany({ + data: [ + { id: '1', title: 'p1', state: 'IN_PROGRESS' }, + { id: '2', title: 'p2', state: 'DONE' }, + { id: '3', title: 'p3', state: 'REVIEWED' }, + ], + }); + + await expect(db.post.findMany()).resolves.toHaveLength(2); + await expect(db.post.findUnique({ where: { id: '1' } })).resolves.toBeNull(); + await expect(db.post.findUnique({ where: { id: '2' } })).toResolveTruthy(); + }); +}); diff --git a/tests/runtimes/bun/package.json b/tests/runtimes/bun/package.json index a99454a00..b35084204 100644 --- a/tests/runtimes/bun/package.json +++ b/tests/runtimes/bun/package.json @@ -1,6 +1,6 @@ { "name": "bun-e2e", - "version": "3.7.2", + "version": "3.8.0", "private": true, "type": "module", "scripts": { diff --git a/tests/runtimes/edge-runtime/package.json b/tests/runtimes/edge-runtime/package.json index ba2e31069..038e931c6 100644 --- a/tests/runtimes/edge-runtime/package.json +++ b/tests/runtimes/edge-runtime/package.json @@ -1,6 +1,6 @@ { "name": "edge-runtime-e2e", - "version": "3.7.2", + "version": "3.8.0", "private": true, "type": "module", "scripts": {