Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b51d33c
feat(language): implicitly convert `enum` references to arrays (#2807)
sanny-io Oct 5, 2026
c46fbad
feat(policy): field-level update policies on M2M relation fields (#2858)
ludovicmotte Oct 10, 2026
b6e0a8f
fix(language): semantic highlighting issues (#2800)
sanny-io Oct 10, 2026
9b9a179
feat(orm)!: Add typed typed mutation hooks #2365 (#2740)
genu Oct 13, 2026
5199bb1
fix(orm): follow-ups for typed entity mutation hooks (#2874)
ymc9 Oct 14, 2026
8cbe1b0
fix(clients): keep the ORM runtime out of client bundles (#2871)
ErikDakoda Oct 14, 2026
2c81cbd
fix(orm): stop delegate cascade delete recursion and simulate sub mod…
ErikDakoda Oct 19, 2026
efbdc88
feat: primitive custom types (#2852)
sanny-io Oct 22, 2026
151524b
chore(deps): bump dependencies flagged by Dependabot (#2879)
ymc9 Oct 22, 2026
db24ecf
fix(language): remove `this` grammar ambiguity in expressions (#2885)
ymc9 Oct 22, 2026
675fe20
fix(orm): pass the model alias to `$expr` filters (#2864)
Natansal-weme Oct 22, 2026
ee71982
fix(policy): compare `before()` enum fields as text on PostgreSQL (#2…
theluckystrike Oct 23, 2026
862d1c2
fix(orm): alias delegate joins in relation subqueries so they don't s…
ErikDakoda Oct 26, 2026
710aeb0
fix(orm): primitive custom type outputs (#2892)
sanny-io Oct 27, 2026
99dceb5
fix(language): transitive field reference scoping (#2803)
sanny-io Oct 27, 2026
fb98614
[CI] Bump version 3.10.0 (#2865)
github-actions[bot] Oct 27, 2026
87e87e4
fix(orm): set @updatedAt fields in updateMany (#2875)
ErikDakoda Oct 27, 2026
a36809a
fix(orm): resolve rows before simulating delegate cascades from regul…
ymc9 Oct 27, 2026
177e033
Merge remote-tracking branch 'origin/main' into chore/reconcile-main-…
ymc9 Oct 27, 2026
d44e489
chore: merge main back into dev ahead of v3.10.0 release (#2894)
ymc9 Oct 27, 2026
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
1 change: 1 addition & 0 deletions BREAKINGCHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
1. `@omit` and `@password` attributes have been removed
1. SWR plugin is removed
1. `makeModelSchema()` no longer includes relation fields by default — use `include` or `select` options to opt in, mirroring ORM behaviour
1. ORM entity mutation catch-all hooks should now be registered under `onEntityMutation.$all` (the previous shape still works but emits a deprecation warning); entities passed to per-model hooks are typed as raw database rows (same as `$qb` results)
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "zenstack-v3",
"displayName": "ZenStack",
"description": "ZenStack",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down Expand Up @@ -67,7 +67,7 @@
"overrides": {
"cookie@<0.7.0": ">=0.7.0",
"lodash@>=4.0.0 <=4.17.23": ">=4.18.0",
"@better-auth/core": "1.4.19"
"@better-auth/core": "1.7.12"
},
"patchedDependencies": {
"jsonschema@1.5.0": "patches/jsonschema@1.5.0.patch"
Expand Down
4 changes: 2 additions & 2 deletions packages/auth-adapters/better-auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const auth = new BetterAuth({

## Schema generation

You can use the `@better-auth/cli` to populate better-auth's data models into your ZModel schema.
You can use the `auth` CLI (formerly `@better-auth/cli`) to populate better-auth's data models into your ZModel schema.

```bash
npx @better-auth/cli generate
npx auth generate
```
8 changes: 4 additions & 4 deletions packages/auth-adapters/better-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@zenstackhq/better-auth",
"displayName": "ZenStack Better Auth Adapter",
"description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down Expand Up @@ -65,15 +65,15 @@
"better-auth": "^1.3.0"
},
"devDependencies": {
"@better-auth/cli": "1.4.19",
"@better-auth/core": "1.4.19",
"@better-auth/core": "1.7.12",
"auth": "1.7.12",
"@types/tmp": "catalog:",
"@zenstackhq/cli": "workspace:*",
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/tsdown-config": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"better-auth": "1.4.19",
"better-auth": "1.7.12",
"kysely": "catalog:",
"tmp": "catalog:"
},
Expand Down
16 changes: 7 additions & 9 deletions packages/auth-adapters/better-auth/src/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,7 @@ async function updateSchema(

const supportsArrays = getSupportsArrays(config);
for (const [name, table] of Object.entries(tables)) {
const c = addOrUpdateModel(
name,
table,
zmodel,
tables,
toManyRelations,
supportsArrays,
!!options.advanced?.database?.useNumberId,
);
const c = addOrUpdateModel(name, table, zmodel, tables, toManyRelations, supportsArrays, useNumberId(options));
changed = changed || c;
}

Expand Down Expand Up @@ -583,3 +575,9 @@ function createStringAttributeArg(value: string) {
arg.value = expr;
return arg;
}

function useNumberId(options: BetterAuthOptions) {
const dbOptions = options.advanced?.database as { useNumberId?: boolean; generateId?: unknown } | undefined;
// `useNumberId` was replaced by `generateId: 'serial'` in better-auth 1.7
return !!dbOptions?.useNumberId || dbOptions?.generateId === 'serial';
}
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@zenstackhq/cli",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down Expand Up @@ -61,7 +61,7 @@
"@zenstackhq/plugin-policy": "workspace:*",
"@zenstackhq/server": "workspace:*",
"@hono/node-server": "^1.13.8",
"hono": "^4.11.10",
"hono": "^4.13.7",
"chokidar": "^5.0.0",
"colors": "1.4.0",
"commander": "^8.3.0",
Expand Down
43 changes: 43 additions & 0 deletions packages/cli/test/prisma-schema-gen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,47 @@ model User {
expect(prismaSchemaText.includes('@ds.JsonB')).toBe(true);
expect(prismaSchemaText.includes('@ds.ByteA')).toBe(true);
});

it('renames primitive type defs to match the base type', async () => {
const model = await loadSchema(`
model User {
id String @id
name UserName
}

type UserName with String {
this String
}
`);

const generator = new PrismaSchemaGenerator(model);
const prismaSchemaText = await generator.generate();

expect(prismaSchemaText.includes('name UserName')).toBe(false);
expect(prismaSchemaText.includes('name String')).toBe(true);
});

it('renames primitive type defs to match the base type when used with lists', async () => {
const model = await loadSchema(`
datasource db {
provider = 'postgresql'
url = env('DATABASE_URL')
}

model User {
id String @id
name UserName[]
}

type UserName with String {
this String
}
`);

const generator = new PrismaSchemaGenerator(model);
const prismaSchemaText = await generator.generate();

expect(prismaSchemaText.includes('name UserName[]')).toBe(false);
expect(prismaSchemaText.includes('name String[]')).toBe(true);
});
});
177 changes: 177 additions & 0 deletions packages/cli/test/ts-schema-gen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,126 @@ model Post {
});
});

it('supports implicit conversions from enums to arrays', async () => {
const { schema } = await generateTsSchema(`
enum PostStatus {
DRAFT
ACTIVE
CANCELLED
}

model User {
id Int @id @default(autoincrement())
}

model Post {
id String @id
status String

@@validate(status in PostStatus)
}
`);

expect(schema.models['Post']?.attributes).toMatchObject([
{
name: '@@validate',
args: [
{
name: 'value',
value: {
kind: 'binary',
op: 'in',
left: {
kind: 'field',
field: 'status',
},
right: {
kind: 'array',
type: 'PostStatus',
items: [
{
kind: 'literal',
value: 'DRAFT',
},
{
kind: 'literal',
value: 'ACTIVE',
},
{
kind: 'literal',
value: 'CANCELLED',
},
],
},
binding: undefined,
},
},
],
},
]);
});

it('emits enum names (not @map values) when converting enums to arrays', async () => {
// enum values are represented by their names at runtime (TS values, auth(), Zod);
// the ORM's name mapper translates them to `@map`-ed values at SQL execution time
const { schema } = await generateTsSchema(`
enum PostStatus {
DRAFT @map('draft')
ACTIVE @map('active')
CANCELLED @map('cancelled')
}

model User {
id Int @id @default(autoincrement())
}

model Post {
id String @id
status String

@@validate(status in PostStatus)
}
`);

expect(schema.models['Post']?.attributes).toMatchObject([
{
name: '@@validate',
args: [
{
name: 'value',
value: {
kind: 'binary',
op: 'in',
left: {
kind: 'field',
field: 'status',
},
right: {
kind: 'array',
type: 'PostStatus',
items: [
{
kind: 'literal',
value: 'DRAFT',
},
{
kind: 'literal',
value: 'ACTIVE',
},
{
kind: 'literal',
value: 'CANCELLED',
},
],
},
binding: undefined,
},
},
],
},
]);
});

it('supports @@strict for type defs', async () => {
const { schema } = await generateTsSchema(`
model User {
Expand Down Expand Up @@ -895,4 +1015,61 @@ type Profile with Strict {
},
});
});

it('supports primitive type defs', async () => {
const { schema } = await generateTsSchema(`
model User {
id Int @id
name UserName
}

type UserName with String {
this String
}
`);

expect(schema).toMatchObject({
models: {
User: {
name: 'User',
fields: {
id: {
name: 'id',
type: 'Int',
id: true,
attributes: [
{
name: '@id',
},
],
},
name: {
name: 'name',
type: 'UserName',
},
},
idFields: ['id'],
uniqueFields: {
id: {
type: 'Int',
},
},
},
},
typeDefs: {
UserName: {
name: 'UserName',
base: 'String',
fields: {
this: {
name: 'this',
type: 'String',
},
},
},
},
authType: 'User',
plugins: {},
});
});
});
2 changes: 1 addition & 1 deletion packages/clients/client-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@zenstackhq/client-helpers",
"displayName": "ZenStack Client Helpers",
"description": "Helpers for implementing clients that consume ZenStack's CRUD service",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down
2 changes: 1 addition & 1 deletion packages/clients/fetch-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@zenstackhq/fetch-client",
"displayName": "ZenStack Fetch Client",
"description": "Simple fetch-based client for consuming ZenStack's RPC-style CRUD API",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down
2 changes: 1 addition & 1 deletion packages/clients/tanstack-query/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@zenstackhq/tanstack-query",
"displayName": "ZenStack TanStack Query Integration",
"description": "TanStack Query Client for consuming ZenStack v3's CRUD service",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down
2 changes: 1 addition & 1 deletion packages/common-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@zenstackhq/common-helpers",
"displayName": "ZenStack Common Helpers",
"description": "ZenStack Common Helpers",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"author": {
"name": "ZenStack Team",
Expand Down
2 changes: 1 addition & 1 deletion packages/config/eslint-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/eslint-config",
"version": "3.9.8",
"version": "3.10.0",
"type": "module",
"private": true,
"license": "MIT"
Expand Down
2 changes: 1 addition & 1 deletion packages/config/tsdown-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/tsdown-config",
"version": "3.9.8",
"version": "3.10.0",
"private": true,
"type": "module",
"license": "MIT",
Expand Down
Loading
Loading