@zenstackhq/cli 3.0.0-alpha.9 → 3.0.0-beta.10

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.
package/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
3
7
 
4
8
  // src/index.ts
5
9
  import { ZModelLanguageMetaData } from "@zenstackhq/language";
6
- import colors5 from "colors";
7
- import { Command, Option } from "commander";
10
+ import colors7 from "colors";
11
+ import { Command, CommanderError, Option } from "commander";
8
12
 
9
13
  // src/actions/db.ts
10
14
  import fs2 from "fs";
@@ -12,10 +16,10 @@ import fs2 from "fs";
12
16
  // src/utils/exec-utils.ts
13
17
  import { execSync as _exec } from "child_process";
14
18
  function execSync(cmd, options) {
15
- const { env, ...restOptions } = options ?? {};
16
- const mergedEnv = env ? {
19
+ const { env: env2, ...restOptions } = options ?? {};
20
+ const mergedEnv = env2 ? {
17
21
  ...process.env,
18
- ...env
22
+ ...env2
19
23
  } : void 0;
20
24
  _exec(cmd, {
21
25
  encoding: "utf-8",
@@ -33,6 +37,7 @@ __name(execPackage, "execPackage");
33
37
 
34
38
  // src/actions/action-utils.ts
35
39
  import { loadDocument } from "@zenstackhq/language";
40
+ import { isDataSource } from "@zenstackhq/language/ast";
36
41
  import { PrismaSchemaGenerator } from "@zenstackhq/sdk";
37
42
  import colors from "colors";
38
43
  import fs from "fs";
@@ -53,6 +58,13 @@ function getSchemaFile(file) {
53
58
  }
54
59
  return file;
55
60
  }
61
+ const pkgJsonConfig = getPkgJsonConfig(process.cwd());
62
+ if (pkgJsonConfig.schema) {
63
+ if (!fs.existsSync(pkgJsonConfig.schema)) {
64
+ throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`);
65
+ }
66
+ return pkgJsonConfig.schema;
67
+ }
56
68
  if (fs.existsSync("./zenstack/schema.zmodel")) {
57
69
  return "./zenstack/schema.zmodel";
58
70
  } else if (fs.existsSync("./schema.zmodel")) {
@@ -65,12 +77,14 @@ __name(getSchemaFile, "getSchemaFile");
65
77
  async function loadSchemaDocument(schemaFile) {
66
78
  const loadResult = await loadDocument(schemaFile);
67
79
  if (!loadResult.success) {
68
- console.error(colors.red("Error loading schema:"));
69
80
  loadResult.errors.forEach((err) => {
70
81
  console.error(colors.red(err));
71
82
  });
72
- throw new CliError("Failed to load schema");
83
+ throw new CliError("Schema contains errors. See above for details.");
73
84
  }
85
+ loadResult.warnings.forEach((warn) => {
86
+ console.warn(colors.yellow(warn));
87
+ });
74
88
  return loadResult.model;
75
89
  }
76
90
  __name(loadSchemaDocument, "loadSchemaDocument");
@@ -82,14 +96,62 @@ function handleSubProcessError(err) {
82
96
  }
83
97
  }
84
98
  __name(handleSubProcessError, "handleSubProcessError");
85
- async function generateTempPrismaSchema(zmodelPath) {
99
+ async function generateTempPrismaSchema(zmodelPath, folder) {
86
100
  const model = await loadSchemaDocument(zmodelPath);
101
+ if (!model.declarations.some(isDataSource)) {
102
+ throw new CliError("Schema must define a datasource");
103
+ }
87
104
  const prismaSchema = await new PrismaSchemaGenerator(model).generate();
88
- const prismaSchemaFile = path.resolve(path.dirname(zmodelPath), "~schema.prisma");
105
+ if (!folder) {
106
+ folder = path.dirname(zmodelPath);
107
+ }
108
+ const prismaSchemaFile = path.resolve(folder, "~schema.prisma");
89
109
  fs.writeFileSync(prismaSchemaFile, prismaSchema);
90
110
  return prismaSchemaFile;
91
111
  }
92
112
  __name(generateTempPrismaSchema, "generateTempPrismaSchema");
113
+ function getPkgJsonConfig(startPath) {
114
+ const result = {
115
+ schema: void 0,
116
+ output: void 0
117
+ };
118
+ const pkgJsonFile = findUp([
119
+ "package.json"
120
+ ], startPath, false);
121
+ if (!pkgJsonFile) {
122
+ return result;
123
+ }
124
+ let pkgJson = void 0;
125
+ try {
126
+ pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, "utf8"));
127
+ } catch {
128
+ return result;
129
+ }
130
+ if (pkgJson.zenstack && typeof pkgJson.zenstack === "object") {
131
+ result.schema = pkgJson.zenstack.schema && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema);
132
+ result.output = pkgJson.zenstack.output && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output);
133
+ }
134
+ return result;
135
+ }
136
+ __name(getPkgJsonConfig, "getPkgJsonConfig");
137
+ function findUp(names, cwd = process.cwd(), multiple = false, result = []) {
138
+ if (!names.some((name) => !!name)) {
139
+ return void 0;
140
+ }
141
+ const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
142
+ if (multiple === false && target) {
143
+ return path.join(cwd, target);
144
+ }
145
+ if (target) {
146
+ result.push(path.join(cwd, target));
147
+ }
148
+ const up = path.resolve(cwd, "..");
149
+ if (up === cwd) {
150
+ return multiple && result.length > 0 ? result : void 0;
151
+ }
152
+ return findUp(names, up, multiple, result);
153
+ }
154
+ __name(findUp, "findUp");
93
155
 
94
156
  // src/actions/db.ts
95
157
  async function run(command, options) {
@@ -104,11 +166,15 @@ async function runPush(options) {
104
166
  const schemaFile = getSchemaFile(options.schema);
105
167
  const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
106
168
  try {
107
- const cmd = `prisma db push --schema "${prismaSchemaFile}"${options.acceptDataLoss ? " --accept-data-loss" : ""}${options.forceReset ? " --force-reset" : ""} --skip-generate`;
169
+ const cmd = [
170
+ "prisma db push",
171
+ ` --schema "${prismaSchemaFile}"`,
172
+ options.acceptDataLoss ? " --accept-data-loss" : "",
173
+ options.forceReset ? " --force-reset" : "",
174
+ " --skip-generate"
175
+ ].join("");
108
176
  try {
109
- await execPackage(cmd, {
110
- stdio: "inherit"
111
- });
177
+ await execPackage(cmd);
112
178
  } catch (err) {
113
179
  handleSubProcessError(err);
114
180
  }
@@ -123,30 +189,73 @@ __name(runPush, "runPush");
123
189
  // src/actions/generate.ts
124
190
  import { invariant } from "@zenstackhq/common-helpers";
125
191
  import { isPlugin } from "@zenstackhq/language/ast";
126
- import { PrismaSchemaGenerator as PrismaSchemaGenerator2, TsSchemaGenerator } from "@zenstackhq/sdk";
192
+ import { getLiteral, getLiteralArray } from "@zenstackhq/language/utils";
127
193
  import colors2 from "colors";
194
+ import path4 from "path";
195
+ import ora from "ora";
196
+
197
+ // src/plugins/index.ts
198
+ var plugins_exports = {};
199
+ __export(plugins_exports, {
200
+ prisma: () => prisma_default,
201
+ typescript: () => typescript_default
202
+ });
203
+
204
+ // src/plugins/prisma.ts
205
+ import { PrismaSchemaGenerator as PrismaSchemaGenerator2 } from "@zenstackhq/sdk";
128
206
  import fs3 from "fs";
129
207
  import path2 from "path";
130
- async function run2(options) {
131
- const schemaFile = getSchemaFile(options.schema);
132
- const model = await loadSchemaDocument(schemaFile);
133
- const outputPath = options.output ?? path2.dirname(schemaFile);
134
- const tsSchemaFile = path2.join(outputPath, "schema.ts");
135
- await new TsSchemaGenerator().generate(schemaFile, [], tsSchemaFile);
136
- await runPlugins(model, outputPath, tsSchemaFile);
137
- if (options.savePrismaSchema) {
208
+ var plugin = {
209
+ name: "Prisma Schema Generator",
210
+ statusText: "Generating Prisma schema",
211
+ async generate({ model, defaultOutputPath, pluginOptions }) {
212
+ let outFile = path2.join(defaultOutputPath, "schema.prisma");
213
+ if (typeof pluginOptions["output"] === "string") {
214
+ outFile = path2.resolve(defaultOutputPath, pluginOptions["output"]);
215
+ if (!fs3.existsSync(path2.dirname(outFile))) {
216
+ fs3.mkdirSync(path2.dirname(outFile), {
217
+ recursive: true
218
+ });
219
+ }
220
+ }
138
221
  const prismaSchema = await new PrismaSchemaGenerator2(model).generate();
139
- let prismaSchemaFile = path2.join(outputPath, "schema.prisma");
140
- if (typeof options.savePrismaSchema === "string") {
141
- prismaSchemaFile = path2.resolve(outputPath, options.savePrismaSchema);
142
- fs3.mkdirSync(path2.dirname(prismaSchemaFile), {
143
- recursive: true
144
- });
222
+ fs3.writeFileSync(outFile, prismaSchema);
223
+ }
224
+ };
225
+ var prisma_default = plugin;
226
+
227
+ // src/plugins/typescript.ts
228
+ import { TsSchemaGenerator } from "@zenstackhq/sdk";
229
+ import fs4 from "fs";
230
+ import path3 from "path";
231
+ var plugin2 = {
232
+ name: "TypeScript Schema Generator",
233
+ statusText: "Generating TypeScript schema",
234
+ async generate({ model, defaultOutputPath, pluginOptions }) {
235
+ let outDir = defaultOutputPath;
236
+ if (typeof pluginOptions["output"] === "string") {
237
+ outDir = path3.resolve(defaultOutputPath, pluginOptions["output"]);
238
+ if (!fs4.existsSync(outDir)) {
239
+ fs4.mkdirSync(outDir, {
240
+ recursive: true
241
+ });
242
+ }
145
243
  }
146
- fs3.writeFileSync(prismaSchemaFile, prismaSchema);
244
+ await new TsSchemaGenerator().generate(model, outDir);
147
245
  }
246
+ };
247
+ var typescript_default = plugin2;
248
+
249
+ // src/actions/generate.ts
250
+ async function run2(options) {
251
+ const start = Date.now();
252
+ const schemaFile = getSchemaFile(options.schema);
253
+ const model = await loadSchemaDocument(schemaFile);
254
+ const outputPath = getOutputPath(options, schemaFile);
255
+ await runPlugins(schemaFile, model, outputPath, options);
148
256
  if (!options.silent) {
149
- console.log(colors2.green("Generation completed successfully."));
257
+ console.log(colors2.green(`Generation completed successfully in ${Date.now() - start}ms.
258
+ `));
150
259
  console.log(`You can now create a ZenStack client with it.
151
260
 
152
261
  \`\`\`ts
@@ -154,37 +263,113 @@ import { ZenStackClient } from '@zenstackhq/runtime';
154
263
  import { schema } from '${outputPath}/schema';
155
264
 
156
265
  const client = new ZenStackClient(schema, {
157
- dialectConfig: { ... }
266
+ dialect: { ... }
158
267
  });
159
268
  \`\`\`
160
- `);
269
+
270
+ Check documentation: https://zenstack.dev/docs/3.x`);
161
271
  }
162
272
  }
163
273
  __name(run2, "run");
164
- async function runPlugins(model, outputPath, tsSchemaFile) {
274
+ function getOutputPath(options, schemaFile) {
275
+ if (options.output) {
276
+ return options.output;
277
+ }
278
+ const pkgJsonConfig = getPkgJsonConfig(process.cwd());
279
+ if (pkgJsonConfig.output) {
280
+ return pkgJsonConfig.output;
281
+ } else {
282
+ return path4.dirname(schemaFile);
283
+ }
284
+ }
285
+ __name(getOutputPath, "getOutputPath");
286
+ async function runPlugins(schemaFile, model, outputPath, options) {
165
287
  const plugins = model.declarations.filter(isPlugin);
166
- for (const plugin of plugins) {
167
- const providerField = plugin.fields.find((f) => f.name === "provider");
168
- invariant(providerField, `Plugin ${plugin.name} does not have a provider field`);
169
- const provider = providerField.value.value;
170
- let useProvider = provider;
171
- if (useProvider.startsWith("@core/")) {
172
- useProvider = `@zenstackhq/runtime/plugins/${useProvider.slice(6)}`;
288
+ const processedPlugins = [];
289
+ for (const plugin3 of plugins) {
290
+ const provider = getPluginProvider(plugin3);
291
+ let cliPlugin;
292
+ if (provider.startsWith("@core/")) {
293
+ cliPlugin = plugins_exports[provider.slice("@core/".length)];
294
+ if (!cliPlugin) {
295
+ throw new CliError(`Unknown core plugin: ${provider}`);
296
+ }
297
+ } else {
298
+ let moduleSpec = provider;
299
+ if (moduleSpec.startsWith(".")) {
300
+ moduleSpec = path4.resolve(path4.dirname(schemaFile), moduleSpec);
301
+ }
302
+ try {
303
+ cliPlugin = (await import(moduleSpec)).default;
304
+ } catch {
305
+ }
306
+ }
307
+ if (cliPlugin) {
308
+ processedPlugins.push({
309
+ cliPlugin,
310
+ pluginOptions: getPluginOptions(plugin3)
311
+ });
312
+ }
313
+ }
314
+ const defaultPlugins = [
315
+ typescript_default
316
+ ].reverse();
317
+ defaultPlugins.forEach((d) => {
318
+ if (!processedPlugins.some((p) => p.cliPlugin === d)) {
319
+ processedPlugins.push({
320
+ cliPlugin: d,
321
+ pluginOptions: {}
322
+ });
323
+ }
324
+ });
325
+ for (const { cliPlugin, pluginOptions } of processedPlugins) {
326
+ invariant(typeof cliPlugin.generate === "function", `Plugin ${cliPlugin.name} does not have a generate function`);
327
+ let spinner;
328
+ if (!options.silent) {
329
+ spinner = ora(cliPlugin.statusText ?? `Running plugin ${cliPlugin.name}`).start();
330
+ }
331
+ try {
332
+ await cliPlugin.generate({
333
+ schemaFile,
334
+ model,
335
+ defaultOutputPath: outputPath,
336
+ pluginOptions
337
+ });
338
+ spinner?.succeed();
339
+ } catch (err) {
340
+ spinner?.fail();
341
+ console.error(err);
173
342
  }
174
- const generator = (await import(useProvider)).default;
175
- console.log("Running generator:", provider);
176
- await generator({
177
- model,
178
- outputPath,
179
- tsSchemaFile
180
- });
181
343
  }
182
344
  }
183
345
  __name(runPlugins, "runPlugins");
346
+ function getPluginProvider(plugin3) {
347
+ const providerField = plugin3.fields.find((f) => f.name === "provider");
348
+ invariant(providerField, `Plugin ${plugin3.name} does not have a provider field`);
349
+ const provider = providerField.value.value;
350
+ return provider;
351
+ }
352
+ __name(getPluginProvider, "getPluginProvider");
353
+ function getPluginOptions(plugin3) {
354
+ const result = {};
355
+ for (const field of plugin3.fields) {
356
+ if (field.name === "provider") {
357
+ continue;
358
+ }
359
+ const value = getLiteral(field.value) ?? getLiteralArray(field.value);
360
+ if (value === void 0) {
361
+ console.warn(`Plugin "${plugin3.name}" option "${field.name}" has unsupported value, skipping`);
362
+ continue;
363
+ }
364
+ result[field.name] = value;
365
+ }
366
+ return result;
367
+ }
368
+ __name(getPluginOptions, "getPluginOptions");
184
369
 
185
370
  // src/actions/info.ts
186
371
  import colors3 from "colors";
187
- import path3 from "path";
372
+ import path5 from "path";
188
373
  async function run3(projectPath) {
189
374
  const packages = await getZenStackPackages(projectPath);
190
375
  if (!packages) {
@@ -193,11 +378,11 @@ async function run3(projectPath) {
193
378
  }
194
379
  console.log("Installed ZenStack Packages:");
195
380
  const versions = /* @__PURE__ */ new Set();
196
- for (const { pkg, version } of packages) {
197
- if (version) {
198
- versions.add(version);
381
+ for (const { pkg, version: version2 } of packages) {
382
+ if (version2) {
383
+ versions.add(version2);
199
384
  }
200
- console.log(` ${colors3.green(pkg.padEnd(20))} ${version}`);
385
+ console.log(` ${colors3.green(pkg.padEnd(20))} ${version2}`);
201
386
  }
202
387
  if (versions.size > 1) {
203
388
  console.warn(colors3.yellow("WARNING: Multiple versions of Zenstack packages detected. This may cause issues."));
@@ -206,9 +391,9 @@ async function run3(projectPath) {
206
391
  __name(run3, "run");
207
392
  async function getZenStackPackages(projectPath) {
208
393
  let pkgJson;
209
- const resolvedPath = path3.resolve(projectPath);
394
+ const resolvedPath = path5.resolve(projectPath);
210
395
  try {
211
- pkgJson = (await import(path3.join(resolvedPath, "package.json"), {
396
+ pkgJson = (await import(path5.join(resolvedPath, "package.json"), {
212
397
  with: {
213
398
  type: "json"
214
399
  }
@@ -227,6 +412,9 @@ async function getZenStackPackages(projectPath) {
227
412
  type: "json"
228
413
  }
229
414
  })).default;
415
+ if (depPkgJson.private) {
416
+ return void 0;
417
+ }
230
418
  return {
231
419
  pkg,
232
420
  version: depPkgJson.version
@@ -238,15 +426,15 @@ async function getZenStackPackages(projectPath) {
238
426
  };
239
427
  }
240
428
  }));
241
- return result;
429
+ return result.filter((p) => !!p);
242
430
  }
243
431
  __name(getZenStackPackages, "getZenStackPackages");
244
432
 
245
433
  // src/actions/init.ts
246
434
  import colors4 from "colors";
247
- import fs4 from "fs";
248
- import path4 from "path";
249
- import ora from "ora";
435
+ import fs5 from "fs";
436
+ import path6 from "path";
437
+ import ora2 from "ora";
250
438
  import { detect, resolveCommand } from "package-manager-detector";
251
439
 
252
440
  // src/actions/templates.ts
@@ -308,7 +496,7 @@ async function run4(projectPath) {
308
496
  if (!resolved) {
309
497
  throw new CliError(`Unable to determine how to install package "${pkg.name}". Please install it manually.`);
310
498
  }
311
- const spinner = ora(`Installing "${pkg.name}"`).start();
499
+ const spinner = ora2(`Installing "${pkg.name}"`).start();
312
500
  try {
313
501
  execSync(`${resolved.command} ${resolved.args.join(" ")}`, {
314
502
  cwd: projectPath
@@ -320,11 +508,11 @@ async function run4(projectPath) {
320
508
  }
321
509
  }
322
510
  const generationFolder = "zenstack";
323
- if (!fs4.existsSync(path4.join(projectPath, generationFolder))) {
324
- fs4.mkdirSync(path4.join(projectPath, generationFolder));
511
+ if (!fs5.existsSync(path6.join(projectPath, generationFolder))) {
512
+ fs5.mkdirSync(path6.join(projectPath, generationFolder));
325
513
  }
326
- if (!fs4.existsSync(path4.join(projectPath, generationFolder, "schema.zmodel"))) {
327
- fs4.writeFileSync(path4.join(projectPath, generationFolder, "schema.zmodel"), STARTER_ZMODEL);
514
+ if (!fs5.existsSync(path6.join(projectPath, generationFolder, "schema.zmodel"))) {
515
+ fs5.writeFileSync(path6.join(projectPath, generationFolder, "schema.zmodel"), STARTER_ZMODEL);
328
516
  } else {
329
517
  console.log(colors4.yellow("Schema file already exists. Skipping generation of sample."));
330
518
  }
@@ -335,10 +523,12 @@ async function run4(projectPath) {
335
523
  __name(run4, "run");
336
524
 
337
525
  // src/actions/migrate.ts
338
- import fs5 from "fs";
526
+ import fs6 from "fs";
527
+ import path7 from "path";
339
528
  async function run5(command, options) {
340
529
  const schemaFile = getSchemaFile(options.schema);
341
- const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
530
+ const prismaSchemaDir = options.migrations ? path7.dirname(options.migrations) : void 0;
531
+ const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, prismaSchemaDir);
342
532
  try {
343
533
  switch (command) {
344
534
  case "dev":
@@ -353,19 +543,27 @@ async function run5(command, options) {
353
543
  case "status":
354
544
  await runStatus(prismaSchemaFile, options);
355
545
  break;
546
+ case "resolve":
547
+ await runResolve(prismaSchemaFile, options);
548
+ break;
356
549
  }
357
550
  } finally {
358
- if (fs5.existsSync(prismaSchemaFile)) {
359
- fs5.unlinkSync(prismaSchemaFile);
551
+ if (fs6.existsSync(prismaSchemaFile)) {
552
+ fs6.unlinkSync(prismaSchemaFile);
360
553
  }
361
554
  }
362
555
  }
363
556
  __name(run5, "run");
364
557
  async function runDev(prismaSchemaFile, options) {
365
558
  try {
366
- await execPackage(`prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate${options.name ? ` --name ${options.name}` : ""}${options.createOnly ? " --create-only" : ""}`, {
367
- stdio: "inherit"
368
- });
559
+ const cmd = [
560
+ "prisma migrate dev",
561
+ ` --schema "${prismaSchemaFile}"`,
562
+ " --skip-generate",
563
+ options.name ? ` --name ${options.name}` : "",
564
+ options.createOnly ? " --create-only" : ""
565
+ ].join("");
566
+ await execPackage(cmd);
369
567
  } catch (err) {
370
568
  handleSubProcessError2(err);
371
569
  }
@@ -373,9 +571,13 @@ async function runDev(prismaSchemaFile, options) {
373
571
  __name(runDev, "runDev");
374
572
  async function runReset(prismaSchemaFile, options) {
375
573
  try {
376
- await execPackage(`prisma migrate reset --schema "${prismaSchemaFile}"${options.force ? " --force" : ""}`, {
377
- stdio: "inherit"
378
- });
574
+ const cmd = [
575
+ "prisma migrate reset",
576
+ ` --schema "${prismaSchemaFile}"`,
577
+ " --skip-generate",
578
+ options.force ? " --force" : ""
579
+ ].join("");
580
+ await execPackage(cmd);
379
581
  } catch (err) {
380
582
  handleSubProcessError2(err);
381
583
  }
@@ -383,9 +585,11 @@ async function runReset(prismaSchemaFile, options) {
383
585
  __name(runReset, "runReset");
384
586
  async function runDeploy(prismaSchemaFile, _options) {
385
587
  try {
386
- await execPackage(`prisma migrate deploy --schema "${prismaSchemaFile}"`, {
387
- stdio: "inherit"
388
- });
588
+ const cmd = [
589
+ "prisma migrate deploy",
590
+ ` --schema "${prismaSchemaFile}"`
591
+ ].join("");
592
+ await execPackage(cmd);
389
593
  } catch (err) {
390
594
  handleSubProcessError2(err);
391
595
  }
@@ -393,14 +597,29 @@ async function runDeploy(prismaSchemaFile, _options) {
393
597
  __name(runDeploy, "runDeploy");
394
598
  async function runStatus(prismaSchemaFile, _options) {
395
599
  try {
396
- await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`, {
397
- stdio: "inherit"
398
- });
600
+ await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`);
399
601
  } catch (err) {
400
602
  handleSubProcessError2(err);
401
603
  }
402
604
  }
403
605
  __name(runStatus, "runStatus");
606
+ async function runResolve(prismaSchemaFile, options) {
607
+ if (!options.applied && !options.rolledBack) {
608
+ throw new CliError("Either --applied or --rolled-back option must be provided");
609
+ }
610
+ try {
611
+ const cmd = [
612
+ "prisma migrate resolve",
613
+ ` --schema "${prismaSchemaFile}"`,
614
+ options.applied ? ` --applied ${options.applied}` : "",
615
+ options.rolledBack ? ` --rolled-back ${options.rolledBack}` : ""
616
+ ].join("");
617
+ await execPackage(cmd);
618
+ } catch (err) {
619
+ handleSubProcessError2(err);
620
+ }
621
+ }
622
+ __name(runResolve, "runResolve");
404
623
  function handleSubProcessError2(err) {
405
624
  if (err instanceof Error && "status" in err && typeof err.status === "number") {
406
625
  process.exit(err.status);
@@ -410,60 +629,383 @@ function handleSubProcessError2(err) {
410
629
  }
411
630
  __name(handleSubProcessError2, "handleSubProcessError");
412
631
 
632
+ // src/actions/check.ts
633
+ import colors5 from "colors";
634
+ async function run6(options) {
635
+ const schemaFile = getSchemaFile(options.schema);
636
+ try {
637
+ await loadSchemaDocument(schemaFile);
638
+ console.log(colors5.green("\u2713 Schema validation completed successfully."));
639
+ } catch (error) {
640
+ console.error(colors5.red("\u2717 Schema validation failed."));
641
+ throw error;
642
+ }
643
+ }
644
+ __name(run6, "run");
645
+
646
+ // src/telemetry.ts
647
+ import { init } from "mixpanel";
648
+ import { randomUUID as randomUUID2 } from "crypto";
649
+ import fs11 from "fs";
650
+ import * as os2 from "os";
651
+
652
+ // src/constants.ts
653
+ var TELEMETRY_TRACKING_TOKEN = "74944eb779d7d3b4ce185be843fde9fc";
654
+
655
+ // src/utils/is-ci.ts
656
+ import { env } from "process";
657
+ var isInCi = env["CI"] !== "0" && env["CI"] !== "false" && ("CI" in env || "CONTINUOUS_INTEGRATION" in env || Object.keys(env).some((key) => key.startsWith("CI_")));
658
+
659
+ // src/utils/is-container.ts
660
+ import fs8 from "fs";
661
+
662
+ // src/utils/is-docker.ts
663
+ import fs7 from "fs";
664
+ var isDockerCached;
665
+ function hasDockerEnv() {
666
+ try {
667
+ fs7.statSync("/.dockerenv");
668
+ return true;
669
+ } catch {
670
+ return false;
671
+ }
672
+ }
673
+ __name(hasDockerEnv, "hasDockerEnv");
674
+ function hasDockerCGroup() {
675
+ try {
676
+ return fs7.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
677
+ } catch {
678
+ return false;
679
+ }
680
+ }
681
+ __name(hasDockerCGroup, "hasDockerCGroup");
682
+ function isDocker() {
683
+ if (isDockerCached === void 0) {
684
+ isDockerCached = hasDockerEnv() || hasDockerCGroup();
685
+ }
686
+ return isDockerCached;
687
+ }
688
+ __name(isDocker, "isDocker");
689
+
690
+ // src/utils/is-container.ts
691
+ var cachedResult;
692
+ var hasContainerEnv = /* @__PURE__ */ __name(() => {
693
+ try {
694
+ fs8.statSync("/run/.containerenv");
695
+ return true;
696
+ } catch {
697
+ return false;
698
+ }
699
+ }, "hasContainerEnv");
700
+ function isInContainer() {
701
+ if (cachedResult === void 0) {
702
+ cachedResult = hasContainerEnv() || isDocker();
703
+ }
704
+ return cachedResult;
705
+ }
706
+ __name(isInContainer, "isInContainer");
707
+
708
+ // src/utils/is-wsl.ts
709
+ import process2 from "process";
710
+ import os from "os";
711
+ import fs9 from "fs";
712
+ var isWsl = /* @__PURE__ */ __name(() => {
713
+ if (process2.platform !== "linux") {
714
+ return false;
715
+ }
716
+ if (os.release().toLowerCase().includes("microsoft")) {
717
+ return true;
718
+ }
719
+ try {
720
+ return fs9.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
721
+ } catch {
722
+ return false;
723
+ }
724
+ }, "isWsl");
725
+
726
+ // src/utils/machine-id-utils.ts
727
+ import { execSync as execSync2 } from "child_process";
728
+ import { createHash, randomUUID } from "crypto";
729
+ var { platform } = process;
730
+ var win32RegBinPath = {
731
+ native: "%windir%\\System32",
732
+ mixed: "%windir%\\sysnative\\cmd.exe /c %windir%\\System32"
733
+ };
734
+ var guid = {
735
+ darwin: "ioreg -rd1 -c IOPlatformExpertDevice",
736
+ win32: `${win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]}\\REG.exe QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography /v MachineGuid`,
737
+ linux: "( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname 2> /dev/null) | head -n 1 || :",
738
+ freebsd: "kenv -q smbios.system.uuid || sysctl -n kern.hostuuid"
739
+ };
740
+ function isWindowsProcessMixedOrNativeArchitecture() {
741
+ if (process.arch === "ia32" && process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432")) {
742
+ return "mixed";
743
+ }
744
+ return "native";
745
+ }
746
+ __name(isWindowsProcessMixedOrNativeArchitecture, "isWindowsProcessMixedOrNativeArchitecture");
747
+ function hash(guid2) {
748
+ return createHash("sha256").update(guid2).digest("hex");
749
+ }
750
+ __name(hash, "hash");
751
+ function expose(result) {
752
+ switch (platform) {
753
+ case "darwin":
754
+ return result.split("IOPlatformUUID")[1]?.split("\n")[0]?.replace(/=|\s+|"/gi, "").toLowerCase();
755
+ case "win32":
756
+ return result.toString().split("REG_SZ")[1]?.replace(/\r+|\n+|\s+/gi, "").toLowerCase();
757
+ case "linux":
758
+ return result.toString().replace(/\r+|\n+|\s+/gi, "").toLowerCase();
759
+ case "freebsd":
760
+ return result.toString().replace(/\r+|\n+|\s+/gi, "").toLowerCase();
761
+ default:
762
+ throw new Error(`Unsupported platform: ${process.platform}`);
763
+ }
764
+ }
765
+ __name(expose, "expose");
766
+ function getMachineId() {
767
+ if (!(platform in guid)) {
768
+ return randomUUID();
769
+ }
770
+ try {
771
+ const value = execSync2(guid[platform]);
772
+ const id = expose(value.toString());
773
+ if (!id) {
774
+ return randomUUID();
775
+ }
776
+ return hash(id);
777
+ } catch {
778
+ return randomUUID();
779
+ }
780
+ }
781
+ __name(getMachineId, "getMachineId");
782
+
413
783
  // src/utils/version-utils.ts
414
- import fs6 from "fs";
415
- import path5 from "path";
784
+ import colors6 from "colors";
785
+ import fs10 from "fs";
786
+ import path8 from "path";
416
787
  import { fileURLToPath } from "url";
788
+ import semver from "semver";
789
+ var CHECK_VERSION_TIMEOUT = 2e3;
790
+ var VERSION_CHECK_TAG = "next";
417
791
  function getVersion() {
418
792
  try {
419
- const _dirname = typeof __dirname !== "undefined" ? __dirname : path5.dirname(fileURLToPath(import.meta.url));
420
- return JSON.parse(fs6.readFileSync(path5.join(_dirname, "../package.json"), "utf8")).version;
793
+ const _dirname = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath(import.meta.url));
794
+ return JSON.parse(fs10.readFileSync(path8.join(_dirname, "../package.json"), "utf8")).version;
421
795
  } catch {
422
796
  return void 0;
423
797
  }
424
798
  }
425
799
  __name(getVersion, "getVersion");
800
+ async function checkNewVersion() {
801
+ const currVersion = getVersion();
802
+ let latestVersion;
803
+ try {
804
+ latestVersion = await getLatestVersion();
805
+ } catch {
806
+ return;
807
+ }
808
+ if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) {
809
+ console.log(`A newer version ${colors6.cyan(latestVersion)} is available.`);
810
+ }
811
+ }
812
+ __name(checkNewVersion, "checkNewVersion");
813
+ async function getLatestVersion() {
814
+ const fetchResult = await fetch(`https://registry.npmjs.org/@zenstackhq/cli/${VERSION_CHECK_TAG}`, {
815
+ headers: {
816
+ accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"
817
+ },
818
+ signal: AbortSignal.timeout(CHECK_VERSION_TIMEOUT)
819
+ });
820
+ if (fetchResult.ok) {
821
+ const data = await fetchResult.json();
822
+ const latestVersion = data?.version;
823
+ if (typeof latestVersion === "string" && semver.valid(latestVersion)) {
824
+ return latestVersion;
825
+ }
826
+ }
827
+ throw new Error("invalid npm registry response");
828
+ }
829
+ __name(getLatestVersion, "getLatestVersion");
830
+
831
+ // src/telemetry.ts
832
+ var Telemetry = class {
833
+ static {
834
+ __name(this, "Telemetry");
835
+ }
836
+ mixpanel;
837
+ hostId = getMachineId();
838
+ sessionid = randomUUID2();
839
+ _os_type = os2.type();
840
+ _os_release = os2.release();
841
+ _os_arch = os2.arch();
842
+ _os_version = os2.version();
843
+ _os_platform = os2.platform();
844
+ version = getVersion();
845
+ prismaVersion = this.getPrismaVersion();
846
+ isDocker = isDocker();
847
+ isWsl = isWsl();
848
+ isContainer = isInContainer();
849
+ isCi = isInCi;
850
+ constructor() {
851
+ if (process.env["DO_NOT_TRACK"] !== "1" && TELEMETRY_TRACKING_TOKEN) {
852
+ this.mixpanel = init(TELEMETRY_TRACKING_TOKEN, {
853
+ geolocate: true
854
+ });
855
+ }
856
+ }
857
+ get isTracking() {
858
+ return !!this.mixpanel;
859
+ }
860
+ track(event, properties = {}) {
861
+ if (this.mixpanel) {
862
+ const payload = {
863
+ distinct_id: this.hostId,
864
+ session: this.sessionid,
865
+ time: /* @__PURE__ */ new Date(),
866
+ $os: this._os_type,
867
+ osType: this._os_type,
868
+ osRelease: this._os_release,
869
+ osPlatform: this._os_platform,
870
+ osArch: this._os_arch,
871
+ osVersion: this._os_version,
872
+ nodeVersion: process.version,
873
+ version: this.version,
874
+ prismaVersion: this.prismaVersion,
875
+ isDocker: this.isDocker,
876
+ isWsl: this.isWsl,
877
+ isContainer: this.isContainer,
878
+ isCi: this.isCi,
879
+ ...properties
880
+ };
881
+ this.mixpanel.track(event, payload);
882
+ }
883
+ }
884
+ trackError(err) {
885
+ this.track("cli:error", {
886
+ message: err.message,
887
+ stack: err.stack
888
+ });
889
+ }
890
+ async trackSpan(startEvent, completeEvent, errorEvent, properties, action) {
891
+ this.track(startEvent, properties);
892
+ const start = Date.now();
893
+ let success = true;
894
+ try {
895
+ return await action();
896
+ } catch (err) {
897
+ this.track(errorEvent, {
898
+ message: err.message,
899
+ stack: err.stack,
900
+ ...properties
901
+ });
902
+ success = false;
903
+ throw err;
904
+ } finally {
905
+ this.track(completeEvent, {
906
+ duration: Date.now() - start,
907
+ success,
908
+ ...properties
909
+ });
910
+ }
911
+ }
912
+ async trackCommand(command, action) {
913
+ await this.trackSpan("cli:command:start", "cli:command:complete", "cli:command:error", {
914
+ command
915
+ }, action);
916
+ }
917
+ async trackCli(action) {
918
+ await this.trackSpan("cli:start", "cli:complete", "cli:error", {}, action);
919
+ }
920
+ getPrismaVersion() {
921
+ try {
922
+ const packageJsonPath = import.meta.resolve("prisma/package.json");
923
+ const packageJsonUrl = new URL(packageJsonPath);
924
+ const packageJson = JSON.parse(fs11.readFileSync(packageJsonUrl, "utf8"));
925
+ return packageJson.version;
926
+ } catch {
927
+ return void 0;
928
+ }
929
+ }
930
+ };
931
+ var telemetry = new Telemetry();
426
932
 
427
933
  // src/index.ts
428
934
  var generateAction = /* @__PURE__ */ __name(async (options) => {
429
- await run2(options);
935
+ await telemetry.trackCommand("generate", () => run2(options));
430
936
  }, "generateAction");
431
- var migrateAction = /* @__PURE__ */ __name(async (command, options) => {
432
- await run5(command, options);
937
+ var migrateAction = /* @__PURE__ */ __name(async (subCommand, options) => {
938
+ await telemetry.trackCommand(`migrate ${subCommand}`, () => run5(subCommand, options));
433
939
  }, "migrateAction");
434
- var dbAction = /* @__PURE__ */ __name(async (command, options) => {
435
- await run(command, options);
940
+ var dbAction = /* @__PURE__ */ __name(async (subCommand, options) => {
941
+ await telemetry.trackCommand(`db ${subCommand}`, () => run(subCommand, options));
436
942
  }, "dbAction");
437
943
  var infoAction = /* @__PURE__ */ __name(async (projectPath) => {
438
- await run3(projectPath);
944
+ await telemetry.trackCommand("info", () => run3(projectPath));
439
945
  }, "infoAction");
440
946
  var initAction = /* @__PURE__ */ __name(async (projectPath) => {
441
- await run4(projectPath);
947
+ await telemetry.trackCommand("init", () => run4(projectPath));
442
948
  }, "initAction");
949
+ var checkAction = /* @__PURE__ */ __name(async (options) => {
950
+ await telemetry.trackCommand("check", () => run6(options));
951
+ }, "checkAction");
443
952
  function createProgram() {
444
- const program2 = new Command("zenstack");
445
- program2.version(getVersion(), "-v --version", "display CLI version");
953
+ const program = new Command("zen");
954
+ program.version(getVersion(), "-v --version", "display CLI version");
446
955
  const schemaExtensions = ZModelLanguageMetaData.fileExtensions.join(", ");
447
- program2.description(`${colors5.bold.blue("\u03B6")} ZenStack is a Prisma power pack for building full-stack apps.
448
-
449
- Documentation: https://zenstack.dev.`).showHelpAfterError().showSuggestionAfterError();
450
- const schemaOption = new Option("--schema <file>", `schema file (with extension ${schemaExtensions}). Defaults to "schema.zmodel" unless specified in package.json.`);
451
- program2.command("generate").description("Run code generation.").addOption(schemaOption).addOption(new Option("--silent", "do not print any output")).addOption(new Option("--save-prisma-schema [path]", "save a Prisma schema file, by default into the output directory")).addOption(new Option("-o, --output <path>", "default output directory for core plugins")).action(generateAction);
452
- const migrateCommand = program2.command("migrate").description("Update the database schema with migrations.");
453
- migrateCommand.command("dev").addOption(schemaOption).addOption(new Option("-n, --name <name>", "migration name")).addOption(new Option("--create-only", "only create migration, do not apply")).description("Create a migration from changes in schema and apply it to the database.").action((options) => migrateAction("dev", options));
454
- migrateCommand.command("reset").addOption(schemaOption).addOption(new Option("--force", "skip the confirmation prompt")).description("Reset your database and apply all migrations, all data will be lost.").action((options) => migrateAction("reset", options));
455
- migrateCommand.command("deploy").addOption(schemaOption).description("Deploy your pending migrations to your production/staging database.").action((options) => migrateAction("deploy", options));
456
- migrateCommand.command("status").addOption(schemaOption).description("check the status of your database migrations.").action((options) => migrateAction("status", options));
457
- const dbCommand = program2.command("db").description("Manage your database schema during development.");
458
- dbCommand.command("push").description("Push the state from your schema to your database").addOption(schemaOption).addOption(new Option("--accept-data-loss", "ignore data loss warnings")).addOption(new Option("--force-reset", "force a reset of the database before push")).action((options) => dbAction("push", options));
459
- program2.command("info").description("Get information of installed ZenStack and related packages.").argument("[path]", "project path", ".").action(infoAction);
460
- program2.command("init").description("Initialize an existing project for ZenStack.").argument("[path]", "project path", ".").action(initAction);
461
- return program2;
956
+ program.description(`${colors7.bold.blue("\u03B6")} ZenStack is the data layer for modern TypeScript apps.
957
+
958
+ Documentation: https://zenstack.dev/docs/3.x`).showHelpAfterError().showSuggestionAfterError();
959
+ const schemaOption = new Option("--schema <file>", `schema file (with extension ${schemaExtensions}). Defaults to "zenstack/schema.zmodel" unless specified in package.json.`);
960
+ const noVersionCheckOption = new Option("--no-version-check", "do not check for new version");
961
+ program.command("generate").description("Run code generation plugins.").addOption(schemaOption).addOption(noVersionCheckOption).addOption(new Option("-o, --output <path>", "default output directory for code generation")).addOption(new Option("--silent", "suppress all output except errors").default(false)).action(generateAction);
962
+ const migrateCommand = program.command("migrate").description("Run database schema migration related tasks.");
963
+ const migrationsOption = new Option("--migrations <path>", 'path that contains the "migrations" directory');
964
+ migrateCommand.command("dev").addOption(schemaOption).addOption(noVersionCheckOption).addOption(new Option("-n, --name <name>", "migration name")).addOption(new Option("--create-only", "only create migration, do not apply")).addOption(migrationsOption).description("Create a migration from changes in schema and apply it to the database.").action((options) => migrateAction("dev", options));
965
+ migrateCommand.command("reset").addOption(schemaOption).addOption(new Option("--force", "skip the confirmation prompt")).addOption(migrationsOption).addOption(noVersionCheckOption).description("Reset your database and apply all migrations, all data will be lost.").action((options) => migrateAction("reset", options));
966
+ migrateCommand.command("deploy").addOption(schemaOption).addOption(noVersionCheckOption).addOption(migrationsOption).description("Deploy your pending migrations to your production/staging database.").action((options) => migrateAction("deploy", options));
967
+ migrateCommand.command("status").addOption(schemaOption).addOption(noVersionCheckOption).addOption(migrationsOption).description("Check the status of your database migrations.").action((options) => migrateAction("status", options));
968
+ migrateCommand.command("resolve").addOption(schemaOption).addOption(noVersionCheckOption).addOption(migrationsOption).addOption(new Option("--applied <migration>", "record a specific migration as applied")).addOption(new Option("--rolled-back <migration>", "record a specific migration as rolled back")).description("Resolve issues with database migrations in deployment databases.").action((options) => migrateAction("resolve", options));
969
+ const dbCommand = program.command("db").description("Manage your database schema during development.");
970
+ dbCommand.command("push").description("Push the state from your schema to your database.").addOption(schemaOption).addOption(noVersionCheckOption).addOption(new Option("--accept-data-loss", "ignore data loss warnings")).addOption(new Option("--force-reset", "force a reset of the database before push")).action((options) => dbAction("push", options));
971
+ program.command("info").description("Get information of installed ZenStack packages.").argument("[path]", "project path", ".").addOption(noVersionCheckOption).action(infoAction);
972
+ program.command("init").description("Initialize an existing project for ZenStack.").argument("[path]", "project path", ".").addOption(noVersionCheckOption).action(initAction);
973
+ program.command("check").description("Check a ZModel schema for syntax or semantic errors.").addOption(schemaOption).addOption(noVersionCheckOption).action(checkAction);
974
+ program.hook("preAction", async (_thisCommand, actionCommand) => {
975
+ if (actionCommand.getOptionValue("versionCheck") !== false) {
976
+ await checkNewVersion();
977
+ }
978
+ });
979
+ return program;
462
980
  }
463
981
  __name(createProgram, "createProgram");
464
- var program = createProgram();
465
- program.parse(process.argv);
466
- export {
467
- createProgram
468
- };
982
+ async function main() {
983
+ let exitCode = 0;
984
+ const program = createProgram();
985
+ program.exitOverride();
986
+ try {
987
+ await telemetry.trackCli(async () => {
988
+ await program.parseAsync();
989
+ });
990
+ } catch (e) {
991
+ if (e instanceof CommanderError) {
992
+ exitCode = e.exitCode;
993
+ } else if (e instanceof CliError) {
994
+ console.error(colors7.red(e.message));
995
+ exitCode = 1;
996
+ } else {
997
+ console.error(colors7.red(`Unhandled error: ${e}`));
998
+ exitCode = 1;
999
+ }
1000
+ }
1001
+ if (telemetry.isTracking) {
1002
+ setTimeout(() => {
1003
+ process.exit(exitCode);
1004
+ }, 200);
1005
+ } else {
1006
+ process.exit(exitCode);
1007
+ }
1008
+ }
1009
+ __name(main, "main");
1010
+ main();
469
1011
  //# sourceMappingURL=index.js.map