Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-v3",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"description": "ZenStack",
"packageManager": "pnpm@10.12.1",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand Down
12 changes: 6 additions & 6 deletions packages/cli/src/plugins/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import path from 'node:path';
const plugin: CliPlugin = {
name: 'Prisma Schema Generator',
statusText: 'Generating Prisma schema',
async generate({ model, defaultOutputPath, pluginOptions }) {
let outDir = defaultOutputPath;
async generate({ model, schemaFile, defaultOutputPath, pluginOptions }) {
let outFile = path.join(defaultOutputPath, 'schema.prisma');
if (typeof pluginOptions['output'] === 'string') {
outDir = path.resolve(defaultOutputPath, pluginOptions['output']);
if (!fs.existsSync(outDir)) {
fs.mkdirSync(outDir, { recursive: true });
outFile = path.resolve(path.dirname(schemaFile), pluginOptions['output']);
if (!fs.existsSync(path.dirname(outFile))) {
fs.mkdirSync(path.dirname(outFile), { recursive: true });
}
}
const prismaSchema = await new PrismaSchemaGenerator(model).generate();
fs.writeFileSync(path.join(outDir, 'schema.prisma'), prismaSchema);
fs.writeFileSync(outFile, prismaSchema);
},
};

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/plugins/prisma-plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ model User {
const workDir = createProject(`
plugin prisma {
provider = '@core/prisma'
output = './prisma'
output = '../prisma/schema.prisma'
}

model User {
id String @id @default(cuid())
}
`);
runCli('generate', workDir);
expect(fs.existsSync(path.join(workDir, 'zenstack/prisma/schema.prisma'))).toBe(true);
expect(fs.existsSync(path.join(workDir, 'prisma/schema.prisma'))).toBe(true);
});
});
2 changes: 1 addition & 1 deletion packages/common-helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/common-helpers",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"description": "ZenStack Common Helpers",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/create-zenstack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-zenstack",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"description": "Create a new ZenStack project",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/dialects/sql.js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/kysely-sql-js",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"description": "Kysely dialect for sql.js",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/eslint-config",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"type": "module",
"private": true,
"license": "MIT"
Expand Down
2 changes: 1 addition & 1 deletion packages/ide/vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zenstack",
"publisher": "zenstack",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"displayName": "ZenStack Language Tools",
"description": "VSCode extension for ZenStack ZModel language",
"private": true,
Expand Down
2 changes: 1 addition & 1 deletion packages/language/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/language",
"description": "ZenStack ZModel language specification",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"license": "MIT",
"author": "ZenStack Team",
"files": [
Expand Down
5 changes: 3 additions & 2 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/runtime",
"version": "3.0.0-alpha.29",
"version": "3.0.0-alpha.30",
"description": "ZenStack Runtime",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -99,6 +99,7 @@
"@zenstackhq/testtools": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"tsx": "^4.19.2"
"tsx": "^4.19.2",
"zod": "~3.25.0"
}
}
43 changes: 23 additions & 20 deletions packages/runtime/src/client/crud-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type {
FieldIsDelegateDiscriminator,
FieldIsDelegateRelation,
FieldIsRelation,
FieldIsRelationArray,
FieldType,
ForeignKeyFields,
GetEnum,
Expand Down Expand Up @@ -218,11 +217,11 @@ export type WhereInput<
: Key]?: Key extends RelationFields<Schema, Model>
? // relation
RelationFilter<Schema, Model, Key>
: // enum
GetModelFieldType<Schema, Model, Key> extends GetEnums<Schema>
? EnumFilter<Schema, GetModelFieldType<Schema, Model, Key>, ModelFieldIsOptional<Schema, Model, Key>>
: FieldIsArray<Schema, Model, Key> extends true
? ArrayFilter<GetModelFieldType<Schema, Model, Key>>
: FieldIsArray<Schema, Model, Key> extends true
? ArrayFilter<Schema, GetModelFieldType<Schema, Model, Key>>
: // enum
GetModelFieldType<Schema, Model, Key> extends GetEnums<Schema>
? EnumFilter<Schema, GetModelFieldType<Schema, Model, Key>, ModelFieldIsOptional<Schema, Model, Key>>
: // primitive
PrimitiveFilter<
Schema,
Expand All @@ -247,14 +246,18 @@ type EnumFilter<Schema extends SchemaDef, T extends GetEnums<Schema>, Nullable e
not?: EnumFilter<Schema, T, Nullable>;
};

type ArrayFilter<T extends string> = {
equals?: MapBaseType<T>[];
has?: MapBaseType<T>;
hasEvery?: MapBaseType<T>[];
hasSome?: MapBaseType<T>[];
type ArrayFilter<Schema extends SchemaDef, T extends string> = {
equals?: MapScalarType<Schema, T>[] | null;
has?: MapScalarType<Schema, T> | null;
hasEvery?: MapScalarType<Schema, T>[];
hasSome?: MapScalarType<Schema, T>[];
isEmpty?: boolean;
};

// map a scalar type (primitive and enum) to TS type
type MapScalarType<Schema extends SchemaDef, T extends string> =
T extends GetEnums<Schema> ? keyof GetEnum<Schema, T> : MapBaseType<T>;

type PrimitiveFilter<
Schema extends SchemaDef,
T extends string,
Expand Down Expand Up @@ -428,7 +431,7 @@ export type WhereUniqueInput<Schema extends SchemaDef, Model extends GetModels<S
>;

export type OmitInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
[Key in NonRelationFields<Schema, Model>]?: true;
[Key in NonRelationFields<Schema, Model>]?: boolean;
};

export type SelectIncludeOmit<Schema extends SchemaDef, Model extends GetModels<Schema>, AllowCount extends boolean> = {
Expand All @@ -443,7 +446,7 @@ export type SelectInput<
AllowCount extends boolean = true,
AllowRelation extends boolean = true,
> = {
[Key in NonRelationFields<Schema, Model>]?: true;
[Key in NonRelationFields<Schema, Model>]?: boolean;
} & (AllowRelation extends true ? IncludeInput<Schema, Model> : {}) & // relation fields
// relation count
(AllowCount extends true
Expand All @@ -454,11 +457,11 @@ export type SelectInput<
: {});

type SelectCount<Schema extends SchemaDef, Model extends GetModels<Schema>> =
| true
| boolean
| {
select: {
[Key in RelationFields<Schema, Model> as FieldIsArray<Schema, Model, Key> extends true ? Key : never]:
| true
[Key in RelationFields<Schema, Model> as FieldIsArray<Schema, Model, Key> extends true ? Key : never]?:
| boolean
| {
where: WhereInput<Schema, RelationFieldType<Schema, Model, Key>, false>;
};
Expand Down Expand Up @@ -561,9 +564,9 @@ type OptionalFieldsForCreate<Schema extends SchemaDef, Model extends GetModels<S
? Key
: FieldHasDefault<Schema, Model, Key> extends true
? Key
: GetModelField<Schema, Model, Key>['updatedAt'] extends true
: FieldIsArray<Schema, Model, Key> extends true
? Key
: FieldIsRelationArray<Schema, Model, Key> extends true
: GetModelField<Schema, Model, Key>['updatedAt'] extends true
? Key
: never]: GetModelField<Schema, Model, Key>;
};
Expand Down Expand Up @@ -694,7 +697,7 @@ type ScalarCreatePayload<
| MapModelFieldType<Schema, Model, Field>
| (FieldIsArray<Schema, Model, Field> extends true
? {
set?: MapModelFieldType<Schema, Model, Field>[];
set?: MapModelFieldType<Schema, Model, Field>;
}
: never);

Expand Down Expand Up @@ -1204,7 +1207,7 @@ type NestedUpsertInput<
Field extends RelationFields<Schema, Model>,
> = OrArray<
{
where: WhereUniqueInput<Schema, Model>;
where: WhereUniqueInput<Schema, RelationFieldType<Schema, Model, Field>>;
create: CreateInput<
Schema,
RelationFieldType<Schema, Model, Field>,
Expand Down
50 changes: 26 additions & 24 deletions packages/runtime/src/client/crud/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,10 @@ export class InputValidator<Schema extends SchemaDef> {
} else {
return match(type)
.with('String', () => z.string())
.with('Int', () => z.int())
.with('Int', () => z.number().int())
.with('Float', () => z.number())
.with('Boolean', () => z.boolean())
.with('BigInt', () => z.union([z.int(), z.bigint()]))
.with('BigInt', () => z.union([z.number().int(), z.bigint()]))
.with('Decimal', () => z.union([z.number(), z.instanceof(Decimal), z.string()]))
.with('DateTime', () => z.union([z.date(), z.string().datetime()]))
.with('Bytes', () => z.instanceof(Uint8Array))
Expand All @@ -252,20 +252,22 @@ export class InputValidator<Schema extends SchemaDef> {
}
const typeDef = this.schema.typeDefs?.[type];
invariant(typeDef, `Type definition "${type}" not found in schema`);
schema = z.looseObject(
Object.fromEntries(
Object.entries(typeDef.fields).map(([field, def]) => {
let fieldSchema = this.makePrimitiveSchema(def.type);
if (def.array) {
fieldSchema = fieldSchema.array();
}
if (def.optional) {
fieldSchema = fieldSchema.optional();
}
return [field, fieldSchema];
}),
),
);
schema = z
.object(
Object.fromEntries(
Object.entries(typeDef.fields).map(([field, def]) => {
let fieldSchema = this.makePrimitiveSchema(def.type);
if (def.array) {
fieldSchema = fieldSchema.array();
}
if (def.optional) {
fieldSchema = fieldSchema.optional();
}
return [field, fieldSchema];
}),
),
)
.passthrough();
this.schemaCache.set(key, schema);
return schema;
}
Expand Down Expand Up @@ -469,7 +471,7 @@ export class InputValidator<Schema extends SchemaDef> {

private makeDateTimeFilterSchema(optional: boolean, withAggregations: boolean): ZodType {
return this.makeCommonPrimitiveFilterSchema(
z.union([z.iso.datetime(), z.date()]),
z.union([z.string().datetime(), z.date()]),
optional,
() => z.lazy(() => this.makeDateTimeFilterSchema(optional, withAggregations)),
withAggregations ? ['_count', '_min', '_max'] : undefined,
Expand Down Expand Up @@ -519,7 +521,7 @@ export class InputValidator<Schema extends SchemaDef> {
gte: baseSchema.optional(),
not: makeThis().optional(),
...(withAggregations?.includes('_count')
? { _count: this.makeNumberFilterSchema(z.int(), false, false).optional() }
? { _count: this.makeNumberFilterSchema(z.number().int(), false, false).optional() }
: {}),
...(withAggregations?.includes('_avg') ? { _avg: commonAggSchema() } : {}),
...(withAggregations?.includes('_sum') ? { _sum: commonAggSchema() } : {}),
Expand Down Expand Up @@ -1020,7 +1022,7 @@ export class InputValidator<Schema extends SchemaDef> {
return z.strictObject({
where: this.makeWhereSchema(model, false).optional(),
data: this.makeUpdateDataSchema(model, [], true),
limit: z.int().nonnegative().optional(),
limit: z.number().int().nonnegative().optional(),
});
}

Expand Down Expand Up @@ -1158,7 +1160,7 @@ export class InputValidator<Schema extends SchemaDef> {
return z
.object({
where: this.makeWhereSchema(model, false).optional(),
limit: z.int().nonnegative().optional(),
limit: z.number().int().nonnegative().optional(),
})

.optional();
Expand Down Expand Up @@ -1255,10 +1257,10 @@ export class InputValidator<Schema extends SchemaDef> {
const modelDef = requireModel(this.schema, model);
const nonRelationFields = Object.keys(modelDef.fields).filter((field) => !modelDef.fields[field]?.relation);

let schema = z.strictObject({
let schema: z.ZodSchema = z.strictObject({
where: this.makeWhereSchema(model, false).optional(),
orderBy: this.orArray(this.makeOrderBySchema(model, false, true), true).optional(),
by: this.orArray(z.enum(nonRelationFields), true),
by: this.orArray(z.enum(nonRelationFields as [string, ...string[]]), true),
having: this.makeHavingSchema(model).optional(),
skip: this.makeSkipSchema().optional(),
take: this.makeTakeSchema().optional(),
Expand Down Expand Up @@ -1340,11 +1342,11 @@ export class InputValidator<Schema extends SchemaDef> {
// #region Helpers

private makeSkipSchema() {
return z.int().nonnegative();
return z.number().int().nonnegative();
}

private makeTakeSchema() {
return z.int();
return z.number().int();
}

private refineForSelectIncludeMutuallyExclusive(schema: ZodType) {
Expand Down
2 changes: 2 additions & 0 deletions packages/runtime/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export type * from './crud-types';
export * from './errors';
export type { ClientOptions } from './options';
export { definePlugin } from './plugin';
export type { ZenStackPromise } from './promise';
export type { ToKysely } from './query-builder';
export { sql } from 'kysely';
2 changes: 1 addition & 1 deletion packages/runtime/test/client-api/mixin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ model Bar with CommonFields {
description: 'Bar',
},
}),
).rejects.toThrow('Invalid input');
).rejects.toThrow(/invalid/i);

await expect(
client.bar.create({
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/test/client-api/typed-json-fields.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ model User {
},
},
}),
).rejects.toThrow('Invalid input');
).rejects.toThrow(/invalid/i);
});

it('works with find', async () => {
Expand Down Expand Up @@ -215,7 +215,7 @@ model User {
},
},
}),
).rejects.toThrow('Invalid input');
).rejects.toThrow(/invalid/i);
});
},
);
2 changes: 2 additions & 0 deletions packages/runtime/test/schemas/typing/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export type Identity = $TypeDefResult<$Schema, "Identity">;
export type IdentityProvider = $TypeDefResult<$Schema, "IdentityProvider">;
export const Role = $schema.enums.Role;
export type Role = (typeof Role)[keyof typeof Role];
export const Status = $schema.enums.Status;
export type Status = (typeof Status)[keyof typeof Status];
Loading