diff --git a/packages/orm/package.json b/packages/orm/package.json index 63d70a3bc..27a48a34e 100644 --- a/packages/orm/package.json +++ b/packages/orm/package.json @@ -18,7 +18,8 @@ "build": "tsc --noEmit && tsdown", "watch": "tsdown --watch", "lint": "eslint src --ext ts", - "pack": "pnpm pack" + "pack": "pnpm pack", + "test:generate": "tsx ../../scripts/test-generate.ts . --generate-models" }, "keywords": [], "files": [ diff --git a/packages/orm/test/schema/models.ts b/packages/orm/test/schema/models.ts new file mode 100644 index 000000000..82b5e812c --- /dev/null +++ b/packages/orm/test/schema/models.ts @@ -0,0 +1,18 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { schema as $schema, type SchemaType as $Schema } from "./schema"; +import type { ModelResult as $ModelResult, TypeDefResult as $TypeDefResult } from "@zenstackhq/orm"; +export type User = $ModelResult<$Schema, "User">; +export type Post = $ModelResult<$Schema, "Post">; +export type Product = $ModelResult<$Schema, "Product">; +export type Asset = $ModelResult<$Schema, "Asset">; +export type Video = $ModelResult<$Schema, "Video">; +export type Image = $ModelResult<$Schema, "Image">; +export type Address = $TypeDefResult<$Schema, "Address">; +export const Status = $schema.enums.Status.values; +export type Status = (typeof Status)[keyof typeof Status]; diff --git a/packages/orm/test/schema/schema.ts b/packages/orm/test/schema/schema.ts new file mode 100644 index 000000000..e0dff2a49 --- /dev/null +++ b/packages/orm/test/schema/schema.ts @@ -0,0 +1,353 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { type SchemaDef, type AttributeApplication, type FieldDefault, ExpressionUtils } from "@zenstackhq/schema"; +export class SchemaType implements SchemaDef { + provider = { + type: "postgresql" + } as const; + models = { + User: { + name: "User", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + email: { + name: "email", + type: "String", + attributes: [{ name: "@email" }, { name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("The user's email address") }] }] as readonly AttributeApplication[] + }, + username: { + name: "username", + type: "String", + attributes: [{ name: "@length", args: [{ name: "min", value: ExpressionUtils.literal(3) }, { name: "max", value: ExpressionUtils.literal(50) }] }] as readonly AttributeApplication[] + }, + website: { + name: "website", + type: "String", + optional: true, + attributes: [{ name: "@url" }] as readonly AttributeApplication[] + }, + code: { + name: "code", + type: "String", + attributes: [{ name: "@startsWith", args: [{ name: "text", value: ExpressionUtils.literal("USR") }] }] as readonly AttributeApplication[] + }, + age: { + name: "age", + type: "Int", + attributes: [{ name: "@gt", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }, { name: "@lte", args: [{ name: "value", value: ExpressionUtils.literal(150) }] }] as readonly AttributeApplication[] + }, + score: { + name: "score", + type: "Float", + attributes: [{ name: "@gte", args: [{ name: "value", value: ExpressionUtils.literal(0.0) }] }, { name: "@lt", args: [{ name: "value", value: ExpressionUtils.literal(100.0) }] }] as readonly AttributeApplication[] + }, + bigNum: { + name: "bigNum", + type: "BigInt", + attributes: [{ name: "@gte", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[] + }, + balance: { + name: "balance", + type: "Decimal", + attributes: [{ name: "@gt", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[] + }, + active: { + name: "active", + type: "Boolean" + }, + birthdate: { + name: "birthdate", + type: "DateTime", + optional: true + }, + avatar: { + name: "avatar", + type: "Bytes", + optional: true + }, + metadata: { + name: "metadata", + type: "Json", + optional: true + }, + status: { + name: "status", + type: "Status" + }, + address: { + name: "address", + type: "Address", + optional: true, + attributes: [{ name: "@json" }] as readonly AttributeApplication[] + }, + posts: { + name: "posts", + type: "Post", + array: true, + relation: { opposite: "author" } + } + }, + attributes: [ + { name: "@@validate", args: [{ name: "value", value: ExpressionUtils.binary(ExpressionUtils.field("age"), ">=", ExpressionUtils.literal(18)) }, { name: "message", value: ExpressionUtils.literal("Must be adult") }, { name: "path", value: ExpressionUtils.array("String", [ExpressionUtils.literal("age")]) }] }, + { name: "@@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("A user of the system") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Post: { + name: "Post", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + title: { + name: "title", + type: "String" + }, + published: { + name: "published", + type: "Boolean" + }, + tags: { + name: "tags", + type: "String", + array: true + }, + author: { + name: "author", + type: "User", + optional: true, + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], + relation: { opposite: "posts", fields: ["authorId"], references: ["id"] } + }, + authorId: { + name: "authorId", + type: "String", + optional: true, + foreignKeyFor: [ + "author" + ] as readonly string[] + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Product: { + name: "Product", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault + }, + name: { + name: "name", + type: "String" + }, + price: { + name: "price", + type: "Float" + }, + discount: { + name: "discount", + type: "Float", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[], + default: 0 as FieldDefault + }, + finalPrice: { + name: "finalPrice", + type: "Float", + attributes: [{ name: "@computed" }] as readonly AttributeApplication[], + computed: true + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + }, + computedFields: { + finalPrice(_context: { + modelAlias: string; + }): number { + throw new Error("This is a stub for computed field"); + } + } + }, + Asset: { + name: "Asset", + fields: { + id: { + name: "id", + type: "Int", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("autoincrement") as FieldDefault + }, + createdAt: { + name: "createdAt", + type: "DateTime", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + assetType: { + name: "assetType", + type: "String", + isDiscriminator: true + } + }, + attributes: [ + { name: "@@delegate", args: [{ name: "discriminator", value: ExpressionUtils.field("assetType") }] } + ] as readonly AttributeApplication[], + idFields: ["id"], + uniqueFields: { + id: { type: "Int" } + }, + isDelegate: true, + subModels: ["Video", "Image"] + }, + Video: { + name: "Video", + baseModel: "Asset", + fields: { + id: { + name: "id", + type: "Int", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("autoincrement") as FieldDefault + }, + createdAt: { + name: "createdAt", + type: "DateTime", + originModel: "Asset", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + assetType: { + name: "assetType", + type: "String", + originModel: "Asset", + isDiscriminator: true + }, + duration: { + name: "duration", + type: "Int" + }, + url: { + name: "url", + type: "String" + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "Int" } + } + }, + Image: { + name: "Image", + baseModel: "Asset", + fields: { + id: { + name: "id", + type: "Int", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("autoincrement") as FieldDefault + }, + createdAt: { + name: "createdAt", + type: "DateTime", + originModel: "Asset", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault + }, + assetType: { + name: "assetType", + type: "String", + originModel: "Asset", + isDiscriminator: true + }, + format: { + name: "format", + type: "String" + }, + width: { + name: "width", + type: "Int" + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "Int" } + } + } + } as const; + typeDefs = { + Address: { + name: "Address", + fields: { + residents: { + name: "residents", + type: "String", + array: true + }, + street: { + name: "street", + type: "String", + attributes: [{ name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("Street address line") }] }] as readonly AttributeApplication[] + }, + city: { + name: "city", + type: "String", + attributes: [{ name: "@length", args: [{ name: "min", value: ExpressionUtils.literal(2) }] }] as readonly AttributeApplication[] + }, + zip: { + name: "zip", + type: "String", + optional: true + } + }, + attributes: [ + { name: "@@validate", args: [{ name: "value", value: ExpressionUtils.binary(ExpressionUtils.binary(ExpressionUtils.field("zip"), "==", ExpressionUtils._null()), "||", ExpressionUtils.binary(ExpressionUtils.call("length", [ExpressionUtils.field("zip")]), "==", ExpressionUtils.literal(5))) }, { name: "message", value: ExpressionUtils.literal("Zip code must be exactly 5 characters") }, { name: "path", value: ExpressionUtils.array("String", [ExpressionUtils.literal("zip")]) }] }, + { name: "@@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("A mailing address") }] } + ] as readonly AttributeApplication[] + } + } as const; + enums = { + Status: { + name: "Status", + values: { + ACTIVE: "ACTIVE", + INACTIVE: "INACTIVE", + PENDING: "PENDING" + }, + attributes: [ + { name: "@@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("User account status") }] } + ] as readonly AttributeApplication[] + } + } as const; + authType = "User" as const; + plugins = {}; +} +export const schema = new SchemaType(); diff --git a/packages/orm/test/schema/schema.zmodel b/packages/orm/test/schema/schema.zmodel new file mode 100644 index 000000000..9e0d5716a --- /dev/null +++ b/packages/orm/test/schema/schema.zmodel @@ -0,0 +1,80 @@ +datasource db { + provider = 'postgresql' +} + +enum Status { + ACTIVE + INACTIVE + PENDING + + @@meta("description", "User account status") +} + +type Address { + residents String[] + street String @meta("description", "Street address line") + city String @length(2) + zip String? + + @@validate(zip == null || length(zip) == 5, "Zip code must be exactly 5 characters", ["zip"]) + @@meta("description", "A mailing address") +} + +model User { + id String @id @default(cuid()) + email String @email @meta("description", "The user's email address") + username String @length(3, 50) + website String? @url + code String @startsWith("USR") + age Int @gt(0) @lte(150) + score Float @gte(0.0) @lt(100.0) + bigNum BigInt @gte(0) + balance Decimal @gt(0) + active Boolean + birthdate DateTime? + avatar Bytes? + metadata Json? + status Status + address Address? @json + posts Post[] + + @@validate(age >= 18, "Must be adult", ["age"]) + @@meta("description", "A user of the system") +} + +model Post { + id String @id @default(cuid()) + title String + published Boolean + tags String[] + author User? @relation(fields: [authorId], references: [id]) + authorId String? +} + +// --- Computed fields --- +model Product { + id String @id @default(cuid()) + name String + price Float + discount Float @default(0) + finalPrice Float @computed +} + +// --- Delegate models --- +model Asset { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + assetType String + + @@delegate(assetType) +} + +model Video extends Asset { + duration Int + url String +} + +model Image extends Asset { + format String + width Int +} diff --git a/packages/orm/test/zod-compat.test.ts b/packages/orm/test/zod-compat.test.ts new file mode 100644 index 000000000..a7a1f083b --- /dev/null +++ b/packages/orm/test/zod-compat.test.ts @@ -0,0 +1,24 @@ +import { describe, expectTypeOf, it } from 'vitest'; +import { createSchemaFactory } from '@zenstackhq/zod'; +import type { User as ModelUser } from './schema/models'; +import { schema } from './schema/schema'; +import z from 'zod'; + +const factory = createSchemaFactory(schema); + +describe('Zod ↔ ORM type compatibility', () => { + it('infers zod type compatible with ORM model type (except optionality)', () => { + // ORM model results use `T | null` for optional fields; the Zod schema + // uses `T | null | undefined` to also accept missing fields in input + // objects. The useful property is that any ORM model value is valid + // input for the Zod schema. + const userSchema = factory.makeModelSchema('User'); + type ZodUser = z.infer; + expectTypeOf().toExtend(); + + // or with required + const _userSchemaRequired = userSchema.required(); + type ZodUserRequired = z.infer; + expectTypeOf().toMatchTypeOf(); + }); +}); diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index 4323894e8..0000fbe35 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -1,3 +1,3 @@ export { createSchemaFactory } from './factory'; -export type { ModelSchemaOptions, GetModelSchemaShapeWithOptions } from './types'; +export type { ModelSchemaOptions, GetModelSchemaShapeWithOptions, JsonValue } from './types'; export * as ZodUtils from './utils'; diff --git a/packages/zod/src/types.ts b/packages/zod/src/types.ts index 8dfc235a7..679eb8231 100644 --- a/packages/zod/src/types.ts +++ b/packages/zod/src/types.ts @@ -135,13 +135,11 @@ type MapFieldTypeToZod = FieldType extends ? z.ZodObject, z.core.$strict> : z.ZodUnknown; -type JsonZodType = - | z.ZodObject, z.core.$loose> - | z.ZodArray - | z.ZodString - | z.ZodNumber - | z.ZodBoolean - | z.ZodNull; +export type JsonValue = string | number | boolean | JsonObject | JsonArray; +type JsonObject = { [key: string]: JsonValue | null }; +type JsonArray = Array; + +type JsonZodType = z.ZodType; type EnumZodType> = z.ZodEnum<{ [Key in keyof GetEnum]: GetEnum[Key]; diff --git a/packages/zod/test/factory.test.ts b/packages/zod/test/factory.test.ts index cfbdaa4cc..0dd616c2d 100644 --- a/packages/zod/test/factory.test.ts +++ b/packages/zod/test/factory.test.ts @@ -3,6 +3,7 @@ import { describe, expect, expectTypeOf, it } from 'vitest'; import { createSchemaFactory } from '../src/index'; import { schema } from './schema/schema'; import z from 'zod'; +import type { JsonValue } from '../src/index'; const factory = createSchemaFactory(schema); @@ -69,9 +70,7 @@ describe('SchemaFactory - makeModelSchema', () => { // optional Json expectTypeOf().toHaveProperty('metadata'); - expectTypeOf().toEqualTypeOf< - string | number | boolean | null | Record | unknown[] | undefined - >(); + expectTypeOf().toEqualTypeOf(); // required enum expectTypeOf().toEqualTypeOf<'ACTIVE' | 'INACTIVE' | 'PENDING'>(); @@ -194,6 +193,7 @@ describe('SchemaFactory - makeModelSchema', () => { expect(userSchema.safeParse({ ...validUser, metadata: { key: 'value' } }).success).toBe(true); expect(userSchema.safeParse({ ...validUser, metadata: [1, 2, 3] }).success).toBe(true); expect(userSchema.safeParse({ ...validUser, metadata: 42 }).success).toBe(true); + expect(userSchema.safeParse({ ...validUser, metadata: null }).success).toBe(true); }); it('rejects invalid Json values', () => { diff --git a/packages/zod/test/schema/schema.ts b/packages/zod/test/schema/schema.ts index 8abbf29c0..e0dff2a49 100644 --- a/packages/zod/test/schema/schema.ts +++ b/packages/zod/test/schema/schema.ts @@ -5,7 +5,7 @@ /* eslint-disable */ -import { type SchemaDef, ExpressionUtils } from "@zenstackhq/schema"; +import { type SchemaDef, type AttributeApplication, type FieldDefault, ExpressionUtils } from "@zenstackhq/schema"; export class SchemaType implements SchemaDef { provider = { type: "postgresql" @@ -18,49 +18,49 @@ export class SchemaType implements SchemaDef { name: "id", type: "String", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }], - default: ExpressionUtils.call("cuid") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault }, email: { name: "email", type: "String", - attributes: [{ name: "@email" }, { name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("The user's email address") }] }] + attributes: [{ name: "@email" }, { name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("The user's email address") }] }] as readonly AttributeApplication[] }, username: { name: "username", type: "String", - attributes: [{ name: "@length", args: [{ name: "min", value: ExpressionUtils.literal(3) }, { name: "max", value: ExpressionUtils.literal(50) }] }] + attributes: [{ name: "@length", args: [{ name: "min", value: ExpressionUtils.literal(3) }, { name: "max", value: ExpressionUtils.literal(50) }] }] as readonly AttributeApplication[] }, website: { name: "website", type: "String", optional: true, - attributes: [{ name: "@url" }] + attributes: [{ name: "@url" }] as readonly AttributeApplication[] }, code: { name: "code", type: "String", - attributes: [{ name: "@startsWith", args: [{ name: "text", value: ExpressionUtils.literal("USR") }] }] + attributes: [{ name: "@startsWith", args: [{ name: "text", value: ExpressionUtils.literal("USR") }] }] as readonly AttributeApplication[] }, age: { name: "age", type: "Int", - attributes: [{ name: "@gt", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }, { name: "@lte", args: [{ name: "value", value: ExpressionUtils.literal(150) }] }] + attributes: [{ name: "@gt", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }, { name: "@lte", args: [{ name: "value", value: ExpressionUtils.literal(150) }] }] as readonly AttributeApplication[] }, score: { name: "score", type: "Float", - attributes: [{ name: "@gte", args: [{ name: "value", value: ExpressionUtils.literal(0.0) }] }, { name: "@lt", args: [{ name: "value", value: ExpressionUtils.literal(100.0) }] }] + attributes: [{ name: "@gte", args: [{ name: "value", value: ExpressionUtils.literal(0.0) }] }, { name: "@lt", args: [{ name: "value", value: ExpressionUtils.literal(100.0) }] }] as readonly AttributeApplication[] }, bigNum: { name: "bigNum", type: "BigInt", - attributes: [{ name: "@gte", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] + attributes: [{ name: "@gte", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[] }, balance: { name: "balance", type: "Decimal", - attributes: [{ name: "@gt", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] + attributes: [{ name: "@gt", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[] }, active: { name: "active", @@ -89,7 +89,7 @@ export class SchemaType implements SchemaDef { name: "address", type: "Address", optional: true, - attributes: [{ name: "@json" }] + attributes: [{ name: "@json" }] as readonly AttributeApplication[] }, posts: { name: "posts", @@ -101,7 +101,7 @@ export class SchemaType implements SchemaDef { attributes: [ { name: "@@validate", args: [{ name: "value", value: ExpressionUtils.binary(ExpressionUtils.field("age"), ">=", ExpressionUtils.literal(18)) }, { name: "message", value: ExpressionUtils.literal("Must be adult") }, { name: "path", value: ExpressionUtils.array("String", [ExpressionUtils.literal("age")]) }] }, { name: "@@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("A user of the system") }] } - ], + ] as readonly AttributeApplication[], idFields: ["id"], uniqueFields: { id: { type: "String" } @@ -114,8 +114,8 @@ export class SchemaType implements SchemaDef { name: "id", type: "String", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }], - default: ExpressionUtils.call("cuid") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault }, title: { name: "title", @@ -134,7 +134,7 @@ export class SchemaType implements SchemaDef { name: "author", type: "User", optional: true, - attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }], + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array("String", [ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array("String", [ExpressionUtils.field("id")]) }] }] as readonly AttributeApplication[], relation: { opposite: "posts", fields: ["authorId"], references: ["id"] } }, authorId: { @@ -143,7 +143,7 @@ export class SchemaType implements SchemaDef { optional: true, foreignKeyFor: [ "author" - ] + ] as readonly string[] } }, idFields: ["id"], @@ -158,8 +158,8 @@ export class SchemaType implements SchemaDef { name: "id", type: "String", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }], - default: ExpressionUtils.call("cuid") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("cuid") as FieldDefault }, name: { name: "name", @@ -172,13 +172,13 @@ export class SchemaType implements SchemaDef { discount: { name: "discount", type: "Float", - attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }], - default: 0 + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(0) }] }] as readonly AttributeApplication[], + default: 0 as FieldDefault }, finalPrice: { name: "finalPrice", type: "Float", - attributes: [{ name: "@computed" }], + attributes: [{ name: "@computed" }] as readonly AttributeApplication[], computed: true } }, @@ -201,14 +201,14 @@ export class SchemaType implements SchemaDef { name: "id", type: "Int", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }], - default: ExpressionUtils.call("autoincrement") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("autoincrement") as FieldDefault }, createdAt: { name: "createdAt", type: "DateTime", - attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }], - default: ExpressionUtils.call("now") + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault }, assetType: { name: "assetType", @@ -218,7 +218,7 @@ export class SchemaType implements SchemaDef { }, attributes: [ { name: "@@delegate", args: [{ name: "discriminator", value: ExpressionUtils.field("assetType") }] } - ], + ] as readonly AttributeApplication[], idFields: ["id"], uniqueFields: { id: { type: "Int" } @@ -234,15 +234,15 @@ export class SchemaType implements SchemaDef { name: "id", type: "Int", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }], - default: ExpressionUtils.call("autoincrement") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("autoincrement") as FieldDefault }, createdAt: { name: "createdAt", type: "DateTime", originModel: "Asset", - attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }], - default: ExpressionUtils.call("now") + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault }, assetType: { name: "assetType", @@ -272,15 +272,15 @@ export class SchemaType implements SchemaDef { name: "id", type: "Int", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }], - default: ExpressionUtils.call("autoincrement") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("autoincrement") as FieldDefault }, createdAt: { name: "createdAt", type: "DateTime", originModel: "Asset", - attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }], - default: ExpressionUtils.call("now") + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }] as readonly AttributeApplication[], + default: ExpressionUtils.call("now") as FieldDefault }, assetType: { name: "assetType", @@ -315,12 +315,12 @@ export class SchemaType implements SchemaDef { street: { name: "street", type: "String", - attributes: [{ name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("Street address line") }] }] + attributes: [{ name: "@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("Street address line") }] }] as readonly AttributeApplication[] }, city: { name: "city", type: "String", - attributes: [{ name: "@length", args: [{ name: "min", value: ExpressionUtils.literal(2) }] }] + attributes: [{ name: "@length", args: [{ name: "min", value: ExpressionUtils.literal(2) }] }] as readonly AttributeApplication[] }, zip: { name: "zip", @@ -331,7 +331,7 @@ export class SchemaType implements SchemaDef { attributes: [ { name: "@@validate", args: [{ name: "value", value: ExpressionUtils.binary(ExpressionUtils.binary(ExpressionUtils.field("zip"), "==", ExpressionUtils._null()), "||", ExpressionUtils.binary(ExpressionUtils.call("length", [ExpressionUtils.field("zip")]), "==", ExpressionUtils.literal(5))) }, { name: "message", value: ExpressionUtils.literal("Zip code must be exactly 5 characters") }, { name: "path", value: ExpressionUtils.array("String", [ExpressionUtils.literal("zip")]) }] }, { name: "@@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("A mailing address") }] } - ] + ] as readonly AttributeApplication[] } } as const; enums = { @@ -344,7 +344,7 @@ export class SchemaType implements SchemaDef { }, attributes: [ { name: "@@meta", args: [{ name: "name", value: ExpressionUtils.literal("description") }, { name: "value", value: ExpressionUtils.literal("User account status") }] } - ] + ] as readonly AttributeApplication[] } } as const; authType = "User" as const; diff --git a/scripts/test-generate.ts b/scripts/test-generate.ts index 7c3926eff..5a612ae07 100644 --- a/scripts/test-generate.ts +++ b/scripts/test-generate.ts @@ -22,7 +22,7 @@ async function generate(schemaPath: string, options: string[]) { const cliPath = path.join(_dirname, '../packages/cli/dist/index.mjs'); const RUNTIME = process.env.RUNTIME ?? 'node'; execSync( - `${RUNTIME} ${cliPath} generate --schema ${schemaPath} ${options.join(' ')} --generate-models=false --generate-input=false --no-version-check --no-tips`, + `${RUNTIME} ${cliPath} generate --schema ${schemaPath} --generate-models=false ${options.join(' ')} --generate-input=false --no-version-check --no-tips`, { cwd: path.dirname(schemaPath), },