@zenstackhq/cli 3.3.0-beta.2 → 3.3.0-beta.4

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
@@ -13,7 +13,7 @@ var __export = (target, all) => {
13
13
 
14
14
  // src/index.ts
15
15
  import { ZModelLanguageMetaData as ZModelLanguageMetaData2 } from "@zenstackhq/language";
16
- import colors9 from "colors";
16
+ import colors10 from "colors";
17
17
  import { Command, CommanderError, Option } from "commander";
18
18
 
19
19
  // src/actions/check.ts
@@ -154,6 +154,18 @@ async function requireDataSourceUrl(schemaFile) {
154
154
  }
155
155
  }
156
156
  __name(requireDataSourceUrl, "requireDataSourceUrl");
157
+ function getOutputPath(options, schemaFile) {
158
+ if (options.output) {
159
+ return options.output;
160
+ }
161
+ const pkgJsonConfig = getPkgJsonConfig(process.cwd());
162
+ if (pkgJsonConfig.output) {
163
+ return pkgJsonConfig.output;
164
+ } else {
165
+ return path.dirname(schemaFile);
166
+ }
167
+ }
168
+ __name(getOutputPath, "getOutputPath");
157
169
 
158
170
  // src/actions/check.ts
159
171
  async function run(options) {
@@ -214,7 +226,7 @@ function execPrisma(args, options) {
214
226
  execPackage(`prisma ${args}`, _options);
215
227
  return;
216
228
  }
217
- execSync(`node ${prismaPath} ${args}`, _options);
229
+ execSync(`node "${prismaPath}" ${args}`, _options);
218
230
  }
219
231
  __name(execPrisma, "execPrisma");
220
232
 
@@ -449,18 +461,6 @@ Check documentation: https://zenstack.dev/docs/`);
449
461
  return model;
450
462
  }
451
463
  __name(pureGenerate, "pureGenerate");
452
- function getOutputPath(options, schemaFile) {
453
- if (options.output) {
454
- return options.output;
455
- }
456
- const pkgJsonConfig = getPkgJsonConfig(process.cwd());
457
- if (pkgJsonConfig.output) {
458
- return pkgJsonConfig.output;
459
- } else {
460
- return path4.dirname(schemaFile);
461
- }
462
- }
463
- __name(getOutputPath, "getOutputPath");
464
464
  async function runPlugins(schemaFile, model, outputPath, options) {
465
465
  const plugins = model.declarations.filter(isPlugin);
466
466
  const processedPlugins = [];
@@ -912,6 +912,209 @@ function handleSubProcessError2(err) {
912
912
  }
913
913
  __name(handleSubProcessError2, "handleSubProcessError");
914
914
 
915
+ // src/actions/proxy.ts
916
+ import { ZModelCodeGenerator } from "@zenstackhq/language";
917
+ import { isDataSource as isDataSource2 } from "@zenstackhq/language/ast";
918
+ import { getStringLiteral } from "@zenstackhq/language/utils";
919
+ import { ZenStackClient } from "@zenstackhq/orm";
920
+ import { MysqlDialect } from "@zenstackhq/orm/dialects/mysql";
921
+ import { PostgresDialect } from "@zenstackhq/orm/dialects/postgres";
922
+ import { SqliteDialect } from "@zenstackhq/orm/dialects/sqlite";
923
+ import { RPCApiHandler } from "@zenstackhq/server/api";
924
+ import { ZenStackMiddleware } from "@zenstackhq/server/express";
925
+ import SQLite from "better-sqlite3";
926
+ import colors9 from "colors";
927
+ import cors from "cors";
928
+ import express from "express";
929
+ import { createJiti as createJiti2 } from "jiti";
930
+ import { createPool as createMysqlPool } from "mysql2";
931
+ import path9 from "path";
932
+ import { Pool as PgPool } from "pg";
933
+
934
+ // src/utils/version-utils.ts
935
+ import colors8 from "colors";
936
+ import fs9 from "fs";
937
+ import path8 from "path";
938
+ import { fileURLToPath as fileURLToPath2 } from "url";
939
+ import semver from "semver";
940
+ var CHECK_VERSION_TIMEOUT = 2e3;
941
+ var VERSION_CHECK_TAG = "next";
942
+ function getVersion() {
943
+ try {
944
+ const _dirname = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath2(import.meta.url));
945
+ return JSON.parse(fs9.readFileSync(path8.join(_dirname, "../package.json"), "utf8")).version;
946
+ } catch {
947
+ return void 0;
948
+ }
949
+ }
950
+ __name(getVersion, "getVersion");
951
+ async function checkNewVersion() {
952
+ const currVersion = getVersion();
953
+ let latestVersion;
954
+ try {
955
+ latestVersion = await getLatestVersion();
956
+ } catch {
957
+ return;
958
+ }
959
+ if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) {
960
+ console.log(`A newer version ${colors8.cyan(latestVersion)} is available.`);
961
+ }
962
+ }
963
+ __name(checkNewVersion, "checkNewVersion");
964
+ async function getLatestVersion() {
965
+ const fetchResult = await fetch(`https://registry.npmjs.org/@zenstackhq/cli/${VERSION_CHECK_TAG}`, {
966
+ headers: {
967
+ accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"
968
+ },
969
+ signal: AbortSignal.timeout(CHECK_VERSION_TIMEOUT)
970
+ });
971
+ if (fetchResult.ok) {
972
+ const data = await fetchResult.json();
973
+ const latestVersion = data?.version;
974
+ if (typeof latestVersion === "string" && semver.valid(latestVersion)) {
975
+ return latestVersion;
976
+ }
977
+ }
978
+ throw new Error("invalid npm registry response");
979
+ }
980
+ __name(getLatestVersion, "getLatestVersion");
981
+
982
+ // src/actions/proxy.ts
983
+ async function run9(options) {
984
+ const schemaFile = getSchemaFile(options.schema);
985
+ console.log(colors9.gray(`Loading ZModel schema from: ${schemaFile}`));
986
+ let outputPath = getOutputPath(options, schemaFile);
987
+ if (!path9.isAbsolute(outputPath)) {
988
+ outputPath = path9.resolve(process.cwd(), outputPath);
989
+ }
990
+ const model = await loadSchemaDocument(schemaFile);
991
+ const dataSource = model.declarations.find(isDataSource2);
992
+ let databaseUrl = options.databaseUrl;
993
+ if (!databaseUrl) {
994
+ const schemaUrl = dataSource?.fields.find((f) => f.name === "url")?.value;
995
+ if (!schemaUrl) {
996
+ throw new CliError(`The schema's "datasource" does not have a "url" field, please provide it with -d option.`);
997
+ }
998
+ const zModelGenerator = new ZModelCodeGenerator();
999
+ const url = zModelGenerator.generate(schemaUrl);
1000
+ databaseUrl = evaluateUrl(url);
1001
+ }
1002
+ const provider = getStringLiteral(dataSource?.fields.find((f) => f.name === "provider")?.value);
1003
+ const dialect = createDialect(provider, databaseUrl, outputPath);
1004
+ const jiti = createJiti2(import.meta.url);
1005
+ const schemaModule = await jiti.import(path9.join(outputPath, "schema"));
1006
+ const allowedLogLevels = [
1007
+ "error",
1008
+ "query"
1009
+ ];
1010
+ const log = options.logLevel?.filter((level) => allowedLogLevels.includes(level));
1011
+ const db = new ZenStackClient(schemaModule.schema, {
1012
+ dialect,
1013
+ log: log && log.length > 0 ? log : void 0
1014
+ });
1015
+ try {
1016
+ await db.$connect();
1017
+ } catch (err) {
1018
+ throw new CliError(`Failed to connect to the database: ${err instanceof Error ? err.message : String(err)}`);
1019
+ }
1020
+ startServer(db, schemaModule.schema, options);
1021
+ }
1022
+ __name(run9, "run");
1023
+ function evaluateUrl(value) {
1024
+ const env2 = /* @__PURE__ */ __name((varName) => {
1025
+ const envValue = process.env[varName];
1026
+ if (!envValue) {
1027
+ throw new CliError(`Environment variable ${varName} is not set`);
1028
+ }
1029
+ return envValue;
1030
+ }, "env");
1031
+ try {
1032
+ const urlFn = new Function("env", `return ${value}`);
1033
+ const url = urlFn(env2);
1034
+ return url;
1035
+ } catch (err) {
1036
+ if (err instanceof CliError) {
1037
+ throw err;
1038
+ }
1039
+ throw new CliError("Could not evaluate datasource url from schema, you could provide it via -d option.");
1040
+ }
1041
+ }
1042
+ __name(evaluateUrl, "evaluateUrl");
1043
+ function createDialect(provider, databaseUrl, outputPath) {
1044
+ switch (provider) {
1045
+ case "sqlite": {
1046
+ let resolvedUrl = databaseUrl.trim();
1047
+ if (resolvedUrl.startsWith("file:")) {
1048
+ const filePath = resolvedUrl.substring("file:".length);
1049
+ if (!path9.isAbsolute(filePath)) {
1050
+ resolvedUrl = path9.join(outputPath, filePath);
1051
+ }
1052
+ }
1053
+ console.log(colors9.gray(`Connecting to SQLite database at: ${resolvedUrl}`));
1054
+ return new SqliteDialect({
1055
+ database: new SQLite(resolvedUrl)
1056
+ });
1057
+ }
1058
+ case "postgresql":
1059
+ console.log(colors9.gray(`Connecting to PostgreSQL database at: ${databaseUrl}`));
1060
+ return new PostgresDialect({
1061
+ pool: new PgPool({
1062
+ connectionString: databaseUrl
1063
+ })
1064
+ });
1065
+ case "mysql":
1066
+ console.log(colors9.gray(`Connecting to MySQL database at: ${databaseUrl}`));
1067
+ return new MysqlDialect({
1068
+ pool: createMysqlPool(databaseUrl)
1069
+ });
1070
+ default:
1071
+ throw new CliError(`Unsupported database provider: ${provider}`);
1072
+ }
1073
+ }
1074
+ __name(createDialect, "createDialect");
1075
+ function startServer(client, schema, options) {
1076
+ const app = express();
1077
+ app.use(cors());
1078
+ app.use(express.json({
1079
+ limit: "5mb"
1080
+ }));
1081
+ app.use(express.urlencoded({
1082
+ extended: true,
1083
+ limit: "5mb"
1084
+ }));
1085
+ app.use("/api/model", ZenStackMiddleware({
1086
+ apiHandler: new RPCApiHandler({
1087
+ schema
1088
+ }),
1089
+ getClient: /* @__PURE__ */ __name(() => client, "getClient")
1090
+ }));
1091
+ app.get("/api/schema", (_req, res) => {
1092
+ res.json({
1093
+ ...schema,
1094
+ zenstackVersion: getVersion()
1095
+ });
1096
+ });
1097
+ const server = app.listen(options.port, () => {
1098
+ console.log(`ZenStack proxy server is running on port: ${options.port}`);
1099
+ console.log(`You can visit ZenStack Studio at: ${colors9.blue("https://studio.zenstack.dev")}`);
1100
+ });
1101
+ process.on("SIGTERM", async () => {
1102
+ server.close(() => {
1103
+ console.log("\nZenStack proxy server closed");
1104
+ });
1105
+ await client.$disconnect();
1106
+ process.exit(0);
1107
+ });
1108
+ process.on("SIGINT", async () => {
1109
+ server.close(() => {
1110
+ console.log("\nZenStack proxy server closed");
1111
+ });
1112
+ await client.$disconnect();
1113
+ process.exit(0);
1114
+ });
1115
+ }
1116
+ __name(startServer, "startServer");
1117
+
915
1118
  // src/telemetry.ts
916
1119
  import { init } from "mixpanel";
917
1120
  import { randomUUID as randomUUID2 } from "crypto";
@@ -926,14 +1129,14 @@ import { env } from "process";
926
1129
  var isInCi = env["CI"] !== "0" && env["CI"] !== "false" && ("CI" in env || "CONTINUOUS_INTEGRATION" in env || Object.keys(env).some((key) => key.startsWith("CI_")));
927
1130
 
928
1131
  // src/utils/is-container.ts
929
- import fs10 from "fs";
1132
+ import fs11 from "fs";
930
1133
 
931
1134
  // src/utils/is-docker.ts
932
- import fs9 from "fs";
1135
+ import fs10 from "fs";
933
1136
  var isDockerCached;
934
1137
  function hasDockerEnv() {
935
1138
  try {
936
- fs9.statSync("/.dockerenv");
1139
+ fs10.statSync("/.dockerenv");
937
1140
  return true;
938
1141
  } catch {
939
1142
  return false;
@@ -942,7 +1145,7 @@ function hasDockerEnv() {
942
1145
  __name(hasDockerEnv, "hasDockerEnv");
943
1146
  function hasDockerCGroup() {
944
1147
  try {
945
- return fs9.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
1148
+ return fs10.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
946
1149
  } catch {
947
1150
  return false;
948
1151
  }
@@ -960,7 +1163,7 @@ __name(isDocker, "isDocker");
960
1163
  var cachedResult;
961
1164
  var hasContainerEnv = /* @__PURE__ */ __name(() => {
962
1165
  try {
963
- fs10.statSync("/run/.containerenv");
1166
+ fs11.statSync("/run/.containerenv");
964
1167
  return true;
965
1168
  } catch {
966
1169
  return false;
@@ -977,7 +1180,7 @@ __name(isInContainer, "isInContainer");
977
1180
  // src/utils/is-wsl.ts
978
1181
  import process2 from "process";
979
1182
  import os from "os";
980
- import fs11 from "fs";
1183
+ import fs12 from "fs";
981
1184
  var isWsl = /* @__PURE__ */ __name(() => {
982
1185
  if (process2.platform !== "linux") {
983
1186
  return false;
@@ -986,7 +1189,7 @@ var isWsl = /* @__PURE__ */ __name(() => {
986
1189
  return true;
987
1190
  }
988
1191
  try {
989
- return fs11.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
1192
+ return fs12.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
990
1193
  } catch {
991
1194
  return false;
992
1195
  }
@@ -1049,54 +1252,6 @@ function getMachineId() {
1049
1252
  }
1050
1253
  __name(getMachineId, "getMachineId");
1051
1254
 
1052
- // src/utils/version-utils.ts
1053
- import colors8 from "colors";
1054
- import fs12 from "fs";
1055
- import path8 from "path";
1056
- import { fileURLToPath as fileURLToPath2 } from "url";
1057
- import semver from "semver";
1058
- var CHECK_VERSION_TIMEOUT = 2e3;
1059
- var VERSION_CHECK_TAG = "next";
1060
- function getVersion() {
1061
- try {
1062
- const _dirname = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath2(import.meta.url));
1063
- return JSON.parse(fs12.readFileSync(path8.join(_dirname, "../package.json"), "utf8")).version;
1064
- } catch {
1065
- return void 0;
1066
- }
1067
- }
1068
- __name(getVersion, "getVersion");
1069
- async function checkNewVersion() {
1070
- const currVersion = getVersion();
1071
- let latestVersion;
1072
- try {
1073
- latestVersion = await getLatestVersion();
1074
- } catch {
1075
- return;
1076
- }
1077
- if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) {
1078
- console.log(`A newer version ${colors8.cyan(latestVersion)} is available.`);
1079
- }
1080
- }
1081
- __name(checkNewVersion, "checkNewVersion");
1082
- async function getLatestVersion() {
1083
- const fetchResult = await fetch(`https://registry.npmjs.org/@zenstackhq/cli/${VERSION_CHECK_TAG}`, {
1084
- headers: {
1085
- accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"
1086
- },
1087
- signal: AbortSignal.timeout(CHECK_VERSION_TIMEOUT)
1088
- });
1089
- if (fetchResult.ok) {
1090
- const data = await fetchResult.json();
1091
- const latestVersion = data?.version;
1092
- if (typeof latestVersion === "string" && semver.valid(latestVersion)) {
1093
- return latestVersion;
1094
- }
1095
- }
1096
- throw new Error("invalid npm registry response");
1097
- }
1098
- __name(getLatestVersion, "getLatestVersion");
1099
-
1100
1255
  // src/telemetry.ts
1101
1256
  var Telemetry = class {
1102
1257
  static {
@@ -1224,10 +1379,13 @@ var formatAction = /* @__PURE__ */ __name(async (options) => {
1224
1379
  var seedAction = /* @__PURE__ */ __name(async (options, args) => {
1225
1380
  await telemetry.trackCommand("db seed", () => run7(options, args));
1226
1381
  }, "seedAction");
1382
+ var proxyAction = /* @__PURE__ */ __name(async (options) => {
1383
+ await telemetry.trackCommand("proxy", () => run9(options));
1384
+ }, "proxyAction");
1227
1385
  function createProgram() {
1228
1386
  const program = new Command("zen").alias("zenstack").helpOption("-h, --help", "Show this help message").version(getVersion(), "-v --version", "Show CLI version");
1229
1387
  const schemaExtensions = ZModelLanguageMetaData2.fileExtensions.join(", ");
1230
- program.description(`${colors9.bold.blue("\u03B6")} ZenStack is the modern data layer for TypeScript apps.
1388
+ program.description(`${colors10.bold.blue("\u03B6")} ZenStack is the modern data layer for TypeScript apps.
1231
1389
 
1232
1390
  Documentation: https://zenstack.dev/docs`).showHelpAfterError().showSuggestionAfterError();
1233
1391
  const schemaOption = new Option("--schema <file>", `schema file (with extension ${schemaExtensions}). Defaults to "zenstack/schema.zmodel" unless specified in package.json.`);
@@ -1256,6 +1414,7 @@ Arguments following -- are passed to the seed script. E.g.: "zen db seed -- --us
1256
1414
  program.command("init").description("Initialize an existing project for ZenStack").argument("[path]", "project path", ".").addOption(noVersionCheckOption).action(initAction);
1257
1415
  program.command("check").description("Check a ZModel schema for syntax or semantic errors").addOption(schemaOption).addOption(noVersionCheckOption).action(checkAction);
1258
1416
  program.command("format").description("Format a ZModel schema file").addOption(schemaOption).addOption(noVersionCheckOption).action(formatAction);
1417
+ program.command("proxy").alias("studio").description("Start the ZenStack proxy server").addOption(schemaOption).addOption(new Option("-p, --port <port>", "port to run the proxy server on").default(8008)).addOption(new Option("-o, --output <path>", "output directory for `zen generate` command")).addOption(new Option("-d, --databaseUrl <url>", "database connection URL")).addOption(new Option("-l, --logLevel <level>", "Query log levels (e.g., query, error)")).addOption(noVersionCheckOption).action(proxyAction);
1259
1418
  program.addHelpCommand("help [command]", "Display help for a command");
1260
1419
  program.hook("preAction", async (_thisCommand, actionCommand) => {
1261
1420
  if (actionCommand.getOptionValue("versionCheck") !== false) {
@@ -1277,14 +1436,17 @@ async function main() {
1277
1436
  if (e instanceof CommanderError) {
1278
1437
  exitCode = e.exitCode;
1279
1438
  } else if (e instanceof CliError) {
1280
- console.error(colors9.red(e.message));
1439
+ console.error(colors10.red(e.message));
1281
1440
  exitCode = 1;
1282
1441
  } else {
1283
- console.error(colors9.red(`Unhandled error: ${e}`));
1442
+ console.error(colors10.red(`Unhandled error: ${e}`));
1284
1443
  exitCode = 1;
1285
1444
  }
1286
1445
  }
1287
- if (program.args.includes("generate") && (program.args.includes("-w") || program.args.includes("--watch"))) {
1446
+ if (program.args.includes("generate") && (program.args.includes("-w") || program.args.includes("--watch")) || [
1447
+ "proxy",
1448
+ "studio"
1449
+ ].some((cmd) => program.args.includes(cmd))) {
1288
1450
  return;
1289
1451
  }
1290
1452
  if (telemetry.isTracking) {