@zenstackhq/cli 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/index.cjs +88 -5
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +89 -6
  4. package/dist/index.js.map +1 -1
  5. package/package.json +17 -9
  6. package/.turbo/turbo-build.log +0 -22
  7. package/eslint.config.js +0 -4
  8. package/scripts/post-build.ts +0 -20
  9. package/src/actions/action-utils.ts +0 -146
  10. package/src/actions/check.ts +0 -22
  11. package/src/actions/db.ts +0 -51
  12. package/src/actions/format.ts +0 -27
  13. package/src/actions/generate.ts +0 -226
  14. package/src/actions/index.ts +0 -10
  15. package/src/actions/info.ts +0 -71
  16. package/src/actions/init.ts +0 -61
  17. package/src/actions/migrate.ts +0 -149
  18. package/src/actions/seed.ts +0 -38
  19. package/src/actions/templates.ts +0 -58
  20. package/src/cli-error.ts +0 -4
  21. package/src/constants.ts +0 -5
  22. package/src/index.ts +0 -233
  23. package/src/plugins/index.ts +0 -2
  24. package/src/plugins/prisma.ts +0 -21
  25. package/src/plugins/typescript.ts +0 -40
  26. package/src/telemetry.ts +0 -139
  27. package/src/utils/exec-utils.ts +0 -61
  28. package/src/utils/is-ci.ts +0 -5
  29. package/src/utils/is-container.ts +0 -23
  30. package/src/utils/is-docker.ts +0 -31
  31. package/src/utils/is-wsl.ts +0 -18
  32. package/src/utils/machine-id-utils.ts +0 -76
  33. package/src/utils/version-utils.ts +0 -50
  34. package/test/check.test.ts +0 -101
  35. package/test/db.test.ts +0 -61
  36. package/test/format.test.ts +0 -33
  37. package/test/generate.test.ts +0 -76
  38. package/test/init.test.ts +0 -14
  39. package/test/migrate.test.ts +0 -72
  40. package/test/plugins/custom-plugin.test.ts +0 -50
  41. package/test/plugins/prisma-plugin.test.ts +0 -81
  42. package/test/ts-schema-gen.test.ts +0 -445
  43. package/test/utils.ts +0 -23
  44. package/tsconfig.json +0 -4
  45. package/tsup.config.ts +0 -13
  46. package/vitest.config.ts +0 -4
@@ -1,149 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { CliError } from '../cli-error';
4
- import { execPrisma } from '../utils/exec-utils';
5
- import { generateTempPrismaSchema, getSchemaFile, requireDataSourceUrl } from './action-utils';
6
- import { run as runSeed } from './seed';
7
-
8
- type CommonOptions = {
9
- schema?: string;
10
- migrations?: string;
11
- skipSeed?: boolean;
12
- };
13
-
14
- type DevOptions = CommonOptions & {
15
- name?: string;
16
- createOnly?: boolean;
17
- };
18
-
19
- type ResetOptions = CommonOptions & {
20
- force?: boolean;
21
- };
22
-
23
- type DeployOptions = CommonOptions;
24
-
25
- type StatusOptions = CommonOptions;
26
-
27
- type ResolveOptions = CommonOptions & {
28
- applied?: string;
29
- rolledBack?: string;
30
- };
31
-
32
- /**
33
- * CLI action for migration-related commands
34
- */
35
- export async function run(command: string, options: CommonOptions) {
36
- const schemaFile = getSchemaFile(options.schema);
37
-
38
- // validate datasource url exists
39
- await requireDataSourceUrl(schemaFile);
40
-
41
- const prismaSchemaDir = options.migrations ? path.dirname(options.migrations) : undefined;
42
- const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, prismaSchemaDir);
43
-
44
- try {
45
- switch (command) {
46
- case 'dev':
47
- await runDev(prismaSchemaFile, options as DevOptions);
48
- break;
49
-
50
- case 'reset':
51
- await runReset(prismaSchemaFile, options as ResetOptions);
52
- break;
53
-
54
- case 'deploy':
55
- await runDeploy(prismaSchemaFile, options as DeployOptions);
56
- break;
57
-
58
- case 'status':
59
- await runStatus(prismaSchemaFile, options as StatusOptions);
60
- break;
61
-
62
- case 'resolve':
63
- await runResolve(prismaSchemaFile, options as ResolveOptions);
64
- break;
65
- }
66
- } finally {
67
- if (fs.existsSync(prismaSchemaFile)) {
68
- fs.unlinkSync(prismaSchemaFile);
69
- }
70
- }
71
- }
72
-
73
- function runDev(prismaSchemaFile: string, options: DevOptions) {
74
- try {
75
- const cmd = [
76
- 'migrate dev',
77
- ` --schema "${prismaSchemaFile}"`,
78
- ' --skip-generate',
79
- ' --skip-seed',
80
- options.name ? ` --name "${options.name}"` : '',
81
- options.createOnly ? ' --create-only' : '',
82
- ].join('');
83
- execPrisma(cmd);
84
- } catch (err) {
85
- handleSubProcessError(err);
86
- }
87
- }
88
-
89
- async function runReset(prismaSchemaFile: string, options: ResetOptions) {
90
- try {
91
- const cmd = [
92
- 'migrate reset',
93
- ` --schema "${prismaSchemaFile}"`,
94
- ' --skip-generate',
95
- ' --skip-seed',
96
- options.force ? ' --force' : '',
97
- ].join('');
98
- execPrisma(cmd);
99
- } catch (err) {
100
- handleSubProcessError(err);
101
- }
102
-
103
- if (!options.skipSeed) {
104
- await runSeed({ noWarnings: true, printStatus: true }, []);
105
- }
106
- }
107
-
108
- function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
109
- try {
110
- const cmd = ['migrate deploy', ` --schema "${prismaSchemaFile}"`].join('');
111
- execPrisma(cmd);
112
- } catch (err) {
113
- handleSubProcessError(err);
114
- }
115
- }
116
-
117
- function runStatus(prismaSchemaFile: string, _options: StatusOptions) {
118
- try {
119
- execPrisma(`migrate status --schema "${prismaSchemaFile}"`);
120
- } catch (err) {
121
- handleSubProcessError(err);
122
- }
123
- }
124
-
125
- function runResolve(prismaSchemaFile: string, options: ResolveOptions) {
126
- if (!options.applied && !options.rolledBack) {
127
- throw new CliError('Either --applied or --rolled-back option must be provided');
128
- }
129
-
130
- try {
131
- const cmd = [
132
- 'migrate resolve',
133
- ` --schema "${prismaSchemaFile}"`,
134
- options.applied ? ` --applied "${options.applied}"` : '',
135
- options.rolledBack ? ` --rolled-back "${options.rolledBack}"` : '',
136
- ].join('');
137
- execPrisma(cmd);
138
- } catch (err) {
139
- handleSubProcessError(err);
140
- }
141
- }
142
-
143
- function handleSubProcessError(err: unknown) {
144
- if (err instanceof Error && 'status' in err && typeof err.status === 'number') {
145
- process.exit(err.status);
146
- } else {
147
- process.exit(1);
148
- }
149
- }
@@ -1,38 +0,0 @@
1
- import colors from 'colors';
2
- import { execaCommand } from 'execa';
3
- import { CliError } from '../cli-error';
4
- import { getPkgJsonConfig } from './action-utils';
5
-
6
- type Options = {
7
- noWarnings?: boolean;
8
- printStatus?: boolean;
9
- };
10
-
11
- /**
12
- * CLI action for seeding the database.
13
- */
14
- export async function run(options: Options, args: string[]) {
15
- const pkgJsonConfig = getPkgJsonConfig(process.cwd());
16
- if (!pkgJsonConfig.seed) {
17
- if (!options.noWarnings) {
18
- console.warn(colors.yellow('No seed script defined in package.json. Skipping seeding.'));
19
- }
20
- return;
21
- }
22
-
23
- const command = `${pkgJsonConfig.seed}${args.length > 0 ? ' ' + args.join(' ') : ''}`;
24
-
25
- if (options.printStatus) {
26
- console.log(colors.gray(`Running seed script "${command}"...`));
27
- }
28
-
29
- try {
30
- await execaCommand(command, {
31
- stdout: 'inherit',
32
- stderr: 'inherit',
33
- });
34
- } catch (err) {
35
- console.error(colors.red(err instanceof Error ? err.message : String(err)));
36
- throw new CliError('Failed to seed the database. Please check the error message above for details.');
37
- }
38
- }
@@ -1,58 +0,0 @@
1
- export const STARTER_ZMODEL = `// This is a sample model to get you started.
2
-
3
- /// A sample data source using local sqlite db.
4
- datasource db {
5
- provider = 'sqlite'
6
- url = 'file:./dev.db'
7
- }
8
-
9
- /// User model
10
- model User {
11
- id String @id @default(cuid())
12
- email String @unique @email @length(6, 32)
13
- posts Post[]
14
- }
15
-
16
- /// Post model
17
- model Post {
18
- id String @id @default(cuid())
19
- createdAt DateTime @default(now())
20
- updatedAt DateTime @updatedAt
21
- title String @length(1, 256)
22
- content String
23
- published Boolean @default(false)
24
- author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
25
- authorId String
26
- }
27
- `;
28
-
29
- export const STARTER_MAIN_TS = `import { ZenStackClient } from '@zenstackhq/orm';
30
- import { SqliteDialect } from '@zenstackhq/orm/dialects/sqlite';
31
- import SQLite from 'better-sqlite3';
32
- import { schema } from './zenstack/schema';
33
-
34
- async function main() {
35
- const db = new ZenStackClient(schema, {
36
- dialect: new SqliteDialect({
37
- database: new SQLite('./zenstack/dev.db'),
38
- }),
39
- });
40
- const user = await db.user.create({
41
- data: {
42
- email: 'test@zenstack.dev',
43
- posts: {
44
- create: [
45
- {
46
- title: 'Hello World',
47
- content: 'This is a test post',
48
- },
49
- ],
50
- },
51
- },
52
- include: { posts: true }
53
- });
54
- console.log('User created:', user);
55
- }
56
-
57
- main();
58
- `;
package/src/cli-error.ts DELETED
@@ -1,4 +0,0 @@
1
- /**
2
- * Indicating an error during CLI execution
3
- */
4
- export class CliError extends Error {}
package/src/constants.ts DELETED
@@ -1,5 +0,0 @@
1
- // replaced at build time
2
- export const TELEMETRY_TRACKING_TOKEN = '<TELEMETRY_TRACKING_TOKEN>';
3
-
4
- // plugin-contributed model file name
5
- export const PLUGIN_MODULE_NAME = 'plugin.zmodel';
package/src/index.ts DELETED
@@ -1,233 +0,0 @@
1
- import { ZModelLanguageMetaData } from '@zenstackhq/language';
2
- import colors from 'colors';
3
- import { Command, CommanderError, Option } from 'commander';
4
- import * as actions from './actions';
5
- import { CliError } from './cli-error';
6
- import { telemetry } from './telemetry';
7
- import { checkNewVersion, getVersion } from './utils/version-utils';
8
-
9
- const generateAction = async (options: Parameters<typeof actions.generate>[0]): Promise<void> => {
10
- await telemetry.trackCommand('generate', () => actions.generate(options));
11
- };
12
-
13
- const migrateAction = async (subCommand: string, options: any): Promise<void> => {
14
- await telemetry.trackCommand(`migrate ${subCommand}`, () => actions.migrate(subCommand, options));
15
- };
16
-
17
- const dbAction = async (subCommand: string, options: any): Promise<void> => {
18
- await telemetry.trackCommand(`db ${subCommand}`, () => actions.db(subCommand, options));
19
- };
20
-
21
- const infoAction = async (projectPath: string): Promise<void> => {
22
- await telemetry.trackCommand('info', () => actions.info(projectPath));
23
- };
24
-
25
- const initAction = async (projectPath: string): Promise<void> => {
26
- await telemetry.trackCommand('init', () => actions.init(projectPath));
27
- };
28
-
29
- const checkAction = async (options: Parameters<typeof actions.check>[0]): Promise<void> => {
30
- await telemetry.trackCommand('check', () => actions.check(options));
31
- };
32
-
33
- const formatAction = async (options: Parameters<typeof actions.format>[0]): Promise<void> => {
34
- await telemetry.trackCommand('format', () => actions.format(options));
35
- };
36
-
37
- const seedAction = async (options: Parameters<typeof actions.seed>[0], args: string[]): Promise<void> => {
38
- await telemetry.trackCommand('db seed', () => actions.seed(options, args));
39
- };
40
-
41
- function createProgram() {
42
- const program = new Command('zen')
43
- .alias('zenstack')
44
- .helpOption('-h, --help', 'Show this help message')
45
- .version(getVersion()!, '-v --version', 'Show CLI version');
46
-
47
- const schemaExtensions = ZModelLanguageMetaData.fileExtensions.join(', ');
48
-
49
- program
50
- .description(
51
- `${colors.bold.blue(
52
- 'ζ',
53
- )} ZenStack is the modern data layer for TypeScript apps.\n\nDocumentation: https://zenstack.dev/docs`,
54
- )
55
- .showHelpAfterError()
56
- .showSuggestionAfterError();
57
-
58
- const schemaOption = new Option(
59
- '--schema <file>',
60
- `schema file (with extension ${schemaExtensions}). Defaults to "zenstack/schema.zmodel" unless specified in package.json.`,
61
- );
62
-
63
- const noVersionCheckOption = new Option('--no-version-check', 'do not check for new version');
64
-
65
- program
66
- .command('generate')
67
- .description('Run code generation plugins')
68
- .addOption(schemaOption)
69
- .addOption(noVersionCheckOption)
70
- .addOption(new Option('-o, --output <path>', 'default output directory for code generation'))
71
- .addOption(new Option('--lite', 'also generate a lite version of schema without attributes').default(false))
72
- .addOption(new Option('--lite-only', 'only generate lite version of schema without attributes').default(false))
73
- .addOption(new Option('--silent', 'suppress all output except errors').default(false))
74
- .action(generateAction);
75
-
76
- const migrateCommand = program.command('migrate').description('Run database schema migration related tasks.');
77
- const migrationsOption = new Option('--migrations <path>', 'path that contains the "migrations" directory');
78
-
79
- migrateCommand
80
- .command('dev')
81
- .addOption(schemaOption)
82
- .addOption(noVersionCheckOption)
83
- .addOption(new Option('-n, --name <name>', 'migration name'))
84
- .addOption(new Option('--create-only', 'only create migration, do not apply'))
85
- .addOption(migrationsOption)
86
- .description('Create a migration from changes in schema and apply it to the database')
87
- .action((options) => migrateAction('dev', options));
88
-
89
- migrateCommand
90
- .command('reset')
91
- .addOption(schemaOption)
92
- .addOption(new Option('--force', 'skip the confirmation prompt'))
93
- .addOption(migrationsOption)
94
- .addOption(new Option('--skip-seed', 'skip seeding the database after reset'))
95
- .addOption(noVersionCheckOption)
96
- .description('Reset your database and apply all migrations, all data will be lost')
97
- .addHelpText(
98
- 'after',
99
- '\nIf there is a seed script defined in package.json, it will be run after the reset. Use --skip-seed to skip it.',
100
- )
101
- .action((options) => migrateAction('reset', options));
102
-
103
- migrateCommand
104
- .command('deploy')
105
- .addOption(schemaOption)
106
- .addOption(noVersionCheckOption)
107
- .addOption(migrationsOption)
108
- .description('Deploy your pending migrations to your production/staging database')
109
- .action((options) => migrateAction('deploy', options));
110
-
111
- migrateCommand
112
- .command('status')
113
- .addOption(schemaOption)
114
- .addOption(noVersionCheckOption)
115
- .addOption(migrationsOption)
116
- .description('Check the status of your database migrations')
117
- .action((options) => migrateAction('status', options));
118
-
119
- migrateCommand
120
- .command('resolve')
121
- .addOption(schemaOption)
122
- .addOption(noVersionCheckOption)
123
- .addOption(migrationsOption)
124
- .addOption(new Option('--applied <migration>', 'record a specific migration as applied'))
125
- .addOption(new Option('--rolled-back <migration>', 'record a specific migration as rolled back'))
126
- .description('Resolve issues with database migrations in deployment databases')
127
- .action((options) => migrateAction('resolve', options));
128
-
129
- const dbCommand = program.command('db').description('Manage your database schema during development');
130
-
131
- dbCommand
132
- .command('push')
133
- .description('Push the state from your schema to your database')
134
- .addOption(schemaOption)
135
- .addOption(noVersionCheckOption)
136
- .addOption(new Option('--accept-data-loss', 'ignore data loss warnings'))
137
- .addOption(new Option('--force-reset', 'force a reset of the database before push'))
138
- .action((options) => dbAction('push', options));
139
-
140
- dbCommand
141
- .command('seed')
142
- .description('Seed the database')
143
- .allowExcessArguments(true)
144
- .addHelpText(
145
- 'after',
146
- `
147
- Seed script is configured under the "zenstack.seed" field in package.json.
148
- E.g.:
149
- {
150
- "zenstack": {
151
- "seed": "ts-node ./zenstack/seed.ts"
152
- }
153
- }
154
-
155
- Arguments following -- are passed to the seed script. E.g.: "zen db seed -- --users 10"`,
156
- )
157
- .addOption(noVersionCheckOption)
158
- .action((options, command) => seedAction(options, command.args));
159
-
160
- program
161
- .command('info')
162
- .description('Get information of installed ZenStack packages')
163
- .argument('[path]', 'project path', '.')
164
- .addOption(noVersionCheckOption)
165
- .action(infoAction);
166
-
167
- program
168
- .command('init')
169
- .description('Initialize an existing project for ZenStack')
170
- .argument('[path]', 'project path', '.')
171
- .addOption(noVersionCheckOption)
172
- .action(initAction);
173
-
174
- program
175
- .command('check')
176
- .description('Check a ZModel schema for syntax or semantic errors')
177
- .addOption(schemaOption)
178
- .addOption(noVersionCheckOption)
179
- .action(checkAction);
180
-
181
- program
182
- .command('format')
183
- .description('Format a ZModel schema file')
184
- .addOption(schemaOption)
185
- .addOption(noVersionCheckOption)
186
- .action(formatAction);
187
-
188
- program.addHelpCommand('help [command]', 'Display help for a command');
189
-
190
- program.hook('preAction', async (_thisCommand, actionCommand) => {
191
- if (actionCommand.getOptionValue('versionCheck') !== false) {
192
- await checkNewVersion();
193
- }
194
- });
195
-
196
- return program;
197
- }
198
-
199
- async function main() {
200
- let exitCode = 0;
201
-
202
- const program = createProgram();
203
- program.exitOverride();
204
-
205
- try {
206
- await telemetry.trackCli(async () => {
207
- await program.parseAsync();
208
- });
209
- } catch (e) {
210
- if (e instanceof CommanderError) {
211
- // ignore
212
- exitCode = e.exitCode;
213
- } else if (e instanceof CliError) {
214
- // log
215
- console.error(colors.red(e.message));
216
- exitCode = 1;
217
- } else {
218
- console.error(colors.red(`Unhandled error: ${e}`));
219
- exitCode = 1;
220
- }
221
- }
222
-
223
- if (telemetry.isTracking) {
224
- // give telemetry a chance to send events before exit
225
- setTimeout(() => {
226
- process.exit(exitCode);
227
- }, 200);
228
- } else {
229
- process.exit(exitCode);
230
- }
231
- }
232
-
233
- main();
@@ -1,2 +0,0 @@
1
- export { default as prisma } from './prisma';
2
- export { default as typescript } from './typescript';
@@ -1,21 +0,0 @@
1
- import { PrismaSchemaGenerator, type CliPlugin } from '@zenstackhq/sdk';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
-
5
- const plugin: CliPlugin = {
6
- name: 'Prisma Schema Generator',
7
- statusText: 'Generating Prisma schema',
8
- async generate({ model, defaultOutputPath, pluginOptions }) {
9
- let outFile = path.join(defaultOutputPath, 'schema.prisma');
10
- if (typeof pluginOptions['output'] === 'string') {
11
- outFile = path.resolve(defaultOutputPath, pluginOptions['output']);
12
- if (!fs.existsSync(path.dirname(outFile))) {
13
- fs.mkdirSync(path.dirname(outFile), { recursive: true });
14
- }
15
- }
16
- const prismaSchema = await new PrismaSchemaGenerator(model).generate();
17
- fs.writeFileSync(outFile, prismaSchema);
18
- },
19
- };
20
-
21
- export default plugin;
@@ -1,40 +0,0 @@
1
- import type { CliPlugin } from '@zenstackhq/sdk';
2
- import { TsSchemaGenerator } from '@zenstackhq/sdk';
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
-
6
- const plugin: CliPlugin = {
7
- name: 'TypeScript Schema Generator',
8
- statusText: 'Generating TypeScript schema',
9
- async generate({ model, defaultOutputPath, pluginOptions }) {
10
- // output path
11
- let outDir = defaultOutputPath;
12
- if (typeof pluginOptions['output'] === 'string') {
13
- outDir = path.resolve(defaultOutputPath, pluginOptions['output']);
14
- if (!fs.existsSync(outDir)) {
15
- fs.mkdirSync(outDir, { recursive: true });
16
- }
17
- }
18
-
19
- // lite mode
20
- const lite = pluginOptions['lite'] === true;
21
-
22
- // liteOnly mode
23
- const liteOnly = pluginOptions['liteOnly'] === true;
24
-
25
- // add .js extension when importing
26
- const importWithFileExtension = pluginOptions['importWithFileExtension'];
27
- if (importWithFileExtension && typeof importWithFileExtension !== 'string') {
28
- throw new Error('The "importWithFileExtension" option must be a string if specified.');
29
- }
30
-
31
- await new TsSchemaGenerator().generate(model, {
32
- outDir,
33
- lite,
34
- liteOnly,
35
- importWithFileExtension: importWithFileExtension as string | undefined,
36
- });
37
- },
38
- };
39
-
40
- export default plugin;