Skip to content

Commit a21a56f

Browse files
authored
merge dev to main (v3.6.1) (#2599)
2 parents 03f6dfa + 14e1d55 commit a21a56f

35 files changed

Lines changed: 252 additions & 39 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "zenstack-v3",
33
"displayName": "ZenStack",
44
"description": "ZenStack",
5-
"version": "3.6.0",
5+
"version": "3.6.1",
66
"type": "module",
77
"author": {
88
"name": "ZenStack Team",

packages/auth-adapters/better-auth/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@zenstackhq/better-auth",
33
"displayName": "ZenStack Better Auth Adapter",
44
"description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.",
5-
"version": "3.6.0",
5+
"version": "3.6.1",
66
"type": "module",
77
"author": {
88
"name": "ZenStack Team",

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@zenstackhq/cli",
33
"displayName": "ZenStack CLI",
44
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
5-
"version": "3.6.0",
5+
"version": "3.6.1",
66
"type": "module",
77
"author": {
88
"name": "ZenStack Team",

packages/cli/src/actions/action-utils.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type Model, type Plugin, isDataSource, type LiteralExpr } from '@zensta
44
import { type CliPlugin, PrismaSchemaGenerator } from '@zenstackhq/sdk';
55
import colors from 'colors';
66
import { createJiti } from 'jiti';
7+
import crypto from 'node:crypto';
78
import fs from 'node:fs';
89
import { createRequire } from 'node:module';
910
import path from 'node:path';
@@ -86,16 +87,19 @@ export function handleSubProcessError(err: unknown) {
8687
}
8788
}
8889

89-
export async function generateTempPrismaSchema(zmodelPath: string, folder?: string) {
90+
export async function generateTempPrismaSchema(
91+
zmodelPath: string,
92+
opts: { folder?: string; randomName?: boolean } = {},
93+
) {
94+
const { folder: folderOpt, randomName = false } = opts;
9095
const model = await loadSchemaDocument(zmodelPath);
9196
if (!model.declarations.some(isDataSource)) {
9297
throw new CliError('Schema must define a datasource');
9398
}
9499
const prismaSchema = await new PrismaSchemaGenerator(model).generate();
95-
if (!folder) {
96-
folder = path.dirname(zmodelPath);
97-
}
98-
const prismaSchemaFile = path.resolve(folder, '~schema.prisma');
100+
const folder = folderOpt ?? path.dirname(zmodelPath);
101+
const fileName = randomName ? `~schema.${crypto.randomUUID()}.prisma` : '~schema.prisma';
102+
const prismaSchemaFile = path.resolve(folder, fileName);
99103
fs.writeFileSync(prismaSchemaFile, prismaSchema);
100104
return prismaSchemaFile;
101105
}

packages/cli/src/actions/db.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type PushOptions = {
2222
schema?: string;
2323
acceptDataLoss?: boolean;
2424
forceReset?: boolean;
25+
randomPrismaSchemaName?: boolean;
2526
};
2627

2728
export type PullOptions = {
@@ -55,7 +56,9 @@ async function runPush(options: PushOptions) {
5556
await requireDataSourceUrl(schemaFile);
5657

5758
// generate a temp prisma schema file
58-
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
59+
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, {
60+
randomName: options.randomPrismaSchemaName,
61+
});
5962

6063
try {
6164
// run prisma db push

packages/cli/src/actions/migrate.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type CommonOptions = {
99
schema?: string;
1010
migrations?: string;
1111
skipSeed?: boolean;
12+
randomPrismaSchemaName?: boolean;
1213
};
1314

1415
type DevOptions = CommonOptions & {
@@ -39,7 +40,10 @@ export async function run(command: string, options: CommonOptions) {
3940
await requireDataSourceUrl(schemaFile);
4041

4142
const prismaSchemaDir = options.migrations ? path.dirname(options.migrations) : undefined;
42-
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, prismaSchemaDir);
43+
const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, {
44+
folder: prismaSchemaDir,
45+
randomName: options.randomPrismaSchemaName,
46+
});
4347

4448
try {
4549
switch (command) {

packages/cli/src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ function createProgram() {
7676
const noVersionCheckOption = new Option('--no-version-check', 'do not check for new version');
7777
const noTipsOption = new Option('--no-tips', 'do not show usage tips');
7878

79+
const randomPrismaSchemaNameOption = new Option(
80+
'--random-prisma-schema-name',
81+
'append a random UUID to the temporary Prisma schema filename (e.g., ~schema.<uuid>.prisma) to avoid collisions between concurrent runs sharing a working directory',
82+
).default(false);
83+
7984
program
8085
.command('generate')
8186
.description('Run code generation plugins')
@@ -111,6 +116,7 @@ function createProgram() {
111116
.addOption(new Option('-n, --name <name>', 'migration name'))
112117
.addOption(new Option('--create-only', 'only create migration, do not apply'))
113118
.addOption(migrationsOption)
119+
.addOption(randomPrismaSchemaNameOption)
114120
.description('Create a migration from changes in schema and apply it to the database')
115121
.action((options) => migrateAction('dev', options));
116122

@@ -121,6 +127,7 @@ function createProgram() {
121127
.addOption(migrationsOption)
122128
.addOption(new Option('--skip-seed', 'skip seeding the database after reset'))
123129
.addOption(noVersionCheckOption)
130+
.addOption(randomPrismaSchemaNameOption)
124131
.description('Reset your database and apply all migrations, all data will be lost')
125132
.addHelpText(
126133
'after',
@@ -133,6 +140,7 @@ function createProgram() {
133140
.addOption(schemaOption)
134141
.addOption(noVersionCheckOption)
135142
.addOption(migrationsOption)
143+
.addOption(randomPrismaSchemaNameOption)
136144
.description('Deploy your pending migrations to your production/staging database')
137145
.action((options) => migrateAction('deploy', options));
138146

@@ -141,6 +149,7 @@ function createProgram() {
141149
.addOption(schemaOption)
142150
.addOption(noVersionCheckOption)
143151
.addOption(migrationsOption)
152+
.addOption(randomPrismaSchemaNameOption)
144153
.description('Check the status of your database migrations')
145154
.action((options) => migrateAction('status', options));
146155

@@ -149,6 +158,7 @@ function createProgram() {
149158
.addOption(schemaOption)
150159
.addOption(noVersionCheckOption)
151160
.addOption(migrationsOption)
161+
.addOption(randomPrismaSchemaNameOption)
152162
.addOption(new Option('--applied <migration>', 'record a specific migration as applied'))
153163
.addOption(new Option('--rolled-back <migration>', 'record a specific migration as rolled back'))
154164
.description('Resolve issues with database migrations in deployment databases')
@@ -163,6 +173,7 @@ function createProgram() {
163173
.addOption(noVersionCheckOption)
164174
.addOption(new Option('--accept-data-loss', 'ignore data loss warnings'))
165175
.addOption(new Option('--force-reset', 'force a reset of the database before push'))
176+
.addOption(randomPrismaSchemaNameOption)
166177
.action((options) => dbAction('push', options));
167178

168179
dbCommand
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import { describe, expect, it } from 'vitest';
4+
import { generateTempPrismaSchema } from '../src/actions/action-utils';
5+
import { createProject } from './utils';
6+
7+
const model = `
8+
model User {
9+
id String @id @default(cuid())
10+
}
11+
`;
12+
13+
describe('generateTempPrismaSchema', () => {
14+
it('defaults to a deterministic "~schema.prisma" filename', async () => {
15+
const { workDir } = await createProject(model, { provider: 'sqlite' });
16+
const schemaPath = path.join(workDir, 'zenstack/schema.zmodel');
17+
18+
const generated = await generateTempPrismaSchema(schemaPath);
19+
20+
try {
21+
expect(path.basename(generated)).toBe('~schema.prisma');
22+
expect(fs.existsSync(generated)).toBe(true);
23+
} finally {
24+
if (fs.existsSync(generated)) {
25+
fs.unlinkSync(generated);
26+
}
27+
}
28+
});
29+
30+
it('appends a random UUID segment when randomName is true', async () => {
31+
const { workDir } = await createProject(model, { provider: 'sqlite' });
32+
const schemaPath = path.join(workDir, 'zenstack/schema.zmodel');
33+
34+
const first = await generateTempPrismaSchema(schemaPath, { randomName: true });
35+
const second = await generateTempPrismaSchema(schemaPath, { randomName: true });
36+
37+
try {
38+
const uuidPattern = /^~schema\.[0-9a-f-]{36}\.prisma$/;
39+
expect(path.basename(first)).toMatch(uuidPattern);
40+
expect(path.basename(second)).toMatch(uuidPattern);
41+
expect(first).not.toBe(second);
42+
expect(fs.existsSync(first)).toBe(true);
43+
expect(fs.existsSync(second)).toBe(true);
44+
} finally {
45+
for (const f of [first, second]) {
46+
if (fs.existsSync(f)) {
47+
fs.unlinkSync(f);
48+
}
49+
}
50+
}
51+
});
52+
});

packages/clients/client-helpers/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@zenstackhq/client-helpers",
33
"displayName": "ZenStack Client Helpers",
44
"description": "Helpers for implementing clients that consume ZenStack's CRUD service",
5-
"version": "3.6.0",
5+
"version": "3.6.1",
66
"type": "module",
77
"author": {
88
"name": "ZenStack Team",

packages/clients/tanstack-query/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@zenstackhq/tanstack-query",
33
"displayName": "ZenStack TanStack Query Integration",
44
"description": "TanStack Query Client for consuming ZenStack v3's CRUD service",
5-
"version": "3.6.0",
5+
"version": "3.6.1",
66
"type": "module",
77
"author": {
88
"name": "ZenStack Team",

0 commit comments

Comments
 (0)