diff --git a/.npmrc b/.npmrc index 0b634b898..308555701 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,3 @@ auto-install-peers=true git-checks=false +node-linker=hoisted diff --git a/package.json b/package.json index 319ce7a56..8abeb043f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-monorepo", - "version": "2.3.3", + "version": "2.4.0", "description": "", "scripts": { "build": "pnpm -r build", diff --git a/packages/ide/jetbrains/build.gradle.kts b/packages/ide/jetbrains/build.gradle.kts index 1b9312bc8..cf9de5ad3 100644 --- a/packages/ide/jetbrains/build.gradle.kts +++ b/packages/ide/jetbrains/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } group = "dev.zenstack" -version = "2.3.3" +version = "2.4.0" repositories { mavenCentral() diff --git a/packages/ide/jetbrains/package.json b/packages/ide/jetbrains/package.json index 6991b87cd..f68d795c2 100644 --- a/packages/ide/jetbrains/package.json +++ b/packages/ide/jetbrains/package.json @@ -1,6 +1,6 @@ { "name": "jetbrains", - "version": "2.3.3", + "version": "2.4.0", "displayName": "ZenStack JetBrains IDE Plugin", "description": "ZenStack JetBrains IDE plugin", "homepage": "https://zenstack.dev", diff --git a/packages/language/package.json b/packages/language/package.json index abaf24a04..9d576abae 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/language", - "version": "2.3.3", + "version": "2.4.0", "displayName": "ZenStack modeling language compiler", "description": "ZenStack modeling language compiler", "homepage": "https://zenstack.dev", diff --git a/packages/misc/redwood/package.json b/packages/misc/redwood/package.json index b2ff53809..497f43be4 100644 --- a/packages/misc/redwood/package.json +++ b/packages/misc/redwood/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/redwood", "displayName": "ZenStack RedwoodJS Integration", - "version": "2.3.3", + "version": "2.4.0", "description": "CLI and runtime for integrating ZenStack with RedwoodJS projects.", "repository": { "type": "git", @@ -39,17 +39,18 @@ "homepage": "https://zenstack.dev", "license": "MIT", "dependencies": { + "@redwoodjs/cli-helpers": "^7.7.3", "@zenstackhq/runtime": "workspace:*", "colors": "1.4.0", - "ts-morph": "^16.0.0", - "@redwoodjs/cli-helpers": "^6.6.0", "execa": "^5.0.0", "listr2": "^6.0.0", + "semver": "^7.5.2", "terminal-link": "^2.0.0", + "ts-morph": "^16.0.0", "yargs": "^17.7.2" }, "devDependencies": { - "@redwoodjs/graphql-server": "^6.6.0", + "@redwoodjs/graphql-server": "^7.7.3", "@types/yargs": "^17.0.32", "graphql-yoga": "^5.0.2" } diff --git a/packages/misc/redwood/src/commands/setup.ts b/packages/misc/redwood/src/commands/setup.ts index b8f9f16f0..ec5979617 100644 --- a/packages/misc/redwood/src/commands/setup.ts +++ b/packages/misc/redwood/src/commands/setup.ts @@ -1,9 +1,10 @@ -import { getPaths, updateTomlConfig } from '@redwoodjs/cli-helpers'; +import { getInstalledRedwoodVersion, getPaths, updateTomlConfig } from '@redwoodjs/cli-helpers'; import colors from 'colors'; import execa from 'execa'; import fs from 'fs'; import { Listr, ListrTask } from 'listr2'; import path from 'path'; +import semver from 'semver'; import terminalLink from 'terminal-link'; import { Project, SyntaxKind, type PropertyAssignment } from 'ts-morph'; import type { CommandModule } from 'yargs'; @@ -47,8 +48,8 @@ function bootstrapSchema() { const pkg = JSON.parse(content); if (!pkg.zenstack) { pkg.zenstack = { - schema: path.relative(apiPaths.base, zmodel), - prisma: path.relative(apiPaths.base, apiPaths.dbSchema), + schema: normalizePath(path.relative(apiPaths.base, zmodel)), + prisma: normalizePath(path.relative(apiPaths.base, apiPaths.dbSchema)), }; fs.writeFileSync(pkgJson, JSON.stringify(pkg, null, 4)); } @@ -57,6 +58,11 @@ function bootstrapSchema() { }; } +// ensures posix path separators are used in package.json +function normalizePath(_path: string) { + return _path.replaceAll(path.sep, path.posix.sep); +} + // install ZenStack GraphQLYoga plugin function installGraphQLPlugin() { return { @@ -144,11 +150,17 @@ function installGraphQLPlugin() { if (graphQlSourcePath.endsWith('.ts')) { const typeDefPath = path.join(getPaths().api.src, 'zenstack.d.ts'); if (!fs.existsSync(typeDefPath)) { + const rwVersion: string = getInstalledRedwoodVersion(); + const contextModule = + rwVersion && semver.lt(rwVersion, '7.0.0') + ? '@redwoodjs/graphql-server' // pre v7 + : '@redwoodjs/context'; // v7+ + const typeDefSourceFile = project.createSourceFile( typeDefPath, - `import type { PrismaClient } from '@prisma/client' + `import type { PrismaClient } from '@zenstackhq/runtime' -declare module '@redwoodjs/graphql-server' { +declare module '${contextModule}' { interface GlobalContext { db: PrismaClient } diff --git a/packages/plugins/openapi/package.json b/packages/plugins/openapi/package.json index 938e28aba..2b2107ac7 100644 --- a/packages/plugins/openapi/package.json +++ b/packages/plugins/openapi/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/openapi", "displayName": "ZenStack Plugin and Runtime for OpenAPI", - "version": "2.3.3", + "version": "2.4.0", "description": "ZenStack plugin and runtime supporting OpenAPI", "main": "index.js", "repository": { diff --git a/packages/plugins/swr/package.json b/packages/plugins/swr/package.json index 0f665b882..b6ad18232 100644 --- a/packages/plugins/swr/package.json +++ b/packages/plugins/swr/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/swr", "displayName": "ZenStack plugin for generating SWR hooks", - "version": "2.3.3", + "version": "2.4.0", "description": "ZenStack plugin for generating SWR hooks", "main": "index.js", "repository": { diff --git a/packages/plugins/tanstack-query/package.json b/packages/plugins/tanstack-query/package.json index 5e331e185..cc6d52c21 100644 --- a/packages/plugins/tanstack-query/package.json +++ b/packages/plugins/tanstack-query/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/tanstack-query", "displayName": "ZenStack plugin for generating tanstack-query hooks", - "version": "2.3.3", + "version": "2.4.0", "description": "ZenStack plugin for generating tanstack-query hooks", "main": "index.js", "exports": { diff --git a/packages/plugins/trpc/package.json b/packages/plugins/trpc/package.json index a2176b65c..530d1be76 100644 --- a/packages/plugins/trpc/package.json +++ b/packages/plugins/trpc/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/trpc", "displayName": "ZenStack plugin for tRPC", - "version": "2.3.3", + "version": "2.4.0", "description": "ZenStack plugin for tRPC", "main": "index.js", "repository": { diff --git a/packages/plugins/trpc/src/project.ts b/packages/plugins/trpc/src/project.ts index 0a87ba912..2f1ea4c0f 100644 --- a/packages/plugins/trpc/src/project.ts +++ b/packages/plugins/trpc/src/project.ts @@ -6,6 +6,7 @@ const compilerOptions: CompilerOptions = { emitDecoratorMetadata: true, experimentalDecorators: true, esModuleInterop: true, + skipLibCheck: true, }; export const project = new Project({ diff --git a/packages/runtime/jest.config.ts b/packages/runtime/jest.config.ts new file mode 120000 index 000000000..a12d812fd --- /dev/null +++ b/packages/runtime/jest.config.ts @@ -0,0 +1 @@ +../../jest.config.ts \ No newline at end of file diff --git a/packages/runtime/package.json b/packages/runtime/package.json index d30cba0de..129ea32f8 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/runtime", "displayName": "ZenStack Runtime Library", - "version": "2.3.3", + "version": "2.4.0", "description": "Runtime of ZenStack for both client-side and server-side environments.", "repository": { "type": "git", diff --git a/packages/runtime/res/enhance.d.ts b/packages/runtime/res/enhance.d.ts index 1e79b73be..e6ca800d2 100644 --- a/packages/runtime/res/enhance.d.ts +++ b/packages/runtime/res/enhance.d.ts @@ -1 +1 @@ -export { auth, enhance } from '.zenstack/enhance'; +export { auth, enhance, type PrismaClient } from '.zenstack/enhance'; diff --git a/packages/runtime/src/cross/model-meta.ts b/packages/runtime/src/cross/model-meta.ts index a90b20685..3eb6b3786 100644 --- a/packages/runtime/src/cross/model-meta.ts +++ b/packages/runtime/src/cross/model-meta.ts @@ -161,6 +161,11 @@ export type ModelMeta = { * Name of model that backs the `auth()` function */ authModel?: string; + + /** + * Optional map from full names to shortened names, used for extra fields/relations generated by ZenStack + */ + shortNameMap?: Record; }; /** diff --git a/packages/runtime/src/enhancements/default-auth.ts b/packages/runtime/src/enhancements/default-auth.ts index ba1ead7f1..1408a93b7 100644 --- a/packages/runtime/src/enhancements/default-auth.ts +++ b/packages/runtime/src/enhancements/default-auth.ts @@ -1,12 +1,19 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { FieldInfo, NestedWriteVisitor, PrismaWriteActionType, enumerate, getFields, requireField } from '../cross'; -import { clone } from '../cross'; +import { + FieldInfo, + NestedWriteVisitor, + PrismaWriteActionType, + clone, + enumerate, + getFields, + requireField, +} from '../cross'; import { DbClientContract } from '../types'; import { EnhancementContext, InternalEnhancementOptions } from './create-enhancement'; import { DefaultPrismaProxyHandler, PrismaProxyActions, makeProxy } from './proxy'; -import { isUnsafeMutate } from './utils'; +import { isUnsafeMutate, prismaClientValidationError } from './utils'; /** * Gets an enhanced Prisma client that supports `@default(auth())` attribute. @@ -143,7 +150,11 @@ class DefaultAuthHandler extends DefaultPrismaProxyHandler { private getDefaultValueFromAuth(fieldInfo: FieldInfo) { if (!this.userContext) { - throw new Error(`Evaluating default value of field \`${fieldInfo.name}\` requires a user context`); + throw prismaClientValidationError( + this.prisma, + this.options.prismaModule, + `Evaluating default value of field \`${fieldInfo.name}\` requires a user context` + ); } return fieldInfo.defaultValueProvider?.(this.userContext); } diff --git a/packages/runtime/src/enhancements/delegate.ts b/packages/runtime/src/enhancements/delegate.ts index 361d19b4f..e82d24d72 100644 --- a/packages/runtime/src/enhancements/delegate.ts +++ b/packages/runtime/src/enhancements/delegate.ts @@ -1040,7 +1040,10 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { } private makeAuxRelationName(model: ModelInfo) { - return `${DELEGATE_AUX_RELATION_PREFIX}_${lowerCaseFirst(model.name)}`; + const name = `${DELEGATE_AUX_RELATION_PREFIX}_${lowerCaseFirst(model.name)}`; + // make sure we look up into short name map to see if it's truncated + const shortName = this.options.modelMeta.shortNameMap?.[name]; + return shortName ?? name; } private getModelName(model: string) { diff --git a/packages/runtime/src/enhancements/policy/policy-utils.ts b/packages/runtime/src/enhancements/policy/policy-utils.ts index 91e460783..79c50b6c9 100644 --- a/packages/runtime/src/enhancements/policy/policy-utils.ts +++ b/packages/runtime/src/enhancements/policy/policy-utils.ts @@ -30,6 +30,7 @@ import { Logger } from '../logger'; import { QueryUtils } from '../query-utils'; import type { EntityChecker, ModelPolicyDef, PermissionCheckerFunc, PolicyDef, PolicyFunc, ZodSchemas } from '../types'; import { formatObject, prismaClientKnownRequestError } from '../utils'; +import { isPlainObject } from 'is-plain-object'; /** * Access policy enforcement utilities @@ -107,23 +108,63 @@ export class PolicyUtil extends QueryUtils { // Static True/False conditions // https://www.prisma.io/docs/concepts/components/prisma-client/null-and-undefined#the-effect-of-null-and-undefined-on-conditionals - public isTrue(condition: object) { - if (condition === null || condition === undefined) { + private singleKey(obj: object | null | undefined, key: string): obj is { [key: string]: unknown } { + if (!obj) { return false; } else { - return ( - (typeof condition === 'object' && Object.keys(condition).length === 0) || - ('AND' in condition && Array.isArray(condition.AND) && condition.AND.length === 0) - ); + return Object.keys(obj).length === 1 && Object.keys(obj)[0] === key; } } - public isFalse(condition: object) { - if (condition === null || condition === undefined) { + public isTrue(condition: object | null | undefined) { + if (condition === null || condition === undefined || !isPlainObject(condition)) { return false; - } else { - return 'OR' in condition && Array.isArray(condition.OR) && condition.OR.length === 0; } + + // {} is true + if (Object.keys(condition).length === 0) { + return true; + } + + // { OR: TRUE } is true + if (this.singleKey(condition, 'OR') && typeof condition.OR === 'object' && this.isTrue(condition.OR)) { + return true; + } + + // { NOT: FALSE } is true + if (this.singleKey(condition, 'NOT') && typeof condition.NOT === 'object' && this.isFalse(condition.NOT)) { + return true; + } + + // { AND: [] } is true + if (this.singleKey(condition, 'AND') && Array.isArray(condition.AND) && condition.AND.length === 0) { + return true; + } + + return false; + } + + public isFalse(condition: object | null | undefined) { + if (condition === null || condition === undefined || !isPlainObject(condition)) { + return false; + } + + // { AND: FALSE } is false + if (this.singleKey(condition, 'AND') && typeof condition.AND === 'object' && this.isFalse(condition.AND)) { + return true; + } + + // { NOT: TRUE } is false + if (this.singleKey(condition, 'NOT') && typeof condition.NOT === 'object' && this.isTrue(condition.NOT)) { + return true; + } + + // { OR: [] } is false + if (this.singleKey(condition, 'OR') && Array.isArray(condition.OR) && condition.OR.length === 0) { + return true; + } + + return false; } private makeTrue() { @@ -149,11 +190,6 @@ export class PolicyUtil extends QueryUtils { const result: any = {}; for (const [key, value] of Object.entries(condition)) { - if (this.isFalse(result)) { - // already false, no need to continue - break; - } - if (value === null || value === undefined) { result[key] = value; continue; @@ -165,14 +201,13 @@ export class PolicyUtil extends QueryUtils { .map((c: any) => this.reduce(c)) .filter((c) => c !== undefined && !this.isTrue(c)); if (children.length === 0) { - result[key] = []; // true + // { ..., AND: [] } + result[key] = []; } else if (children.some((c) => this.isFalse(c))) { - result['OR'] = []; // false + // { ..., AND: { OR: [] } } + result[key] = this.makeFalse(); } else { - if (!this.isTrue({ AND: result[key] })) { - // use AND only if it's not already true - result[key] = !Array.isArray(value) && children.length === 1 ? children[0] : children; - } + result[key] = !Array.isArray(value) && children.length === 1 ? children[0] : children; } break; } @@ -182,54 +217,43 @@ export class PolicyUtil extends QueryUtils { .map((c: any) => this.reduce(c)) .filter((c) => c !== undefined && !this.isFalse(c)); if (children.length === 0) { - result[key] = []; // false + // { ..., OR: [] } + result[key] = []; } else if (children.some((c) => this.isTrue(c))) { - result['AND'] = []; // true + // { ..., OR: { AND: [] } } + result[key] = this.makeTrue(); } else { - if (!this.isFalse({ OR: result[key] })) { - // use OR only if it's not already false - result[key] = !Array.isArray(value) && children.length === 1 ? children[0] : children; - } + result[key] = !Array.isArray(value) && children.length === 1 ? children[0] : children; } break; } case 'NOT': { - const children = enumerate(value) - .map((c: any) => this.reduce(c)) - .filter((c) => c !== undefined && !this.isFalse(c)); - if (children.length === 0) { - // all clauses are false, result is a constant true, - // thus eliminated (not adding into result) - } else if (children.some((c) => this.isTrue(c))) { - // some clauses are true, result is a constant false, - // eliminate all other keys and set entire condition to false - Object.keys(result).forEach((k) => delete result[k]); - result['OR'] = []; // this will cause the outer loop to exit too - } else { - result[key] = !Array.isArray(value) && children.length === 1 ? children[0] : children; - } + const children = enumerate(value).map((c: any) => this.reduce(c)); + result[key] = !Array.isArray(value) && children.length === 1 ? children[0] : children; break; } default: { - const booleanKeys = ['AND', 'OR', 'NOT', 'is', 'isNot', 'none', 'every', 'some']; - if ( - typeof value === 'object' && - value && - // recurse only if the value has at least one boolean key - Object.keys(value).some((k) => booleanKeys.includes(k)) - ) { - result[key] = this.reduce(value); - } else { + if (!isPlainObject(value)) { + // don't visit into non-plain object values - could be Date, array, etc. result[key] = value; + } else { + result[key] = this.reduce(value); } break; } } } - return result; + // finally normalize constant true/false conditions + if (this.isTrue(result)) { + return this.makeTrue(); + } else if (this.isFalse(result)) { + return this.makeFalse(); + } else { + return result; + } } //#endregion diff --git a/packages/runtime/tests/policy/reduction.test.ts b/packages/runtime/tests/policy/reduction.test.ts new file mode 100644 index 000000000..95a2e6f2f --- /dev/null +++ b/packages/runtime/tests/policy/reduction.test.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { PolicyUtil } from '../../src/enhancements/policy/policy-utils'; + +// eslint-disable-next-line jest/no-disabled-tests +describe.skip('Prisma query reduction tests', () => { + function reduce(query: any) { + const util = new PolicyUtil({} as any, {} as any); + return util['reduce'](query); + } + + const TRUE = { AND: [] }; + const FALSE = { OR: [] }; + + it('should keep regular queries unchanged', () => { + expect(reduce(null)).toEqual(null); + expect(reduce({ x: 1, y: 'hello' })).toEqual({ x: 1, y: 'hello' }); + const d = new Date(); + expect(reduce({ x: d })).toEqual({ x: d }); + }); + + it('should keep regular logical queries unchanged', () => { + expect(reduce({ AND: [{ x: 1 }, { y: 'hello' }] })).toEqual({ AND: [{ x: 1 }, { y: 'hello' }] }); + expect(reduce({ OR: [{ x: 1 }, { y: 'hello' }] })).toEqual({ OR: [{ x: 1 }, { y: 'hello' }] }); + expect(reduce({ NOT: [{ x: 1 }, { y: 'hello' }] })).toEqual({ NOT: [{ x: 1 }, { y: 'hello' }] }); + expect(reduce({ AND: { x: 1 }, OR: { y: 'hello' }, NOT: { z: 2 } })).toEqual({ + AND: { x: 1 }, + OR: { y: 'hello' }, + NOT: { z: 2 }, + }); + expect(reduce({ AND: { x: 1, OR: { y: 'hello', NOT: [{ z: 2 }] } } })).toEqual({ + AND: { x: 1, OR: { y: 'hello', NOT: [{ z: 2 }] } }, + }); + }); + + it('should handle constant true false', () => { + expect(reduce(undefined)).toEqual(TRUE); + expect(reduce({})).toEqual(TRUE); + expect(reduce(TRUE)).toEqual(TRUE); + expect(reduce(FALSE)).toEqual(FALSE); + }); + + it('should reduce simple true false', () => { + expect(reduce({ AND: TRUE })).toEqual(TRUE); + expect(reduce({ AND: FALSE })).toEqual(FALSE); + expect(reduce({ OR: TRUE })).toEqual(TRUE); + expect(reduce({ OR: FALSE })).toEqual(FALSE); + expect(reduce({ NOT: TRUE })).toEqual(FALSE); + expect(reduce({ NOT: FALSE })).toEqual(TRUE); + }); + + it('should reduce AND queries', () => { + expect(reduce({ AND: [{ x: 1 }, TRUE, { y: 2 }] })).toEqual({ AND: [{ x: 1 }, { y: 2 }] }); + expect(reduce({ AND: [{ x: 1 }, FALSE, { y: 2 }] })).toEqual(FALSE); + expect(reduce({ AND: [{ x: 1 }, TRUE, FALSE, { y: 2 }] })).toEqual(FALSE); + }); + + it('should reduce OR queries', () => { + expect(reduce({ OR: [{ x: 1 }, TRUE, { y: 2 }] })).toEqual(TRUE); + expect(reduce({ OR: [{ x: 1 }, FALSE, { y: 2 }] })).toEqual({ OR: [{ x: 1 }, { y: 2 }] }); + expect(reduce({ OR: [{ x: 1 }, TRUE, FALSE, { y: 2 }] })).toEqual(TRUE); + }); + + it('should reduce NOT queries', () => { + expect(reduce({ NOT: { AND: [FALSE, { x: 1 }] } })).toEqual(TRUE); + expect(reduce({ NOT: { OR: [TRUE, { x: 1 }] } })).toEqual(FALSE); + }); +}); diff --git a/packages/schema/README.md b/packages/schema/README.md index 60b1cf5bf..cfb38c259 100644 --- a/packages/schema/README.md +++ b/packages/schema/README.md @@ -1,6 +1,6 @@ # ZenStack VS Code Extension -[ZenStack](https://zenstack.dev) is a toolkit that simplifies the development of a web app's backend. It supercharges [Prisma ORM](https://prisma.io) with a powerful access control layer and unleashes its full potential for web development. +[ZenStack](https://zenstack.dev) is a toolkit that simplifies the development of a web app's backend. It enhances [Prisma ORM](https://prisma.io) with flexible Authorization and auto-generated, type-safe APIs/hooks, simplifying full-stack development. This VS Code extension provides code editing helpers for authoring ZenStack's schema files (.zmodel files). diff --git a/packages/schema/package.json b/packages/schema/package.json index c6e0d8e95..afa10d972 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack Language Tools", "description": "Build scalable web apps with minimum code by defining authorization and validation rules inside the data schema that closer to the database", - "version": "2.3.3", + "version": "2.4.0", "author": { "name": "ZenStack Team" }, diff --git a/packages/schema/src/cli/plugin-runner.ts b/packages/schema/src/cli/plugin-runner.ts index f66b57295..ea00809fe 100644 --- a/packages/schema/src/cli/plugin-runner.ts +++ b/packages/schema/src/cli/plugin-runner.ts @@ -14,6 +14,7 @@ import { type OptionValue, type PluginDeclaredOptions, type PluginFunction, + type PluginOptions, type PluginResult, } from '@zenstackhq/sdk'; import { type DMMF } from '@zenstackhq/sdk/prisma'; @@ -131,11 +132,12 @@ export class PluginRunner { // run core plugins first let dmmf: DMMF.Document | undefined = undefined; + let shortNameMap: Map | undefined; let prismaClientPath = '@prisma/client'; const project = createProject(); for (const { name, description, run, options: pluginOptions } of corePlugins) { const options = { ...pluginOptions, prismaClientPath }; - const r = await this.runPlugin(name, description, run, runnerOptions, options, dmmf, project); + const r = await this.runPlugin(name, description, run, runnerOptions, options, dmmf, shortNameMap, project); warnings.push(...(r?.warnings ?? [])); // the null-check is for backward compatibility if (r.dmmf) { @@ -143,6 +145,11 @@ export class PluginRunner { dmmf = r.dmmf; } + if (r.shortNameMap) { + // use the model short name map returned by the plugin + shortNameMap = r.shortNameMap; + } + if (r.prismaClientPath) { // use the prisma client path returned by the plugin prismaClientPath = r.prismaClientPath; @@ -155,7 +162,7 @@ export class PluginRunner { // run user plugins for (const { name, description, run, options: pluginOptions } of userPlugins) { const options = { ...pluginOptions, prismaClientPath }; - const r = await this.runPlugin(name, description, run, runnerOptions, options, dmmf, project); + const r = await this.runPlugin(name, description, run, runnerOptions, options, dmmf, shortNameMap, project); warnings.push(...(r?.warnings ?? [])); // the null-check is for backward compatibility } @@ -183,12 +190,15 @@ export class PluginRunner { // 2. @core/enhancer const existingEnhancer = plugins.find((p) => p.provider === CorePlugins.Enhancer); if (existingEnhancer) { + // enhancer should load zod schemas if there're validation rules + existingEnhancer.options.withZodSchemas = hasValidation; corePlugins.push(existingEnhancer); plugins.splice(plugins.indexOf(existingEnhancer), 1); } else { if (options.defaultPlugins) { corePlugins.push( this.makeCorePlugin(CorePlugins.Enhancer, options.schemaPath, { + // enhancer should load zod schemas if there're validation rules withZodSchemas: hasValidation, }) ); @@ -308,6 +318,7 @@ export class PluginRunner { runnerOptions: PluginRunnerOptions, options: PluginDeclaredOptions, dmmf: DMMF.Document | undefined, + shortNameMap: Map | undefined, project: Project ) { const title = description ?? `Running plugin ${colors.cyan(name)}`; @@ -322,7 +333,12 @@ export class PluginRunner { options, }, async () => { - return await run(runnerOptions.schema, { ...options, schemaPath: runnerOptions.schemaPath }, dmmf, { + const finalOptions = { + ...options, + schemaPath: runnerOptions.schemaPath, + shortNameMap, + } as PluginOptions; + return await run(runnerOptions.schema, finalOptions, dmmf, { output: runnerOptions.output, compile: runnerOptions.compile, tsProject: project, diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index 0da689e49..ad64f513a 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -7,6 +7,7 @@ import { getDataModels, getLiteral, isDelegateModel, + isDiscriminatorField, type PluginOptions, } from '@zenstackhq/sdk'; import { @@ -94,7 +95,8 @@ export class EnhancerGenerator { const enhanceTs = this.project.createSourceFile( path.join(this.outDir, 'enhance.ts'), - `import { type EnhancementContext, type EnhancementOptions, type ZodSchemas, type AuthUser } from '@zenstackhq/runtime'; + `/* eslint-disable */ +import { type EnhancementContext, type EnhancementOptions, type ZodSchemas, type AuthUser } from '@zenstackhq/runtime'; import { createEnhancement } from '@zenstackhq/runtime/enhancements'; import modelMeta from './model-meta'; import policy from './policy'; @@ -125,8 +127,9 @@ ${ } private createSimplePrismaImports(prismaImport: string) { - return `import { Prisma } from '${prismaImport}'; + return `import { Prisma, type PrismaClient } from '${prismaImport}'; import type * as _P from '${prismaImport}'; +export type { PrismaClient }; `; } @@ -150,6 +153,7 @@ export function enhance(prisma: DbClient, context?: Enh import type { InternalArgs, DynamicClientExtensionThis } from '${prismaImport}/runtime/library'; import type * as _P from '${logicalPrismaClientDir}/index-fixed'; import type { Prisma, PrismaClient } from '${logicalPrismaClientDir}/index-fixed'; +export type { PrismaClient }; `; } @@ -482,12 +486,12 @@ export function enhance(prisma: any, context?: EnhancementContext<${authTypePara const typeName = typeAlias.getName(); const delegateModelNames = delegateModels.map(([delegate]) => delegate.name); const delegateCreateUpdateInputRegex = new RegExp( - `\\${delegateModelNames.join('|')}(Unchecked)?(Create|Update).*Input` + `^(${delegateModelNames.join('|')})(Unchecked)?(Create|Update).*Input$` ); if (delegateCreateUpdateInputRegex.test(typeName)) { const toRemove = typeAlias .getDescendantsOfKind(SyntaxKind.PropertySignature) - .filter((p) => ['create', 'connectOrCreate', 'upsert'].includes(p.getName())); + .filter((p) => ['create', 'createMany', 'connectOrCreate', 'upsert'].includes(p.getName())); toRemove.forEach((r) => { source = source.replace(r.getText(), ''); }); @@ -495,33 +499,34 @@ export function enhance(prisma: any, context?: EnhancementContext<${authTypePara return source; } + private readonly ModelCreateUpdateInputRegex = /(\S+)(Unchecked)?(Create|Update).*Input/; + private removeDiscriminatorFromConcreteInput( typeAlias: TypeAliasDeclaration, - delegateInfo: DelegateInfo, + _delegateInfo: DelegateInfo, source: string ) { - // remove discriminator field from the create/update input of concrete models because - // discriminator cannot be set directly + // remove discriminator field from the create/update input because discriminator cannot be set directly const typeName = typeAlias.getName(); - const concreteModelNames = delegateInfo.map(([, concretes]) => concretes.map((c) => c.name)).flatMap((c) => c); - const concreteCreateUpdateInputRegex = new RegExp( - `(${concreteModelNames.join('|')})(Unchecked)?(Create|Update).*Input` - ); - const match = typeName.match(concreteCreateUpdateInputRegex); + const match = typeName.match(this.ModelCreateUpdateInputRegex); if (match) { const modelName = match[1]; - const record = delegateInfo.find(([, concretes]) => concretes.some((c) => c.name === modelName)); - if (record) { - // remove all discriminator fields recursively - const delegateOfConcrete = record[0]; - const discriminators = this.getDiscriminatorFieldsRecursively(delegateOfConcrete); - discriminators.forEach((discriminatorDecl) => { - const discriminatorNode = this.findNamedProperty(typeAlias, discriminatorDecl.name); - if (discriminatorNode) { - source = source.replace(discriminatorNode.getText(), ''); + const dataModel = this.model.declarations.find( + (d): d is DataModel => isDataModel(d) && d.name === modelName + ); + + if (!dataModel) { + return source; + } + + for (const field of dataModel.fields) { + if (isDiscriminatorField(field)) { + const fieldDef = this.findNamedProperty(typeAlias, field.name); + if (fieldDef) { + source = source.replace(fieldDef.getText(), ''); } - }); + } } } return source; @@ -538,6 +543,10 @@ export function enhance(prisma: any, context?: EnhancementContext<${authTypePara return source; } + private readonly CreateUpdateWithoutDelegateRelationRegex = new RegExp( + `(.+)(Create|Update)Without${upperCaseFirst(DELEGATE_AUX_RELATION_PREFIX)}_(.+)Input` + ); + private removeDelegateFieldsFromNestedMutationInput( typeAlias: TypeAliasDeclaration, _delegateInfo: DelegateInfo, @@ -548,8 +557,7 @@ export function enhance(prisma: any, context?: EnhancementContext<${authTypePara // remove delegate model fields (and corresponding fk fields) from // create/update input types nested inside concrete models - const regex = new RegExp(`(.+)(Create|Update)Without${upperCaseFirst(DELEGATE_AUX_RELATION_PREFIX)}_(.+)Input`); - const match = name.match(regex); + const match = name.match(this.CreateUpdateWithoutDelegateRelationRegex); if (!match) { return source; } @@ -618,22 +626,6 @@ export function enhance(prisma: any, context?: EnhancementContext<${authTypePara return isReferenceExpr(arg) ? (arg.target.ref as DataModelField) : undefined; } - private getDiscriminatorFieldsRecursively(delegate: DataModel, result: DataModelField[] = []) { - if (isDelegateModel(delegate)) { - const discriminator = this.getDiscriminatorField(delegate); - if (discriminator) { - result.push(discriminator); - } - - for (const superType of delegate.superTypes) { - if (superType.ref) { - result.push(...this.getDiscriminatorFieldsRecursively(superType.ref, result)); - } - } - } - return result; - } - private async saveSourceFile(sf: SourceFile) { if (this.options.preserveTsFiles) { await sf.save(); diff --git a/packages/schema/src/plugins/enhancer/index.ts b/packages/schema/src/plugins/enhancer/index.ts index 79e8fd6e6..8d05c50f4 100644 --- a/packages/schema/src/plugins/enhancer/index.ts +++ b/packages/schema/src/plugins/enhancer/index.ts @@ -24,9 +24,11 @@ const run: PluginFunction = async (model, options, _dmmf, globalOptions) => { let prismaClientPath: string | undefined; if (dmmf) { // a logical client is generated - if (typeof options.output === 'string') { + if (options.output || globalOptions?.output) { + // handle custom output path + // get the absolute path of the prisma client types - const prismaClientPathAbs = path.resolve(options.output, 'models'); + const prismaClientPathAbs = path.resolve(outDir, 'models'); // resolve it relative to the schema path prismaClientPath = path.relative(path.dirname(options.schemaPath), prismaClientPathAbs); diff --git a/packages/schema/src/plugins/enhancer/model-meta/index.ts b/packages/schema/src/plugins/enhancer/model-meta/index.ts index 9939ae346..38757ae6f 100644 --- a/packages/schema/src/plugins/enhancer/model-meta/index.ts +++ b/packages/schema/src/plugins/enhancer/model-meta/index.ts @@ -13,5 +13,6 @@ export async function generate(model: Model, options: PluginOptions, project: Pr output: outFile, generateAttributes: true, preserveTsFiles, + shortNameMap: options.shortNameMap, }); } diff --git a/packages/schema/src/plugins/prisma/index.ts b/packages/schema/src/plugins/prisma/index.ts index aeab332c3..d3a9c120a 100644 --- a/packages/schema/src/plugins/prisma/index.ts +++ b/packages/schema/src/plugins/prisma/index.ts @@ -1,6 +1,7 @@ -import { PluginError, PluginFunction, getLiteral, resolvePath } from '@zenstackhq/sdk'; +import { PluginError, type PluginFunction, type PluginOptions, getLiteral, resolvePath } from '@zenstackhq/sdk'; import { GeneratorDecl, isGeneratorDecl } from '@zenstackhq/sdk/ast'; import { getDMMF } from '@zenstackhq/sdk/prisma'; +import colors from 'colors'; import fs from 'fs'; import path from 'path'; import stripColor from 'strip-color'; @@ -8,7 +9,6 @@ import telemetry from '../../telemetry'; import { execPackage } from '../../utils/exec-utils'; import { findUp } from '../../utils/pkg-utils'; import { PrismaSchemaGenerator } from './schema-generator'; -import colors from 'colors'; export const name = 'Prisma'; export const description = 'Generating Prisma schema'; @@ -19,7 +19,8 @@ const run: PluginFunction = async (model, options, _dmmf, _globalOptions) => { ? resolvePath(options.output as string, options) : getDefaultPrismaOutputFile(options.schemaPath); - const warnings = await new PrismaSchemaGenerator(model).generate({ ...options, output }); + const mergedOptions = { ...options, output } as unknown as PluginOptions; + const { warnings, shortNameMap } = await new PrismaSchemaGenerator(model).generate(mergedOptions); let prismaClientPath = '@prisma/client'; if (options.generateClient !== false) { @@ -74,7 +75,7 @@ const run: PluginFunction = async (model, options, _dmmf, _globalOptions) => { datamodel: fs.readFileSync(output, 'utf-8'), }); - return { warnings, dmmf, prismaClientPath }; + return { warnings, dmmf, prismaClientPath, shortNameMap }; }; function getDefaultPrismaOutputFile(schemaPath: string) { diff --git a/packages/schema/src/plugins/prisma/schema-generator.ts b/packages/schema/src/plugins/prisma/schema-generator.ts index d84b3d0a9..54d111d88 100644 --- a/packages/schema/src/plugins/prisma/schema-generator.ts +++ b/packages/schema/src/plugins/prisma/schema-generator.ts @@ -101,8 +101,8 @@ export class PrismaSchemaGenerator { private mode: 'logical' | 'physical' = 'physical'; - // a mapping from shortened names to their original full names - private shortNameMap = new Map(); + // a mapping from full names to shortened names + private shortNameMap = new Map(); constructor(private readonly zmodel: Model) {} @@ -160,7 +160,7 @@ export class PrismaSchemaGenerator { } } - return warnings; + return { warnings, shortNameMap: this.shortNameMap }; } private generateDataSource(prisma: PrismaModel, dataSource: DataSource) { @@ -318,7 +318,7 @@ export class PrismaSchemaGenerator { // generate an optional relation field in delegate base model to each concrete model concreteModels.forEach((concrete) => { - const auxName = `${DELEGATE_AUX_RELATION_PREFIX}_${this.truncate(lowerCaseFirst(concrete.name))}`; + const auxName = this.truncate(`${DELEGATE_AUX_RELATION_PREFIX}_${lowerCaseFirst(concrete.name)}`); model.addField(auxName, new ModelFieldType(concrete.name, false, true)); }); } @@ -339,7 +339,7 @@ export class PrismaSchemaGenerator { const idFields = getIdFields(base); // add relation fields - const relationField = `${DELEGATE_AUX_RELATION_PREFIX}_${this.truncate(lowerCaseFirst(base.name))}`; + const relationField = this.truncate(`${DELEGATE_AUX_RELATION_PREFIX}_${lowerCaseFirst(base.name)}`); model.addField(relationField, base.name, [ new PrismaFieldAttribute('@relation', [ new PrismaAttributeArg( @@ -403,9 +403,11 @@ export class PrismaSchemaGenerator { concreteModels.forEach((concrete) => { // aux relation name format: delegate_aux_[model]_[relationField]_[concrete] // e.g., delegate_aux_User_myAsset_Video - const auxRelationName = `${dataModel.name}_${field.name}_${concrete.name}`; + const auxRelationName = this.truncate( + `${DELEGATE_AUX_RELATION_PREFIX}_${dataModel.name}_${field.name}_${concrete.name}` + ); const auxRelationField = model.addField( - `${DELEGATE_AUX_RELATION_PREFIX}_${this.truncate(auxRelationName)}`, + auxRelationName, new ModelFieldType(concrete.name, field.type.array, field.type.optional) ); @@ -442,7 +444,10 @@ export class PrismaSchemaGenerator { const addedRel = new PrismaFieldAttribute('@relation', [ // use field name as relation name for disambiguation - new PrismaAttributeArg(undefined, new AttributeArgValue('String', nameArg?.value || auxRelationField.name)), + new PrismaAttributeArg( + undefined, + new AttributeArgValue('String', nameArg?.value || auxRelationField.name) + ), new PrismaAttributeArg('fields', fieldsArg), new PrismaAttributeArg('references', referencesArg), ]); @@ -485,9 +490,12 @@ export class PrismaSchemaGenerator { // generate a fk field based on the original fk field const addedFkField = this.generateModelField(model, origForeignKey); + // `@map` attribute should not be inherited + addedFkField.attributes = addedFkField.attributes.filter((attr) => !('name' in attr && attr.name === '@map')); + // fix its name const addedFkFieldName = `${dataModel.name}_${origForeignKey.name}_${concreteModel.name}`; - addedFkField.name = `${DELEGATE_AUX_RELATION_PREFIX}_${this.truncate(addedFkFieldName)}`; + addedFkField.name = this.truncate(`${DELEGATE_AUX_RELATION_PREFIX}_${addedFkFieldName}`); // we also need to make sure `@unique` constraint's `map` parameter is fixed to avoid conflict const uniqueAttr = addedFkField.attributes.find( @@ -495,7 +503,7 @@ export class PrismaSchemaGenerator { ) as PrismaFieldAttribute; if (uniqueAttr) { const mapArg = uniqueAttr.args.find((arg) => arg.name === 'map'); - const constraintName = `${addedFkField.name}_unique`; + const constraintName = this.truncate(`${addedFkField.name}_unique`); if (mapArg) { mapArg.value = new AttributeArgValue('String', constraintName); } else { @@ -557,21 +565,29 @@ export class PrismaSchemaGenerator { return name; } - const shortName = name.slice(0, IDENTIFIER_NAME_MAX_LENGTH); - const entry = this.shortNameMap.get(shortName); - if (!entry) { - this.shortNameMap.set(shortName, [name]); - return `${shortName}_0`; - } else { - const index = entry.findIndex((n) => n === name); - if (index >= 0) { - return `${shortName}_${index}`; - } else { - const newIndex = entry.length; - entry.push(name); - return `${shortName}_${newIndex}`; + const existing = this.shortNameMap.get(name); + if (existing) { + return existing; + } + + const baseName = name.slice(0, IDENTIFIER_NAME_MAX_LENGTH); + let index = 0; + let shortName = `${baseName}_${index}`; + + // eslint-disable-next-line no-constant-condition + while (true) { + const conflict = Array.from(this.shortNameMap.values()).find((v) => v === shortName); + if (!conflict) { + this.shortNameMap.set(name, shortName); + break; } + + // try next index + index++; + shortName = `${baseName}_${index}`; } + + return shortName; } private nameRelationsInheritedFromDelegate(model: PrismaDataModel, decl: DataModel) { @@ -620,7 +636,7 @@ export class PrismaSchemaGenerator { // relation name format: delegate_aux_[relationType]_[oppositeRelationField]_[concrete] const relAttr = getAttribute(f, '@relation'); const name = `${fieldType.name}_${oppositeRelationField.name}_${decl.name}`; - const relName = `${DELEGATE_AUX_RELATION_PREFIX}_${this.truncate(name)}`; + const relName = this.truncate(`${DELEGATE_AUX_RELATION_PREFIX}_${name}`); if (relAttr) { const nameArg = getAttributeArg(relAttr, 'name'); diff --git a/packages/schema/src/plugins/zod/generator.ts b/packages/schema/src/plugins/zod/generator.ts index 91f152af8..fecf69ced 100644 --- a/packages/schema/src/plugins/zod/generator.ts +++ b/packages/schema/src/plugins/zod/generator.ts @@ -5,6 +5,7 @@ import { ensureEmptyDir, getDataModels, hasAttribute, + isDiscriminatorField, isEnumFieldReference, isForeignKeyField, isFromStdlib, @@ -368,6 +369,13 @@ export function ${refineFuncName}(schema: z.ZodType isDiscriminatorField(field)); + const omitDiscriminators = + delegateFields.length > 0 + ? `.omit({ ${delegateFields.map((f) => `${f.name}: true`).join(', ')} })` + : ''; + //////////////////////////////////////////////// // 1. Model schema //////////////////////////////////////////////// @@ -429,7 +437,7 @@ export const ${upperCaseFirst(model.name)}Schema = ${modelSchema}; //////////////////////////////////////////////// // schema for validating prisma create input (all fields optional) - let prismaCreateSchema = this.makePassthrough(this.makePartial('baseSchema')); + let prismaCreateSchema = this.makePassthrough(this.makePartial(`baseSchema${omitDiscriminators}`)); if (refineFuncName) { prismaCreateSchema = `${refineFuncName}(${prismaCreateSchema})`; } @@ -445,6 +453,7 @@ export const ${upperCaseFirst(model.name)}PrismaCreateSchema = ${prismaCreateSch // note numeric fields can be simple update or atomic operations let prismaUpdateSchema = `z.object({ ${scalarFields + .filter((f) => !isDiscriminatorField(f)) .map((field) => { let fieldSchema = makeFieldSchema(field); if (field.type.type === 'Int' || field.type.type === 'Float') { @@ -455,9 +464,6 @@ export const ${upperCaseFirst(model.name)}PrismaCreateSchema = ${prismaCreateSch .join(',\n')} })`; prismaUpdateSchema = this.makePartial(prismaUpdateSchema); - if (refineFuncName) { - prismaUpdateSchema = `${refineFuncName}(${prismaUpdateSchema})`; - } writer.writeLine( ` /** @@ -472,7 +478,7 @@ export const ${upperCaseFirst(model.name)}PrismaUpdateSchema = ${prismaUpdateSch // 3. Create schema //////////////////////////////////////////////// - let createSchema = 'baseSchema'; + let createSchema = `baseSchema${omitDiscriminators}`; const fieldsWithDefault = scalarFields.filter( (field) => hasAttribute(field, '@default') || hasAttribute(field, '@updatedAt') || field.type.array ); @@ -524,7 +530,7 @@ export const ${upperCaseFirst(model.name)}CreateSchema = ${createSchema}; //////////////////////////////////////////////// // for update all fields are optional - let updateSchema = this.makePartial('baseSchema'); + let updateSchema = this.makePartial(`baseSchema${omitDiscriminators}`); // export schema with only scalar fields: `[Model]UpdateScalarSchema` const updateScalarSchema = `${upperCaseFirst(model.name)}UpdateScalarSchema`; diff --git a/packages/schema/src/plugins/zod/transformer.ts b/packages/schema/src/plugins/zod/transformer.ts index 09804f42b..ca714f1ad 100644 --- a/packages/schema/src/plugins/zod/transformer.ts +++ b/packages/schema/src/plugins/zod/transformer.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { indentString, type PluginOptions } from '@zenstackhq/sdk'; -import type { Model } from '@zenstackhq/sdk/ast'; +import { indentString, isDiscriminatorField, type PluginOptions } from '@zenstackhq/sdk'; +import { DataModel, isDataModel, type Model } from '@zenstackhq/sdk/ast'; import { checkModelHasModelRelation, findModelByName, isAggregateInputType } from '@zenstackhq/sdk/dmmf-helpers'; import { supportCreateMany, type DMMF as PrismaDMMF } from '@zenstackhq/sdk/prisma'; import path from 'path'; @@ -90,8 +90,31 @@ export default class Transformer { return `${this.name}.schema`; } + private delegateCreateUpdateInputRegex = /(\S+)(Unchecked)?(Create|Update).*Input/; + generateObjectSchemaFields(generateUnchecked: boolean) { - const zodObjectSchemaFields = this.fields + let fields = this.fields; + + // exclude discriminator fields from create/update input schemas + const createUpdateMatch = this.delegateCreateUpdateInputRegex.exec(this.name); + if (createUpdateMatch) { + const modelName = createUpdateMatch[1]; + const dataModel = this.zmodel.declarations.find( + (d): d is DataModel => isDataModel(d) && d.name === modelName + ); + if (dataModel) { + const discriminatorFields = dataModel.fields.filter(isDiscriminatorField); + if (discriminatorFields.length > 0) { + fields = fields.filter((field) => { + return !discriminatorFields.some( + (discriminatorField) => discriminatorField.name === field.name + ); + }); + } + } + } + + const zodObjectSchemaFields = fields .map((field) => this.generateObjectSchemaField(field, generateUnchecked)) .flatMap((item) => item) .map((item) => { diff --git a/packages/schema/src/res/stdlib.zmodel b/packages/schema/src/res/stdlib.zmodel index 1d2a88ba7..feee12cac 100644 --- a/packages/schema/src/res/stdlib.zmodel +++ b/packages/schema/src/res/stdlib.zmodel @@ -402,10 +402,10 @@ enum MSSQLServerTypes { attribute @db.String(_ x: Int?) @@@targetField([StringField]) @@@prisma attribute @db.Text() @@@targetField([StringField]) @@@prisma attribute @db.NText() @@@targetField([StringField]) @@@prisma -attribute @db.Char(_ x: Int) @@@targetField([StringField]) @@@prisma -attribute @db.NChar(_ x: Int) @@@targetField([StringField]) @@@prisma -attribute @db.VarChar(_ x: Any) @@@targetField([StringField]) @@@prisma -attribute @db.NVarChar(_ x: Any) @@@targetField([StringField]) @@@prisma +attribute @db.Char(_ x: Int?) @@@targetField([StringField]) @@@prisma +attribute @db.NChar(_ x: Int?) @@@targetField([StringField]) @@@prisma +attribute @db.VarChar(_ x: Any?) @@@targetField([StringField]) @@@prisma +attribute @db.NVarChar(_ x: Any?) @@@targetField([StringField]) @@@prisma attribute @db.CatalogSingleChar() @@@targetField([StringField]) @@@prisma attribute @db.TinyText() @@@targetField([StringField]) @@@prisma attribute @db.MediumText() @@@targetField([StringField]) @@@prisma diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts index 63254047f..af7cf576c 100644 --- a/packages/schema/tests/generator/expression-writer.test.ts +++ b/packages/schema/tests/generator/expression-writer.test.ts @@ -1332,7 +1332,9 @@ async function check(schema: string, getExpr: (model: DataModel) => Expression, const model = await loadModel(schema); const expr = getExpr(model.declarations.find((d) => isDataModel(d) && d.name === 'Test') as DataModel); - const project = new Project(); + const project = new Project({ + compilerOptions: { skipLibCheck: true }, + }); const { name: sourcePath } = tmp.fileSync({ postfix: '.ts' }); const sf = project.createSourceFile(sourcePath, undefined, { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 0ea247b7b..e00f53fdb 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "2.3.3", + "version": "2.4.0", "description": "ZenStack plugin development SDK", "main": "index.js", "scripts": { diff --git a/packages/sdk/src/model-meta-generator.ts b/packages/sdk/src/model-meta-generator.ts index 3529c6ec8..c0b266a21 100644 --- a/packages/sdk/src/model-meta-generator.ts +++ b/packages/sdk/src/model-meta-generator.ts @@ -54,6 +54,11 @@ export type ModelMetaGeneratorOptions = { * Whether to preserve the pre-compilation TypeScript files */ preserveTsFiles?: boolean; + + /** + * Map from full names to shortened names, used for extra fields/relations generated by ZenStack + */ + shortNameMap?: Map; }; export async function generate(project: Project, models: DataModel[], options: ModelMetaGeneratorOptions) { @@ -84,6 +89,7 @@ function generateModelMetadata( writeModels(sourceFile, writer, dataModels, options); writeDeleteCascade(writer, dataModels); writeAuthModel(writer, dataModels); + writeShortNameMap(options, writer); }); } @@ -125,6 +131,7 @@ function writeAuthModel(writer: CodeBlockWriter, dataModels: DataModel[]) { const authModel = getAuthModel(dataModels); if (authModel) { writer.writeLine(`authModel: '${authModel.name}'`); + writer.writeLine(','); } } @@ -529,3 +536,15 @@ function isAutoIncrement(field: DataModelField) { return isInvocationExpr(arg) && arg.function.$refText === 'autoincrement'; } + +function writeShortNameMap(options: ModelMetaGeneratorOptions, writer: CodeBlockWriter) { + if (options.shortNameMap && options.shortNameMap.size > 0) { + writer.write('shortNameMap:'); + writer.block(() => { + for (const [key, value] of options.shortNameMap!) { + writer.write(`${key}: '${value}',`); + } + }); + writer.write(','); + } +} diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index a6a4b8629..92c099717 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -30,6 +30,12 @@ export type PluginOptions = { * PrismaClient import path, either relative to `schemaPath` or absolute */ prismaClientPath?: string; + + /** + * An optional map of full names to shortened names + * @private + */ + shortNameMap?: Map; } & PluginDeclaredOptions; /** @@ -73,6 +79,12 @@ export type PluginResult = { * @private */ dmmf?: DMMF.Document; + + /** + * An optional map of full names to shortened names + * @private + */ + shortNameMap?: Map; }; /** diff --git a/packages/server/package.json b/packages/server/package.json index 68ab4a0c0..cf610e9d3 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/server", - "version": "2.3.3", + "version": "2.4.0", "displayName": "ZenStack Server-side Adapters", "description": "ZenStack server-side adapters", "homepage": "https://zenstack.dev", diff --git a/packages/server/src/api/rest/index.ts b/packages/server/src/api/rest/index.ts index 530bfbcc9..022ac72a7 100644 --- a/packages/server/src/api/rest/index.ts +++ b/packages/server/src/api/rest/index.ts @@ -1534,7 +1534,16 @@ class RequestHandler extends APIHandlerBase { } return { isEmpty: value === 'true' ? true : false }; default: - return op ? { [op]: coerced } : { equals: coerced }; + if (op === undefined) { + // regular filter, split value by comma + const values = value + .split(',') + .filter((i) => i) + .map((v) => this.coerce(fieldInfo.type, v)); + return values.length > 1 ? { in: values } : { equals: values[0] }; + } else { + return { [op]: coerced }; + } } } } diff --git a/packages/server/tests/api/rest.test.ts b/packages/server/tests/api/rest.test.ts index bdb2f4d8c..a587678c3 100644 --- a/packages/server/tests/api/rest.test.ts +++ b/packages/server/tests/api/rest.test.ts @@ -368,6 +368,16 @@ describe('REST server tests', () => { expect(r.body.data).toHaveLength(1); expect(r.body.data[0]).toMatchObject({ id: 'user2' }); + // multi-id filter + r = await handler({ + method: 'get', + path: '/user', + query: { ['filter[id]']: 'user1,user2' }, + prisma, + }); + expect(r.status).toBe(200); + expect(r.body.data).toHaveLength(2); + // String filter r = await handler({ method: 'get', diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 613bce271..d5c8493df 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "2.3.3", + "version": "2.4.0", "description": "ZenStack Test Tools", "main": "index.js", "private": true, diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index c830b1a6d..919a9d411 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -237,7 +237,7 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { } if (opt.pushDb) { - run('npx prisma db push'); + run('npx prisma db push --skip-generate'); } if (opt.pulseApiKey) { @@ -336,9 +336,6 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { prisma, { user }, { - policy, - modelMeta, - zodSchemas, logPrismaQuery: opt.logPrismaQuery, transactionTimeout: 1000000, kinds: opt.enhancements, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ceb0b0e4..6cc1d96ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,8 +89,8 @@ importers: packages/misc/redwood: dependencies: '@redwoodjs/cli-helpers': - specifier: ^6.6.0 - version: 6.6.4(enquirer@2.4.1) + specifier: ^7.7.3 + version: 7.7.3(enquirer@2.4.1) '@zenstackhq/runtime': specifier: workspace:* version: link:../../runtime/dist @@ -103,6 +103,9 @@ importers: listr2: specifier: ^6.0.0 version: 6.6.1(enquirer@2.4.1) + semver: + specifier: ^7.5.2 + version: 7.6.2 terminal-link: specifier: ^2.0.0 version: 2.1.1 @@ -114,8 +117,8 @@ importers: version: 17.7.2 devDependencies: '@redwoodjs/graphql-server': - specifier: ^6.6.0 - version: 6.6.4(@escape.tech/graphql-armor-types@0.5.0)(prisma@5.16.1) + specifier: ^7.7.3 + version: 7.7.3(@escape.tech/graphql-armor-types@0.5.0)(prisma@5.16.1) '@types/yargs': specifier: ^17.0.32 version: 17.0.32 @@ -1158,8 +1161,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime-corejs3@7.23.6': - resolution: {integrity: sha512-Djs/ZTAnpyj0nyg7p1J6oiE/tZ9G2stqAFlLGZynrW+F3k2w2jGK2mLOBxzYIOcZYA89+c3d3wXKpYLcpwcU6w==} + '@babel/runtime-corejs3@7.24.5': + resolution: {integrity: sha512-GWO0mgzNMLWaSYM4z4NVIuY0Cd1fl8cPnuetuddu5w/qGuvt5Y7oUi/kvvQGK9xgOkFJDQX2heIvTRn/OQ1XTg==} engines: {node: '>=6.9.0'} '@babel/runtime@7.24.7': @@ -1268,32 +1271,32 @@ packages: resolution: {integrity: sha512-wxA8EyE1fPnlbP0nC/SFI7uU8wSNf4YjxZhAPu0P63QbgIvqHtHsH4L3/u+rsTruzhk3OvNRgQyLsMfaR9uzAQ==} engines: {node: '>=18.0.0'} - '@envelop/depth-limit@3.0.3': - resolution: {integrity: sha512-fDyLQDVlSG3DPJmzwb6pdnpK67rNy9KLkN8LTthZOmEXGz0WuVP1EOOnjYY7PE566BySbNXl5z1lTeOPHR1KUw==} - engines: {node: '>=16.0.0'} + '@envelop/depth-limit@4.0.0': + resolution: {integrity: sha512-5lr6x3sx4+CqtrLjnK0UxWrtq0EkAyNHCR25du2hrlvszcq4IA/GjyQEpAy/3J6JYKgRJ4cZ+w5V6ppo2q6LLA==} + engines: {node: '>=18.0.0'} peerDependencies: - '@envelop/core': ^4.0.3 + '@envelop/core': ^5.0.0 graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - '@envelop/disable-introspection@5.0.3': - resolution: {integrity: sha512-PoKbaeCVGdgkgQUGl46L33SBB/bYKVOe1QHEW8++n9lnTTTLJTyo8EbtJb++UcuQCaaKn277fkUyc9sR9fOh5w==} - engines: {node: '>=16.0.0'} + '@envelop/disable-introspection@6.0.0': + resolution: {integrity: sha512-+DxCvKdzsHat/aWr6dqDsebbGk6ZGiM7VZlnpwKS8g4+PDHg7AfMBnDP8CtUODgifU+kgZME4TFzU288MNpNDg==} + engines: {node: '>=18.0.0'} peerDependencies: - '@envelop/core': ^4.0.3 + '@envelop/core': ^5.0.0 graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - '@envelop/filter-operation-type@5.0.3': - resolution: {integrity: sha512-z6gYb+y4O6lsOH+KMwrfYzN7b0ybGAZWSk/++z913nOSxtPm42sYdCQ56upw+tN/WHNxwKejCO54TjhrSdUX9Q==} - engines: {node: '>=16.0.0'} + '@envelop/filter-operation-type@6.0.0': + resolution: {integrity: sha512-eL4FWCNkQ/XUJX2pyHd3xfu9erqj08fVUWDpid8giuYHu5fx+CrVLUjwekkFOAYIIXdxYLdePrfLCrouX2XeWQ==} + engines: {node: '>=18.0.0'} peerDependencies: - '@envelop/core': ^4.0.3 + '@envelop/core': ^5.0.0 graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - '@envelop/on-resolve@3.0.3': - resolution: {integrity: sha512-Mo2w3CHmyLCScFuIO2VS2Co44vlPSc4zwujz0x+/zyaJ+eCwBQMRuE9u+9ORjvKImNxrbXI9FQVNlbF0iDk4iQ==} - engines: {node: '>=16.0.0'} + '@envelop/on-resolve@4.1.0': + resolution: {integrity: sha512-2AXxf8jbBIepBUiY0KQtyCO6gnT7LKBEYdaARZBJ7ujy1+iQHQPORKvAwl51kIdD6v5x38eldZnm7A19jFimOg==} + engines: {node: '>=18.0.0'} peerDependencies: - '@envelop/core': ^4.0.3 + '@envelop/core': ^5.0.0 graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 '@envelop/types@4.0.1': @@ -1806,20 +1809,14 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/merge@9.0.1': - resolution: {integrity: sha512-hIEExWO9fjA6vzsVjJ3s0cCQ+Q/BEeMVJZtMXd7nbaVefVy0YDyYlEkeoYYNV3NVVvu1G9lr6DM1Qd0DGo9Caw==} - engines: {node: '>=16.0.0'} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/merge@9.0.4': resolution: {integrity: sha512-MivbDLUQ+4Q8G/Hp/9V72hbn810IJDEZQ57F01sHnlrrijyadibfVhaQfW/pNH+9T/l8ySZpaR/DpL5i+ruZ+g==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/schema@10.0.2': - resolution: {integrity: sha512-TbPsIZnWyDCLhgPGnDjt4hosiNU2mF/rNtSk5BVaXWnZqvKJ6gzJV4fcHcvhRIwtscDMW2/YTnK6dLVnk8pc4w==} + '@graphql-tools/schema@10.0.3': + resolution: {integrity: sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -1830,8 +1827,8 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/utils@10.0.11': - resolution: {integrity: sha512-vVjXgKn6zjXIlYBd7yJxCVMYGb5j18gE3hx3Qw3mNsSEsYQXbJbPdlwb7Fc9FogsJei5AaqiQerqH4kAosp1nQ==} + '@graphql-tools/utils@10.2.0': + resolution: {integrity: sha512-HYV7dO6pNA2nGKawygaBpk8y+vXOUjjzzO43W/Kb7EPRmXUEQKjHxPYRvQbiF72u1N3XxwGK5jnnFk9WVhUwYw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -1847,26 +1844,22 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-yoga/logger@1.0.0': - resolution: {integrity: sha512-JYoxwnPggH2BfO+dWlWZkDeFhyFZqaTRGLvFhy+Pjp2UxitEW6nDrw+pEDw/K9tJwMjIFMmTT9VfTqrnESmBHg==} - engines: {node: '>=16.0.0'} - '@graphql-yoga/logger@2.0.0': resolution: {integrity: sha512-Mg8psdkAp+YTG1OGmvU+xa6xpsAmSir0hhr3yFYPyLNwzUj95DdIwsMpKadDj9xDpYgJcH3Hp/4JMal9DhQimA==} engines: {node: '>=18.0.0'} - '@graphql-yoga/subscription@4.0.0': - resolution: {integrity: sha512-0qsN/BPPZNMoC2CZ8i+P6PgiJyHh1H35aKDt37qARBDaIOKDQuvEOq7+4txUKElcmXi7DYFo109FkhSQoEajrg==} - engines: {node: '>=16.0.0'} + '@graphql-yoga/plugin-persisted-operations@3.3.1': + resolution: {integrity: sha512-2FteUIepgAZL5q2JSPbTFozba4T6v34skb6I7FiqZp7XwNnp8Da9Jf5BpcwUb4buP51FzbO5WJW1UMyNptxuOA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@graphql-tools/utils': ^10.0.0 + graphql: ^15.2.0 || ^16.0.0 + graphql-yoga: ^5.3.1 '@graphql-yoga/subscription@5.0.1': resolution: {integrity: sha512-1wCB1DfAnaLzS+IdoOzELGGnx1ODEg9nzQXFh4u2j02vAnne6d+v4A7HIH9EqzVdPLoAaMKXCZUUdKs+j3z1fg==} engines: {node: '>=18.0.0'} - '@graphql-yoga/typed-event-target@2.0.0': - resolution: {integrity: sha512-oA/VGxGmaSDym1glOHrltw43qZsFwLLjBwvh57B79UKX/vo3+UQcRgOyE44c5RP7DCYjkrC2tuArZmb6jCzysw==} - engines: {node: '>=16.0.0'} - '@graphql-yoga/typed-event-target@3.0.0': resolution: {integrity: sha512-w+liuBySifrstuHbFrHoHAEyVnDFVib+073q8AeAJ/qqJfvFvAwUPLLtNohR/WDVRgSasfXtl3dcNuVJWN+rjg==} engines: {node: '>=18.0.0'} @@ -1897,9 +1890,6 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead - '@iarna/toml@2.2.5': - resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} - '@ioredis/commands@1.2.0': resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} @@ -2225,8 +2215,8 @@ packages: resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} - '@opentelemetry/api@1.7.0': - resolution: {integrity: sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==} + '@opentelemetry/api@1.8.0': + resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} engines: {node: '>=8.0.0'} '@opentelemetry/api@1.9.0': @@ -2437,8 +2427,8 @@ packages: '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} - '@prisma/client@5.17.0': - resolution: {integrity: sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw==} + '@prisma/client@5.14.0': + resolution: {integrity: sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg==} engines: {node: '>=16.13'} peerDependencies: prisma: '*' @@ -2446,8 +2436,8 @@ packages: prisma: optional: true - '@prisma/client@5.7.0': - resolution: {integrity: sha512-cZmglCrfNbYpzUtz7HscVHl38e9CrUs31nrVoGUK1nIPXGgt8hT4jj2s657UXcNdQ/jBUxDgGmHyu2Nyrq1txg==} + '@prisma/client@5.17.0': + resolution: {integrity: sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw==} engines: {node: '>=16.13'} peerDependencies: prisma: '*' @@ -2455,14 +2445,17 @@ packages: prisma: optional: true + '@prisma/debug@5.14.0': + resolution: {integrity: sha512-iq56qBZuFfX3fCxoxT8gBX33lQzomBU0qIUaEj1RebsKVz1ob/BVH1XSBwwwvRVtZEV1b7Fxx2eVu34Ge/mg3w==} + '@prisma/debug@5.16.1': resolution: {integrity: sha512-JsNgZAg6BD9RInLSrg7ZYzo11N7cVvYArq3fHGSD89HSgtN0VDdjV6bib7YddbcO6snzjchTiLfjeTqBjtArVQ==} '@prisma/debug@5.17.0': resolution: {integrity: sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg==} - '@prisma/debug@5.7.0': - resolution: {integrity: sha512-tZ+MOjWlVvz1kOEhNYMa4QUGURY+kgOUBqLHYIV8jmCsMuvA1tWcn7qtIMLzYWCbDcQT4ZS8xDgK0R2gl6/0wA==} + '@prisma/engines-version@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48': + resolution: {integrity: sha512-ip6pNkRo1UxWv+6toxNcYvItNYaqQjXdFNGJ+Nuk2eYtRoEdoF13wxo7/jsClJFFenMPVNVqXQDV0oveXnR1cA==} '@prisma/engines-version@5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303': resolution: {integrity: sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw==} @@ -2470,8 +2463,8 @@ packages: '@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': resolution: {integrity: sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg==} - '@prisma/engines-version@5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9': - resolution: {integrity: sha512-V6tgRVi62jRwTm0Hglky3Scwjr/AKFBFtS+MdbsBr7UOuiu1TKLPc6xfPiyEN1+bYqjEtjxwGsHgahcJsd1rNg==} + '@prisma/engines@5.14.0': + resolution: {integrity: sha512-lgxkKZ6IEygVcw6IZZUlPIfLQ9hjSYAtHjZ5r64sCLDgVzsPFCi2XBBJgzPMkOQ5RHzUD4E/dVdpn9+ez8tk1A==} '@prisma/engines@5.16.1': resolution: {integrity: sha512-KkyF3eIUtBIyp5A/rJHCtwQO18OjpGgx18PzjyGcJDY/+vNgaVyuVd+TgwBgeq6NLdd1XMwRCI+58vinHsAdfA==} @@ -2479,8 +2472,8 @@ packages: '@prisma/engines@5.17.0': resolution: {integrity: sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg==} - '@prisma/engines@5.7.0': - resolution: {integrity: sha512-TkOMgMm60n5YgEKPn9erIvFX2/QuWnl3GBo6yTRyZKk5O5KQertXiNnrYgSLy0SpsKmhovEPQb+D4l0SzyE7XA==} + '@prisma/fetch-engine@5.14.0': + resolution: {integrity: sha512-VrheA9y9DMURK5vu8OJoOgQpxOhas3qF0IBHJ8G/0X44k82kc8E0w98HCn2nhnbOOMwbWsJWXfLC2/F8n5u0gQ==} '@prisma/fetch-engine@5.16.1': resolution: {integrity: sha512-oOkjaPU1lhcA/Rvr4GVfd1NLJBwExgNBE36Ueq7dr71kTMwy++a3U3oLd2ZwrV9dj9xoP6LjCcky799D9nEt4w==} @@ -2488,14 +2481,14 @@ packages: '@prisma/fetch-engine@5.17.0': resolution: {integrity: sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q==} - '@prisma/fetch-engine@5.7.0': - resolution: {integrity: sha512-zIn/qmO+N/3FYe7/L9o+yZseIU8ivh4NdPKSkQRIHfg2QVTVMnbhGoTcecbxfVubeTp+DjcbjS0H9fCuM4W04w==} + '@prisma/generator-helper@5.14.0': + resolution: {integrity: sha512-xVc71cmTnPZ0lnSs4FAY6Ta72vFJ3webrQwKMQ2ujr6hDG1VPIEf820T1TOS3ZZQd/OKigNKXnq3co8biz9/qw==} '@prisma/generator-helper@5.17.0': resolution: {integrity: sha512-UcYpNjjQNVHAjIxgjfXnF4fcKU7B2vuzG1L27xIV81xQoGSbxg7v670URBhd0/ZoE8v2Itj2bbuyezY1ViHVaA==} - '@prisma/generator-helper@5.7.0': - resolution: {integrity: sha512-Fn4hJHKGJ49+E8sxpfslRauB3Goa3RAENJ/W25NMR754B9KxvmbCJyE3MT/lIZxML2nGgIdXYUtoDHZHnRaKDw==} + '@prisma/get-platform@5.14.0': + resolution: {integrity: sha512-/yAyBvcEjRv41ynZrhdrPtHgk47xLRRq/o5eWGcUpBJ1YrUZTYB8EoPiopnP7iQrMATK8stXQdPOoVlrzuTQZw==} '@prisma/get-platform@5.16.1': resolution: {integrity: sha512-R4IKnWnMkR2nUAbU5gjrPehdQYUUd7RENFD2/D+xXTNhcqczp0N+WEGQ3ViyI3+6mtVcjjNIMdnUTNyu3GxIgA==} @@ -2503,20 +2496,23 @@ packages: '@prisma/get-platform@5.17.0': resolution: {integrity: sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w==} - '@prisma/get-platform@5.7.0': - resolution: {integrity: sha512-ZeV/Op4bZsWXuw5Tg05WwRI8BlKiRFhsixPcAM+5BKYSiUZiMKIi713tfT3drBq8+T0E1arNZgYSA9QYcglWNA==} + '@prisma/internals@5.14.0': + resolution: {integrity: sha512-s0JRNDmR2bvcyy0toz89jy7SbbjANAs4e9KCReNvSm5czctIaZzDf68tcOXdtH0G7m9mKhVhNPdS9lMky0DhWA==} '@prisma/internals@5.17.0': resolution: {integrity: sha512-lWRniOVLgGckRlBI6U/zqfnuAXo3FbOl4WcU+nPxJWe9nFeJj9TN4vjaerufB9suZLQ+8b2FMeKz3KTdX/CGow==} - '@prisma/internals@5.7.0': - resolution: {integrity: sha512-O9x47W1DECAyvNjYUx6oZHmTX10emKuBgsFHZemUbkIcJdCsp3X8Cy2JMJ5z3hqkRX6a6omMamFsWjuTARoaSw==} + '@prisma/prisma-schema-wasm@5.14.0-17.56ca112d5a19c9925b53af75c3c6b7ada97f9f85': + resolution: {integrity: sha512-SX9vE9dGYBap6xsfJuDE5b2eoA6w1vKsx8QpLUHZR+kIV6GQVUYUboEfkvYYoBVen3s9LqxJ1+LjHL/1MqBZag==} + + '@prisma/prisma-schema-wasm@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48': + resolution: {integrity: sha512-WeTmJ0mK8ALoKJUQFO+465k9lm1JWS4ODUg7akJq1wjgyDU1RTAzDFli8ESmNJlMVgJgoAd6jXmzcnoA0HT9Lg==} '@prisma/prisma-schema-wasm@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': resolution: {integrity: sha512-mlmuu0/IPSjMlMKsqdaVVAbGTJwp5sDMFd3ZFQxl4/K8FvH7tb2uy/lTHF0KyAJbveTiV+1yW9MBWspltXZZtg==} - '@prisma/prisma-schema-wasm@5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9': - resolution: {integrity: sha512-w+HdQtux0dJDEn6BG3fgNn+fXErXiekj9n//uHRAgrmZghockJkhnikOmG8aSXjTb1Tu5DrGasBX+rYX6rHT1w==} + '@prisma/schema-files-loader@5.14.0': + resolution: {integrity: sha512-n1QHR2C63dARKPZe0WPn7biybcBHzXe+BEmiHC5Drq9KPWnpmQtIfGpqm1ZKdvCZfcA5FF3wgpSMPK4LnB0obQ==} '@prisma/schema-files-loader@5.17.0': resolution: {integrity: sha512-rmbJZEvY9nOlLduVQww4fGmYM3aU7BYAw/st0K9QNq9dQoLONgQP7t8dhcOVZbBLyNNQu2k2gJdVXSHSY96b4A==} @@ -2570,32 +2566,35 @@ packages: resolution: {integrity: sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==} engines: {node: '>=18'} - '@redwoodjs/api@6.6.4': - resolution: {integrity: sha512-YE+9IKWtMnQBhnhG4h8KD81xDJEISEcOipVlZyPlJi1+d5uMXClO8N7VLaIfx8HwT433mCIeruhDStquxwuTgQ==} + '@redwoodjs/api@7.7.3': + resolution: {integrity: sha512-rHS+wbevjL36eB3az0McfgDXfEp3gwNUb2gVilHmvrgCmCVvOj5q9O6226rq/NurbMLsHu99oUzpimbIMEIGCw==} hasBin: true peerDependencies: - memjs: 1.3.1 - redis: 4.6.7 + memjs: 1.3.2 + redis: 4.6.14 peerDependenciesMeta: memjs: optional: true redis: optional: true - '@redwoodjs/cli-helpers@6.6.4': - resolution: {integrity: sha512-TYCBeZSpsAl/ccayOup9vv3CaBdQikcWqSNNb6+jeDT6uit1e1BG8YrS98tQEZyiO7hxnS6sBtaCWhnQfLQZ4Q==} + '@redwoodjs/cli-helpers@7.7.3': + resolution: {integrity: sha512-LXWMy0R4aI8YGUvJ5rJbrKXbEbZOD6uRgqyUFA7UV+pnRWrdIMyQ0PBBIl8Fmey3Wk5BAY1eIa44YPbYXlHxwg==} + + '@redwoodjs/context@7.7.3': + resolution: {integrity: sha512-9cee4A21/6l9dPnd3zy5ascGgTH+hDtZgeuOACOpqjz5O51gNhtUGEVXKGPRKbMXSDD1RhvaUiknjzAUzHpO2Q==} - '@redwoodjs/graphql-server@6.6.4': - resolution: {integrity: sha512-I9NQq6esMd3k7uUngoKWZo/X3xRPzmm1OII5kb7iinRGCW1vRM10KydI/t5k6r24kI+lKqf5+Kd/AaCf7VR7gg==} + '@redwoodjs/graphql-server@7.7.3': + resolution: {integrity: sha512-YB/SEGPDD2Qias5/O1p+Qg9P9Te/qTWAD1hDJ8iTGuKylomNjjYxWaptJ863o2oU2xTT6unFeF1W9usCqsoR6w==} - '@redwoodjs/project-config@6.6.4': - resolution: {integrity: sha512-n7BIaV2i20Qff+jfySLNhC9BbkjR11gFLKDmO8L1Sl33msrZw/zWrwTkhtxQwUkoa6XVjpgwI9wxVZyr8CoAYg==} + '@redwoodjs/project-config@7.7.3': + resolution: {integrity: sha512-o0vEppJSg/yZ8Koi4CH17fe6wAzr6yxPJnCAlhfg1dmyj4cAHL23CjYv9Z/VE99GfotZGWoNhycv/ZIba59ZkQ==} - '@redwoodjs/structure@6.6.4': - resolution: {integrity: sha512-Tus10ViA8/JfiD7UQ+5DxNaWrjtv2Z6dNY+f6k3Vo/4FQS0xfeyEzyXqjPDsOPaZr/swFmGBVrThlBuiDyZzgA==} + '@redwoodjs/structure@7.7.3': + resolution: {integrity: sha512-q12TkBWH/xQELinTfhSdG6/s5AFsOwQQrIG+8GqvFHFAtETw2z5U4gTQZwG3fXqHfcfaV+u7d7ysBTYUGGl9yQ==} - '@redwoodjs/telemetry@6.6.4': - resolution: {integrity: sha512-SMH/G2h7LcMMYW6cIc6A3+pPlRa9GZy0htzfhoqXFEGOU4GNcOKGTjouFD2TqKuG2t4GzjNHcwixQc/UNADpGA==} + '@redwoodjs/telemetry@7.7.3': + resolution: {integrity: sha512-2xP6QvBCTog54MVjcqXvYba45A/WpP6wWW9IhnkKANhiroHu3Van7AYAtr03hx2JHVhuSAvV7kEDk8Q4LLa2BQ==} '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} @@ -2997,8 +2996,8 @@ packages: '@types/jsonfile@6.1.4': resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} - '@types/line-column@1.0.0': - resolution: {integrity: sha512-wbw+IDRw/xY/RGy+BL6f4Eey4jsUgHQrMuA4Qj0CSG3x/7C2Oc57pmRoM2z3M4DkylWRz+G1pfX06sCXQm0J+w==} + '@types/line-column@1.0.2': + resolution: {integrity: sha512-099oFQmp/Tlf20xW5XI5R4F69N6lF/zQ09XDzc3R5BOLFlqIotgKoNIyj0HD4fQLWcGDreDJv8k/BkLJscrDrw==} '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} @@ -3322,8 +3321,8 @@ packages: resolution: {integrity: sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==} engines: {node: '>=16.0.0'} - '@whatwg-node/fetch@0.9.14': - resolution: {integrity: sha512-wurZC82zzZwXRDSW0OS9l141DynaJQh7Yt0FD1xZ8niX7/Et/7RoiLiltbVU1fSF1RR9z6ndEaTUQBAmddTm1w==} + '@whatwg-node/fetch@0.9.17': + resolution: {integrity: sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==} engines: {node: '>=16.0.0'} '@whatwg-node/fetch@0.9.18': @@ -4037,8 +4036,8 @@ packages: core-js-pure@3.37.1: resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==} - core-js@3.34.0: - resolution: {integrity: sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==} + core-js@3.37.1: + resolution: {integrity: sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -4376,10 +4375,6 @@ packages: resolution: {integrity: sha512-vwEppIphpFdvaMCaHfCEv9IgwcxMljMw2TnAQBB4VWPvzXQLTb82jwmdOKzlEVUL3gNFT4l4TPKO+Bn+sqcrVQ==} engines: {node: '>=12'} - dotenv@16.3.1: - resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} - engines: {node: '>=12'} - dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} @@ -4441,8 +4436,8 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - envinfo@7.11.0: - resolution: {integrity: sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==} + envinfo@7.13.0: + resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} engines: {node: '>=4'} hasBin: true @@ -5044,8 +5039,8 @@ packages: peerDependencies: graphql: '*' - graphql-scalars@1.22.4: - resolution: {integrity: sha512-ILnv7jq5VKHLUyoaTFX7lgYrjCd6vTee9i8/B+D4zJKJT5TguOl0KkpPEbXHjmeor8AZYrVsrYUHdqRBMX1pjA==} + graphql-scalars@1.23.0: + resolution: {integrity: sha512-YTRNcwitkn8CqYcleKOx9IvedA8JIERn8BRq21nlKgOr4NEcTaWEG0sT+H92eF3ALTFbPgsqfft4cw+MGgv0Gg==} engines: {node: '>=10'} peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 @@ -5056,9 +5051,9 @@ packages: peerDependencies: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - graphql-yoga@4.0.4: - resolution: {integrity: sha512-MvCLhFecYNIKuxAZisPjpIL9lxRYbpgPSNKENDO/8CV3oiFlsLJHZb5dp2sVAeLafXHeZ9TgkijLthUBc1+Jag==} - engines: {node: '>=16.0.0'} + graphql-yoga@5.3.1: + resolution: {integrity: sha512-n918QV6TF7xTjb9ASnozgsr4ydMc08c+x4eRAWKxxWVwSnzdP2xeN2zw1ljIzRD0ccSCNoBajGDKwcZkJDitPA==} + engines: {node: '>=18.0.0'} peerDependencies: graphql: ^15.2.0 || ^16.0.0 @@ -5963,6 +5958,10 @@ packages: lowlight@1.17.0: resolution: {integrity: sha512-vmtBgYKD+QVNy7tIa7ulz5d//Il9R4MooOVh4nkOf9R9Cb/Dk5TXMSTieg/vDulkBkIWj59/BIlyFQxT9X1oAQ==} + lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + engines: {node: 14 || >=16.14} + lru-cache@10.3.0: resolution: {integrity: sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==} engines: {node: 14 || >=16.14} @@ -5977,10 +5976,6 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -6626,9 +6621,6 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pino-abstract-transport@1.1.0: - resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} - pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} @@ -6638,8 +6630,8 @@ packages: pino-std-serializers@7.0.0: resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@8.16.2: - resolution: {integrity: sha512-2advCDGVEvkKu9TTVSa/kWW7Z3htI/sBKEZpqiHk6ive0i/7f5b1rsU8jn0aimxqfnSz5bj/nOYkwhBUn5xxvg==} + pino@8.21.0: + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true pino@9.2.0: @@ -6948,9 +6940,6 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - process-warning@2.3.2: - resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} - process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} @@ -7411,6 +7400,10 @@ packages: smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + smol-toml@1.2.1: + resolution: {integrity: sha512-OtZKrVrGIT+m++lxyF0z5n68nkwdgZotPhy89bfA4T7nSWe0xeQtfbjM1z5VLTilJdWXH46g8i0oAcpQNkzZTg==} + engines: {node: '>= 18', pnpm: '>= 9'} + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -7671,8 +7664,8 @@ packages: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} - systeminformation@5.21.20: - resolution: {integrity: sha512-AyS1fNc+MDoAJtFknFbbo587H8h6yejJwM+H9rVusnOToIEkiMehMyD5JM7o3j55Cto20MawIZrcgNMgd4BfOQ==} + systeminformation@5.22.11: + resolution: {integrity: sha512-aLws5yi4KCHTb0BVvbodQY5bY8eW4asMRDTxTW46hqw9lGjACX6TlLdJrkdoHYRB0qs+MekqEq1zG7WDnWE8Ug==} engines: {node: '>=8.0.0'} os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true @@ -8134,9 +8127,6 @@ packages: urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} - urlpattern-polyfill@9.0.0: - resolution: {integrity: sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==} - use-sync-external-store@1.2.2: resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} peerDependencies: @@ -8375,9 +8365,6 @@ packages: vscode-languageserver-textdocument@1.0.11: resolution: {integrity: sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==} - vscode-languageserver-textdocument@1.0.8: - resolution: {integrity: sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==} - vscode-languageserver-types@3.16.0: resolution: {integrity: sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==} @@ -9000,7 +8987,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/runtime-corejs3@7.23.6': + '@babel/runtime-corejs3@7.24.5': dependencies: core-js-pure: 3.37.1 regenerator-runtime: 0.14.1 @@ -9223,39 +9210,41 @@ snapshots: dependencies: '@envelop/types': 4.0.1 tslib: 2.6.3 + optional: true '@envelop/core@5.0.1': dependencies: '@envelop/types': 5.0.0 tslib: 2.6.3 - '@envelop/depth-limit@3.0.3(@envelop/core@4.0.3)(graphql@16.8.1)': + '@envelop/depth-limit@4.0.0(@envelop/core@5.0.1)(graphql@16.8.1)': dependencies: - '@envelop/core': 4.0.3 + '@envelop/core': 5.0.1 graphql: 16.8.1 graphql-depth-limit: 1.1.0(graphql@16.8.1) tslib: 2.6.3 - '@envelop/disable-introspection@5.0.3(@envelop/core@4.0.3)(graphql@16.8.1)': + '@envelop/disable-introspection@6.0.0(@envelop/core@5.0.1)(graphql@16.8.1)': dependencies: - '@envelop/core': 4.0.3 + '@envelop/core': 5.0.1 graphql: 16.8.1 tslib: 2.6.3 - '@envelop/filter-operation-type@5.0.3(@envelop/core@4.0.3)(graphql@16.8.1)': + '@envelop/filter-operation-type@6.0.0(@envelop/core@5.0.1)(graphql@16.8.1)': dependencies: - '@envelop/core': 4.0.3 + '@envelop/core': 5.0.1 graphql: 16.8.1 tslib: 2.6.3 - '@envelop/on-resolve@3.0.3(@envelop/core@4.0.3)(graphql@16.8.1)': + '@envelop/on-resolve@4.1.0(@envelop/core@5.0.1)(graphql@16.8.1)': dependencies: - '@envelop/core': 4.0.3 + '@envelop/core': 5.0.1 graphql: 16.8.1 '@envelop/types@4.0.1': dependencies: tslib: 2.6.3 + optional: true '@envelop/types@5.0.0': dependencies: @@ -9517,7 +9506,7 @@ snapshots: graphql: 16.8.1 optional: true - '@escape.tech/graphql-armor@2.3.1(@envelop/core@4.0.3)(@escape.tech/graphql-armor-types@0.5.0)': + '@escape.tech/graphql-armor@2.3.1(@envelop/core@5.0.1)(@escape.tech/graphql-armor-types@0.5.0)': dependencies: '@escape.tech/graphql-armor-block-field-suggestions': 2.1.0 '@escape.tech/graphql-armor-cost-limit': 2.1.0 @@ -9527,7 +9516,7 @@ snapshots: '@escape.tech/graphql-armor-max-tokens': 2.2.0 graphql: 16.8.1 optionalDependencies: - '@envelop/core': 4.0.3 + '@envelop/core': 5.0.1 '@escape.tech/graphql-armor-types': 0.5.0 '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': @@ -9580,22 +9569,16 @@ snapshots: tslib: 2.6.3 value-or-promise: 1.0.12 - '@graphql-tools/merge@9.0.1(graphql@16.8.1)': - dependencies: - '@graphql-tools/utils': 10.0.11(graphql@16.8.1) - graphql: 16.8.1 - tslib: 2.6.3 - '@graphql-tools/merge@9.0.4(graphql@16.8.1)': dependencies: '@graphql-tools/utils': 10.2.2(graphql@16.8.1) graphql: 16.8.1 tslib: 2.6.3 - '@graphql-tools/schema@10.0.2(graphql@16.8.1)': + '@graphql-tools/schema@10.0.3(graphql@16.8.1)': dependencies: - '@graphql-tools/merge': 9.0.1(graphql@16.8.1) - '@graphql-tools/utils': 10.0.11(graphql@16.8.1) + '@graphql-tools/merge': 9.0.4(graphql@16.8.1) + '@graphql-tools/utils': 10.2.2(graphql@16.8.1) graphql: 16.8.1 tslib: 2.6.3 value-or-promise: 1.0.12 @@ -9608,7 +9591,7 @@ snapshots: tslib: 2.6.3 value-or-promise: 1.0.12 - '@graphql-tools/utils@10.0.11(graphql@16.8.1)': + '@graphql-tools/utils@10.2.0(graphql@16.8.1)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) cross-inspect: 1.0.0 @@ -9628,20 +9611,15 @@ snapshots: dependencies: graphql: 16.8.1 - '@graphql-yoga/logger@1.0.0': - dependencies: - tslib: 2.6.3 - '@graphql-yoga/logger@2.0.0': dependencies: tslib: 2.6.3 - '@graphql-yoga/subscription@4.0.0': + '@graphql-yoga/plugin-persisted-operations@3.3.1(@graphql-tools/utils@10.2.0(graphql@16.8.1))(graphql-yoga@5.3.1(graphql@16.8.1))(graphql@16.8.1)': dependencies: - '@graphql-yoga/typed-event-target': 2.0.0 - '@repeaterjs/repeater': 3.0.6 - '@whatwg-node/events': 0.1.1 - tslib: 2.6.3 + '@graphql-tools/utils': 10.2.0(graphql@16.8.1) + graphql: 16.8.1 + graphql-yoga: 5.3.1(graphql@16.8.1) '@graphql-yoga/subscription@5.0.1': dependencies: @@ -9650,11 +9628,6 @@ snapshots: '@whatwg-node/events': 0.1.1 tslib: 2.6.3 - '@graphql-yoga/typed-event-target@2.0.0': - dependencies: - '@repeaterjs/repeater': 3.0.6 - tslib: 2.6.3 - '@graphql-yoga/typed-event-target@3.0.0': dependencies: '@repeaterjs/repeater': 3.0.6 @@ -9686,8 +9659,6 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} - '@iarna/toml@2.2.5': {} - '@ioredis/commands@1.2.0': {} '@isaacs/cliui@8.0.2': @@ -10281,7 +10252,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api@1.7.0': {} + '@opentelemetry/api@1.8.0': {} '@opentelemetry/api@1.9.0': {} @@ -10505,25 +10476,32 @@ snapshots: '@polka/url@1.0.0-next.25': {} - '@prisma/client@5.17.0(prisma@5.16.1)': + '@prisma/client@5.14.0(prisma@5.16.1)': optionalDependencies: prisma: 5.16.1 - '@prisma/client@5.7.0(prisma@5.16.1)': + '@prisma/client@5.17.0(prisma@5.16.1)': optionalDependencies: prisma: 5.16.1 + '@prisma/debug@5.14.0': {} + '@prisma/debug@5.16.1': {} '@prisma/debug@5.17.0': {} - '@prisma/debug@5.7.0': {} + '@prisma/engines-version@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48': {} '@prisma/engines-version@5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303': {} '@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': {} - '@prisma/engines-version@5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9': {} + '@prisma/engines@5.14.0': + dependencies: + '@prisma/debug': 5.14.0 + '@prisma/engines-version': 5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48 + '@prisma/fetch-engine': 5.14.0 + '@prisma/get-platform': 5.14.0 '@prisma/engines@5.16.1': dependencies: @@ -10539,12 +10517,11 @@ snapshots: '@prisma/fetch-engine': 5.17.0 '@prisma/get-platform': 5.17.0 - '@prisma/engines@5.7.0': + '@prisma/fetch-engine@5.14.0': dependencies: - '@prisma/debug': 5.7.0 - '@prisma/engines-version': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 - '@prisma/fetch-engine': 5.7.0 - '@prisma/get-platform': 5.7.0 + '@prisma/debug': 5.14.0 + '@prisma/engines-version': 5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48 + '@prisma/get-platform': 5.14.0 '@prisma/fetch-engine@5.16.1': dependencies: @@ -10558,19 +10535,17 @@ snapshots: '@prisma/engines-version': 5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053 '@prisma/get-platform': 5.17.0 - '@prisma/fetch-engine@5.7.0': + '@prisma/generator-helper@5.14.0': dependencies: - '@prisma/debug': 5.7.0 - '@prisma/engines-version': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 - '@prisma/get-platform': 5.7.0 + '@prisma/debug': 5.14.0 '@prisma/generator-helper@5.17.0': dependencies: '@prisma/debug': 5.17.0 - '@prisma/generator-helper@5.7.0': + '@prisma/get-platform@5.14.0': dependencies: - '@prisma/debug': 5.7.0 + '@prisma/debug': 5.14.0 '@prisma/get-platform@5.16.1': dependencies: @@ -10580,9 +10555,17 @@ snapshots: dependencies: '@prisma/debug': 5.17.0 - '@prisma/get-platform@5.7.0': + '@prisma/internals@5.14.0': dependencies: - '@prisma/debug': 5.7.0 + '@prisma/debug': 5.14.0 + '@prisma/engines': 5.14.0 + '@prisma/fetch-engine': 5.14.0 + '@prisma/generator-helper': 5.14.0 + '@prisma/get-platform': 5.14.0 + '@prisma/prisma-schema-wasm': 5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48 + '@prisma/schema-files-loader': 5.14.0 + arg: 5.0.2 + prompts: 2.4.2 '@prisma/internals@5.17.0': dependencies: @@ -10596,20 +10579,16 @@ snapshots: arg: 5.0.2 prompts: 2.4.2 - '@prisma/internals@5.7.0': - dependencies: - '@prisma/debug': 5.7.0 - '@prisma/engines': 5.7.0 - '@prisma/fetch-engine': 5.7.0 - '@prisma/generator-helper': 5.7.0 - '@prisma/get-platform': 5.7.0 - '@prisma/prisma-schema-wasm': 5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9 - arg: 5.0.2 - prompts: 2.4.2 + '@prisma/prisma-schema-wasm@5.14.0-17.56ca112d5a19c9925b53af75c3c6b7ada97f9f85': {} + + '@prisma/prisma-schema-wasm@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48': {} '@prisma/prisma-schema-wasm@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': {} - '@prisma/prisma-schema-wasm@5.7.0-41.79fb5193cf0a8fdbef536e4b4a159cad677ab1b9': {} + '@prisma/schema-files-loader@5.14.0': + dependencies: + '@prisma/prisma-schema-wasm': 5.14.0-17.56ca112d5a19c9925b53af75c3c6b7ada97f9f85 + fs-extra: 11.1.1 '@prisma/schema-files-loader@5.17.0': dependencies: @@ -10671,61 +10650,63 @@ snapshots: '@readme/openapi-schemas@3.1.0': {} - '@redwoodjs/api@6.6.4(prisma@5.16.1)': + '@redwoodjs/api@7.7.3(prisma@5.16.1)': dependencies: - '@babel/runtime-corejs3': 7.23.6 - '@prisma/client': 5.7.0(prisma@5.16.1) - '@whatwg-node/fetch': 0.9.14 - core-js: 3.34.0 + '@babel/runtime-corejs3': 7.24.5 + '@prisma/client': 5.14.0(prisma@5.16.1) + '@whatwg-node/fetch': 0.9.17 + core-js: 3.37.1 humanize-string: 2.1.0 jsonwebtoken: 9.0.2 pascalcase: 1.0.0 - pino: 8.16.2 + pino: 8.21.0 title-case: 3.0.3 transitivePeerDependencies: - prisma - '@redwoodjs/cli-helpers@6.6.4(enquirer@2.4.1)': + '@redwoodjs/cli-helpers@7.7.3(enquirer@2.4.1)': dependencies: '@babel/core': 7.24.7 - '@babel/runtime-corejs3': 7.23.6 - '@iarna/toml': 2.2.5 - '@opentelemetry/api': 1.7.0 - '@redwoodjs/project-config': 6.6.4 - '@redwoodjs/telemetry': 6.6.4 + '@opentelemetry/api': 1.8.0 + '@redwoodjs/project-config': 7.7.3 + '@redwoodjs/telemetry': 7.7.3 chalk: 4.1.2 - core-js: 3.34.0 - dotenv: 16.3.1 + dotenv: 16.4.5 execa: 5.1.1 listr2: 6.6.1(enquirer@2.4.1) lodash: 4.17.21 pascalcase: 1.0.0 prettier: 2.8.8 prompts: 2.4.2 + smol-toml: 1.2.1 terminal-link: 2.1.1 transitivePeerDependencies: - enquirer - supports-color - '@redwoodjs/graphql-server@6.6.4(@escape.tech/graphql-armor-types@0.5.0)(prisma@5.16.1)': + '@redwoodjs/context@7.7.3': {} + + '@redwoodjs/graphql-server@7.7.3(@escape.tech/graphql-armor-types@0.5.0)(prisma@5.16.1)': dependencies: - '@babel/runtime-corejs3': 7.23.6 - '@envelop/core': 4.0.3 - '@envelop/depth-limit': 3.0.3(@envelop/core@4.0.3)(graphql@16.8.1) - '@envelop/disable-introspection': 5.0.3(@envelop/core@4.0.3)(graphql@16.8.1) - '@envelop/filter-operation-type': 5.0.3(@envelop/core@4.0.3)(graphql@16.8.1) - '@envelop/on-resolve': 3.0.3(@envelop/core@4.0.3)(graphql@16.8.1) - '@escape.tech/graphql-armor': 2.3.1(@envelop/core@4.0.3)(@escape.tech/graphql-armor-types@0.5.0) - '@graphql-tools/merge': 9.0.1(graphql@16.8.1) - '@graphql-tools/schema': 10.0.2(graphql@16.8.1) - '@graphql-tools/utils': 10.0.11(graphql@16.8.1) - '@opentelemetry/api': 1.7.0 - '@redwoodjs/api': 6.6.4(prisma@5.16.1) - core-js: 3.34.0 + '@babel/runtime-corejs3': 7.24.5 + '@envelop/core': 5.0.1 + '@envelop/depth-limit': 4.0.0(@envelop/core@5.0.1)(graphql@16.8.1) + '@envelop/disable-introspection': 6.0.0(@envelop/core@5.0.1)(graphql@16.8.1) + '@envelop/filter-operation-type': 6.0.0(@envelop/core@5.0.1)(graphql@16.8.1) + '@envelop/on-resolve': 4.1.0(@envelop/core@5.0.1)(graphql@16.8.1) + '@escape.tech/graphql-armor': 2.3.1(@envelop/core@5.0.1)(@escape.tech/graphql-armor-types@0.5.0) + '@graphql-tools/merge': 9.0.4(graphql@16.8.1) + '@graphql-tools/schema': 10.0.3(graphql@16.8.1) + '@graphql-tools/utils': 10.2.0(graphql@16.8.1) + '@graphql-yoga/plugin-persisted-operations': 3.3.1(@graphql-tools/utils@10.2.0(graphql@16.8.1))(graphql-yoga@5.3.1(graphql@16.8.1))(graphql@16.8.1) + '@opentelemetry/api': 1.8.0 + '@redwoodjs/api': 7.7.3(prisma@5.16.1) + '@redwoodjs/context': 7.7.3 + core-js: 3.37.1 graphql: 16.8.1 - graphql-scalars: 1.22.4(graphql@16.8.1) + graphql-scalars: 1.23.0(graphql@16.8.1) graphql-tag: 2.12.6(graphql@16.8.1) - graphql-yoga: 4.0.4(graphql@16.8.1) + graphql-yoga: 5.3.1(graphql@16.8.1) lodash: 4.17.21 uuid: 9.0.1 transitivePeerDependencies: @@ -10735,49 +10716,48 @@ snapshots: - prisma - redis - '@redwoodjs/project-config@6.6.4': + '@redwoodjs/project-config@7.7.3': dependencies: - '@iarna/toml': 2.2.5 deepmerge: 4.3.1 fast-glob: 3.3.2 + smol-toml: 1.2.1 string-env-interpolation: 1.0.1 - '@redwoodjs/structure@6.6.4': + '@redwoodjs/structure@7.7.3': dependencies: - '@babel/runtime-corejs3': 7.23.6 - '@iarna/toml': 2.2.5 - '@prisma/internals': 5.7.0 - '@redwoodjs/project-config': 6.6.4 - '@types/line-column': 1.0.0 + '@babel/runtime-corejs3': 7.24.5 + '@prisma/internals': 5.14.0 + '@redwoodjs/project-config': 7.7.3 + '@types/line-column': 1.0.2 camelcase: 6.3.0 - core-js: 3.34.0 + core-js: 3.37.1 deepmerge: 4.3.1 dotenv-defaults: 5.0.2 enquirer: 2.4.1 fast-glob: 3.3.2 + fs-extra: 11.2.0 graphql: 16.8.1 lazy-get-decorator: 2.2.1 line-column: 1.0.2 lodash: 4.17.21 lodash-decorators: 6.0.1(lodash@4.17.21) - lru-cache: 7.18.3 + lru-cache: 10.2.2 proxyquire: 2.1.3 + smol-toml: 1.2.1 ts-morph: 15.1.0 vscode-languageserver: 6.1.1 - vscode-languageserver-textdocument: 1.0.8 - vscode-languageserver-types: 3.17.3 + vscode-languageserver-textdocument: 1.0.11 + vscode-languageserver-types: 3.17.5 yargs-parser: 21.1.1 - '@redwoodjs/telemetry@6.6.4': + '@redwoodjs/telemetry@7.7.3': dependencies: - '@babel/runtime-corejs3': 7.23.6 - '@redwoodjs/project-config': 6.6.4 - '@redwoodjs/structure': 6.6.4 - '@whatwg-node/fetch': 0.9.14 + '@redwoodjs/project-config': 7.7.3 + '@redwoodjs/structure': 7.7.3 + '@whatwg-node/fetch': 0.9.17 ci-info: 4.0.0 - core-js: 3.34.0 - envinfo: 7.11.0 - systeminformation: 5.21.20 + envinfo: 7.13.0 + systeminformation: 5.22.11 uuid: 9.0.1 yargs: 17.7.2 @@ -11182,7 +11162,7 @@ snapshots: dependencies: '@types/node': 20.14.9 - '@types/line-column@1.0.0': {} + '@types/line-column@1.0.2': {} '@types/methods@1.1.4': {} @@ -11634,10 +11614,10 @@ snapshots: '@whatwg-node/events@0.1.1': {} - '@whatwg-node/fetch@0.9.14': + '@whatwg-node/fetch@0.9.17': dependencies: '@whatwg-node/node-fetch': 0.5.11 - urlpattern-polyfill: 9.0.0 + urlpattern-polyfill: 10.0.0 '@whatwg-node/fetch@0.9.18': dependencies: @@ -12428,7 +12408,7 @@ snapshots: core-js-pure@3.37.1: {} - core-js@3.34.0: {} + core-js@3.37.1: {} core-util-is@1.0.3: {} @@ -12758,8 +12738,6 @@ snapshots: dotenv@14.3.2: {} - dotenv@16.3.1: {} - dotenv@16.4.5: {} dset@3.1.3: {} @@ -12814,7 +12792,7 @@ snapshots: entities@4.5.0: {} - envinfo@7.11.0: {} + envinfo@7.13.0: {} err-code@2.0.3: {} @@ -13583,7 +13561,7 @@ snapshots: arrify: 1.0.1 graphql: 16.8.1 - graphql-scalars@1.22.4(graphql@16.8.1): + graphql-scalars@1.23.0(graphql@16.8.1): dependencies: graphql: 16.8.1 tslib: 2.6.3 @@ -13593,14 +13571,14 @@ snapshots: graphql: 16.8.1 tslib: 2.6.3 - graphql-yoga@4.0.4(graphql@16.8.1): + graphql-yoga@5.3.1(graphql@16.8.1): dependencies: - '@envelop/core': 4.0.3 + '@envelop/core': 5.0.1 '@graphql-tools/executor': 1.2.7(graphql@16.8.1) - '@graphql-tools/schema': 10.0.2(graphql@16.8.1) - '@graphql-tools/utils': 10.0.11(graphql@16.8.1) - '@graphql-yoga/logger': 1.0.0 - '@graphql-yoga/subscription': 4.0.0 + '@graphql-tools/schema': 10.0.4(graphql@16.8.1) + '@graphql-tools/utils': 10.2.2(graphql@16.8.1) + '@graphql-yoga/logger': 2.0.0 + '@graphql-yoga/subscription': 5.0.1 '@whatwg-node/fetch': 0.9.18 '@whatwg-node/server': 0.9.36 dset: 3.1.3 @@ -14745,6 +14723,8 @@ snapshots: fault: 1.0.4 highlight.js: 10.4.1 + lru-cache@10.2.2: {} + lru-cache@10.3.0: {} lru-cache@4.1.5: @@ -14760,8 +14740,6 @@ snapshots: dependencies: yallist: 4.0.0 - lru-cache@7.18.3: {} - lz-string@1.5.0: {} magic-string-ast@0.6.2: @@ -15584,11 +15562,6 @@ snapshots: pify@4.0.1: {} - pino-abstract-transport@1.1.0: - dependencies: - readable-stream: 4.5.2 - split2: 4.2.0 - pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 @@ -15598,14 +15571,14 @@ snapshots: pino-std-serializers@7.0.0: {} - pino@8.16.2: + pino@8.21.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 1.1.0 + pino-abstract-transport: 1.2.0 pino-std-serializers: 6.2.2 - process-warning: 2.3.2 + process-warning: 3.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.4.3 @@ -15906,8 +15879,6 @@ snapshots: process-nextick-args@2.0.1: {} - process-warning@2.3.2: {} - process-warning@3.0.0: {} process@0.11.10: {} @@ -16413,6 +16384,8 @@ snapshots: smob@1.5.0: {} + smol-toml@1.2.1: {} + snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -16685,7 +16658,7 @@ snapshots: system-architecture@0.1.0: {} - systeminformation@5.21.20: {} + systeminformation@5.22.11: {} table-layout@1.0.2: dependencies: @@ -17160,8 +17133,6 @@ snapshots: urlpattern-polyfill@8.0.2: {} - urlpattern-polyfill@9.0.0: {} - use-sync-external-store@1.2.2(react@18.2.0): dependencies: react: 18.2.0 @@ -17388,8 +17359,6 @@ snapshots: vscode-languageserver-textdocument@1.0.11: {} - vscode-languageserver-textdocument@1.0.8: {} - vscode-languageserver-types@3.16.0: {} vscode-languageserver-types@3.17.2: {} diff --git a/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts b/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts index 73b25c1ca..4d71f26dc 100644 --- a/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts +++ b/tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts @@ -1196,6 +1196,50 @@ describe('Polymorphism Test', () => { ); }); + it('handles very long concrete model name', async () => { + const { db, user } = await setup(); + + await db.veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongModelNameA.create({ + data: { + owner: { connect: { id: user.id } }, + duration: 62, + url: 'https://whatever.com/example.mp4', + propA: 'propA', + }, + }); + + await db.veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongModelNameB.create({ + data: { + owner: { connect: { id: user.id } }, + duration: 62, + url: 'https://whatever.com/example.mp4', + propB: 'propB', + }, + }); + + const foundUser = await db.user.findFirst({ + where: { id: user.id }, + include: { + assets: true, + }, + }); + + expect(foundUser).toEqual( + expect.objectContaining({ + assets: expect.arrayContaining([ + expect.objectContaining({ + videoType: 'VeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongModelNameA', + propA: 'propA', + }), + expect.objectContaining({ + videoType: 'VeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongModelNameB', + propB: 'propB', + }), + ]), + }) + ); + }); + it('typescript compilation plain prisma', async () => { const src = ` import { PrismaClient } from '@prisma/client'; diff --git a/tests/integration/tests/enhancements/with-delegate/plugin-interaction.test.ts b/tests/integration/tests/enhancements/with-delegate/plugin-interaction.test.ts index a44e69aac..b0fb0d343 100644 --- a/tests/integration/tests/enhancements/with-delegate/plugin-interaction.test.ts +++ b/tests/integration/tests/enhancements/with-delegate/plugin-interaction.test.ts @@ -55,4 +55,94 @@ describe('Polymorphic Plugin Interaction Test', () => { extraDependencies: ['@trpc/client', '@trpc/server', '@trpc/react-query'], }); }); + + it('zod', async () => { + const { zodSchemas } = await loadSchema(POLYMORPHIC_SCHEMA, { fullZod: true }); + + // model schema + expect( + zodSchemas.models.AssetSchema.parse({ + id: 1, + assetType: 'video', + createdAt: new Date(), + viewCount: 100, + }) + ).toBeTruthy(); + + expect( + zodSchemas.models.AssetSchema.parse({ + id: 1, + assetType: 'video', + createdAt: new Date(), + viewCount: 100, + videoType: 'ratedVideo', // should be stripped + }).videoType + ).toBeUndefined(); + + expect( + zodSchemas.models.VideoSchema.parse({ + id: 1, + assetType: 'video', + videoType: 'ratedVideo', + duration: 100, + url: 'http://example.com', + createdAt: new Date(), + viewCount: 100, + }) + ).toBeTruthy(); + + expect(() => + zodSchemas.models.VideoSchema.parse({ + id: 1, + assetType: 'video', + videoType: 'ratedVideo', + url: 'http://example.com', + createdAt: new Date(), + viewCount: 100, + }) + ).toThrow('duration'); + + // create schema + expect( + zodSchemas.models.VideoCreateSchema.parse({ + duration: 100, + url: 'http://example.com', + }).assetType // discriminator should not be set + ).toBeUndefined(); + + // update schema + expect( + zodSchemas.models.VideoUpdateSchema.parse({ + duration: 100, + url: 'http://example.com', + }).assetType // discriminator should not be set + ).toBeUndefined(); + + // prisma create schema + expect( + zodSchemas.models.VideoPrismaCreateSchema.strip().parse({ + assetType: 'video', + }).assetType // discriminator should not be set + ).toBeUndefined(); + + // input object schema + expect( + zodSchemas.objects.RatedVideoCreateInputObjectSchema.parse({ + duration: 100, + viewCount: 200, + url: 'http://www.example.com', + rating: 5, + }) + ).toBeTruthy(); + + expect(() => + zodSchemas.objects.RatedVideoCreateInputObjectSchema.parse({ + duration: 100, + viewCount: 200, + url: 'http://www.example.com', + rating: 5, + videoType: 'ratedVideo', + }) + ).toThrow('videoType'); + }); }); diff --git a/tests/integration/tests/enhancements/with-delegate/utils.ts b/tests/integration/tests/enhancements/with-delegate/utils.ts index c04106ea2..66f29b221 100644 --- a/tests/integration/tests/enhancements/with-delegate/utils.ts +++ b/tests/integration/tests/enhancements/with-delegate/utils.ts @@ -44,6 +44,14 @@ model Gallery { id Int @id @default(autoincrement()) images Image[] } + +model VeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongModelNameA extends Video { + propA String +} + +model VeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongModelNameB extends Video { + propB String +} `; export const POLYMORPHIC_MANY_TO_MANY_SCHEMA = ` diff --git a/tests/integration/tests/enhancements/with-policy/field-validation.test.ts b/tests/integration/tests/enhancements/with-policy/field-validation.test.ts index 144bf0be6..d508381b4 100644 --- a/tests/integration/tests/enhancements/with-policy/field-validation.test.ts +++ b/tests/integration/tests/enhancements/with-policy/field-validation.test.ts @@ -646,8 +646,9 @@ describe('Model-level validation', () => { const db = enhance(); - await expect(db.model.create({ data: { x: 2, y: 1 } })).toResolveTruthy(); - await expect(db.model.create({ data: { x: 1, y: 2 } })).toBeRejectedByPolicy(); + await expect(db.model.create({ data: { id: 1, x: 2, y: 1 } })).toResolveTruthy(); + await expect(db.model.update({ where: { id: 1 }, data: { y: 3 } })).toBeRejectedByPolicy(); + await expect(db.model.update({ where: { id: 1 }, data: { x: 3 } })).toResolveTruthy(); }); it('int optionality', async () => { @@ -948,7 +949,7 @@ describe('Model-level validation', () => { await expect(db.model.create({ data: { id: 1, x: 0, y: 0 } })).toBeRejectedByPolicy(); await expect(db.model.create({ data: { id: 1, x: 1, y: 0 } })).toResolveTruthy(); - await expect(db.model.update({ where: { id: 1 }, data: {} })).toBeRejectedByPolicy(); + await expect(db.model.update({ where: { id: 1 }, data: {} })).toResolveTruthy(); await expect(db.model.update({ where: { id: 1 }, data: { y: 2 } })).toBeRejectedByPolicy(); await expect(db.model.update({ where: { id: 1 }, data: { y: 1 } })).toResolveTruthy(); await expect(db.model.update({ where: { id: 1 }, data: { x: 2, y: 1 } })).toResolveTruthy(); diff --git a/tests/regression/tests/issue-1466.test.ts b/tests/regression/tests/issue-1466.test.ts index 3ad17143f..f2526c85b 100644 --- a/tests/regression/tests/issue-1466.test.ts +++ b/tests/regression/tests/issue-1466.test.ts @@ -8,24 +8,24 @@ describe('issue 1466', () => { try { const r = await loadSchema( ` - model UserLongLongLongLongName { + model UserLongLongLongLongLongLongLongLongName { id Int @id @default(autoincrement()) level Int @default(0) - asset AssetLongLongLongLongName @relation(fields: [assetId], references: [id]) + asset AssetLongLongLongLongLongLongLongLongName @relation(fields: [assetId], references: [id]) assetId Int @unique } - model AssetLongLongLongLongName { + model AssetLongLongLongLongLongLongLongLongName { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) viewCount Int @default(0) - owner UserLongLongLongLongName? + owner UserLongLongLongLongLongLongLongLongName? assetType String @@delegate(assetType) } - model VideoLongLongLongLongName extends AssetLongLongLongLongName { + model VideoLongLongLongLongLongLongLongLongName extends AssetLongLongLongLongLongLongLongLongName { duration Int } `, @@ -39,22 +39,22 @@ describe('issue 1466', () => { prisma = r.prisma; const db = r.enhance(); - const video = await db.VideoLongLongLongLongName.create({ + const video = await db.VideoLongLongLongLongLongLongLongLongName.create({ data: { duration: 100 }, }); - const user = await db.UserLongLongLongLongName.create({ + const user = await db.UserLongLongLongLongLongLongLongLongName.create({ data: { asset: { connect: { id: video.id } }, }, }); - const userWithAsset = await db.UserLongLongLongLongName.findFirst({ + const userWithAsset = await db.UserLongLongLongLongLongLongLongLongName.findFirst({ include: { asset: true }, }); expect(userWithAsset).toMatchObject({ - asset: { assetType: 'VideoLongLongLongLongName', duration: 100 }, + asset: { assetType: 'VideoLongLongLongLongLongLongLongLongName', duration: 100 }, }); } finally { if (prisma) { diff --git a/tests/regression/tests/issue-1551.test.ts b/tests/regression/tests/issue-1551.test.ts new file mode 100644 index 000000000..3a330de24 --- /dev/null +++ b/tests/regression/tests/issue-1551.test.ts @@ -0,0 +1,26 @@ +import { loadSchema } from '@zenstackhq/testtools'; +describe('issue 1551', () => { + it('regression', async () => { + await loadSchema( + ` + model User { + id Int @id + profile Profile? @relation(fields: [profileId], references: [id]) + profileId Int? @unique @map('profile_id') + } + + model Profile { + id Int @id + contentType String + user User? + + @@delegate(contentType) + } + + model IndividualProfile extends Profile { + name String + } + ` + ); + }); +}); diff --git a/tests/regression/tests/issue-1562.test.ts b/tests/regression/tests/issue-1562.test.ts new file mode 100644 index 000000000..c07fee0d6 --- /dev/null +++ b/tests/regression/tests/issue-1562.test.ts @@ -0,0 +1,45 @@ +import { loadSchema } from '@zenstackhq/testtools'; +describe('issue 1562', () => { + it('regression', async () => { + const { enhance } = await loadSchema( + ` +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} + +plugin zod { + provider = '@core/zod' +} + +plugin enhancer { + provider = '@core/enhancer' + generatePermissionChecker = true +} + +abstract model Base { + id String @id @default(uuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt() + + // require login + @@allow('all', true) +} + +model User extends Base { + name String @unique @regex('^[a-zA-Z0-9_]{3,30}$') + + @@allow('read', true) +} + `, + { addPrelude: false } + ); + + const db = enhance(); + await expect(db.user.create({ data: { name: '1 2 3 4' } })).toBeRejectedByPolicy(); + }); +}); diff --git a/tests/regression/tests/issue-1563.test.ts b/tests/regression/tests/issue-1563.test.ts new file mode 100644 index 000000000..179bc356f --- /dev/null +++ b/tests/regression/tests/issue-1563.test.ts @@ -0,0 +1,30 @@ +import { loadSchema } from '@zenstackhq/testtools'; + +describe('issue 1563', () => { + it('regression', async () => { + const { enhance } = await loadSchema( + ` + model ModelA { + id String @id @default(cuid()) + ref ModelB[] + } + + model ModelB { + id String @id @default(cuid()) + ref ModelA? @relation(fields: [refId], references: [id]) + refId String? + + @@validate(refId != null, "refId must be set") + } + `, + { enhancements: ['validation'] } + ); + + const db = enhance(); + + const a = await db.modelA.create({ data: {} }); + const b = await db.modelB.create({ data: { refId: a.id } }); + + await expect(db.modelB.update({ where: { id: b.id }, data: { refId: a.id } })).toResolveTruthy(); + }); +}); diff --git a/tests/regression/tests/issue-1596.test.ts b/tests/regression/tests/issue-1596.test.ts new file mode 100644 index 000000000..62eedfd5f --- /dev/null +++ b/tests/regression/tests/issue-1596.test.ts @@ -0,0 +1,34 @@ +import { isPrismaClientValidationError } from '@zenstackhq/runtime'; +import { loadSchema } from '@zenstackhq/testtools'; + +describe('issue 1596', () => { + it('regression', async () => { + const { enhance } = await loadSchema( + ` + model User { + id Int @id + posts Post[] + } + + model Post { + id Int @id + title String + author User @relation(fields: [authorId], references: [id]) + authorId Int @default(auth().id) + } + ` + ); + + const db = enhance(); + + try { + await db.post.create({ data: { title: 'Post1' } }); + } catch (e) { + // eslint-disable-next-line jest/no-conditional-expect + expect(isPrismaClientValidationError(e)).toBe(true); + return; + } + + throw new Error('Expected error'); + }); +}); diff --git a/tests/regression/tests/issue-1610.test.ts b/tests/regression/tests/issue-1610.test.ts new file mode 100644 index 000000000..a116dbb5d --- /dev/null +++ b/tests/regression/tests/issue-1610.test.ts @@ -0,0 +1,56 @@ +import { loadSchema } from '@zenstackhq/testtools'; +describe('issue 1610', () => { + it('regular prisma client', async () => { + await loadSchema( + ` + model User { + id Int @id + posts Post[] + } + + model Post { + id Int @id + author User @relation(fields: [authorId], references: [id]) + authorId Int + } + `, + { fullZod: true, output: './lib/zen' } + ); + }); + + it('logical prisma client', async () => { + await loadSchema( + ` + model User { + id Int @id + posts Post[] + } + + model Post { + id Int @id + author User @relation(fields: [authorId], references: [id]) + authorId Int @default(auth().id) + } + `, + { fullZod: true, output: './lib/zen' } + ); + }); + + it('no custom output', async () => { + await loadSchema( + ` + model User { + id Int @id + posts Post[] + } + + model Post { + id Int @id + author User @relation(fields: [authorId], references: [id]) + authorId Int @default(auth().id) + } + `, + { fullZod: true, preserveTsFiles: true } + ); + }); +}); diff --git a/tests/regression/tests/issue-1627.test.ts b/tests/regression/tests/issue-1627.test.ts new file mode 100644 index 000000000..cc3c9fe00 --- /dev/null +++ b/tests/regression/tests/issue-1627.test.ts @@ -0,0 +1,50 @@ +import { loadSchema } from '@zenstackhq/testtools'; +describe('issue 1627', () => { + it('regression', async () => { + const { prisma, enhance } = await loadSchema( + ` +model User { + id String @id + memberships GymUser[] +} + +model Gym { + id String @id + members GymUser[] + + @@allow('all', true) +} + +model GymUser { + id String @id + userID String + user User @relation(fields: [userID], references: [id]) + gymID String? + gym Gym? @relation(fields: [gymID], references: [id]) + role String + + @@allow('read',gym.members?[user == auth() && (role == "ADMIN" || role == "TRAINER")]) + @@unique([userID, gymID]) +} + ` + ); + + await prisma.user.create({ data: { id: '1' } }); + + await prisma.gym.create({ + data: { + id: '1', + members: { + create: { + id: '1', + user: { connect: { id: '1' } }, + role: 'ADMIN', + }, + }, + }, + }); + + const db = enhance(); + await expect(db.gymUser.findMany()).resolves.toHaveLength(0); + }); +});