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

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) {
@@ -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,202 @@ function handleSubProcessError2(err) {
912
912
  }
913
913
  __name(handleSubProcessError2, "handleSubProcessError");
914
914
 
915
+ // src/actions/proxy.ts
916
+ import { isDataSource as isDataSource2 } from "@zenstackhq/language/ast";
917
+ import { ZModelCodeGenerator } from "@zenstackhq/language";
918
+ import { getStringLiteral } from "@zenstackhq/language/utils";
919
+ import { SqliteDialect } from "@zenstackhq/orm/dialects/sqlite";
920
+ import { PostgresDialect } from "@zenstackhq/orm/dialects/postgres";
921
+ import SQLite from "better-sqlite3";
922
+ import { Pool } from "pg";
923
+ import path9 from "path";
924
+ import { ZenStackClient } from "@zenstackhq/orm";
925
+ import { RPCApiHandler } from "@zenstackhq/server/api";
926
+ import { ZenStackMiddleware } from "@zenstackhq/server/express";
927
+ import express from "express";
928
+ import colors9 from "colors";
929
+ import { createJiti as createJiti2 } from "jiti";
930
+
931
+ // src/utils/version-utils.ts
932
+ import colors8 from "colors";
933
+ import fs9 from "fs";
934
+ import path8 from "path";
935
+ import { fileURLToPath as fileURLToPath2 } from "url";
936
+ import semver from "semver";
937
+ var CHECK_VERSION_TIMEOUT = 2e3;
938
+ var VERSION_CHECK_TAG = "next";
939
+ function getVersion() {
940
+ try {
941
+ const _dirname = typeof __dirname !== "undefined" ? __dirname : path8.dirname(fileURLToPath2(import.meta.url));
942
+ return JSON.parse(fs9.readFileSync(path8.join(_dirname, "../package.json"), "utf8")).version;
943
+ } catch {
944
+ return void 0;
945
+ }
946
+ }
947
+ __name(getVersion, "getVersion");
948
+ async function checkNewVersion() {
949
+ const currVersion = getVersion();
950
+ let latestVersion;
951
+ try {
952
+ latestVersion = await getLatestVersion();
953
+ } catch {
954
+ return;
955
+ }
956
+ if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) {
957
+ console.log(`A newer version ${colors8.cyan(latestVersion)} is available.`);
958
+ }
959
+ }
960
+ __name(checkNewVersion, "checkNewVersion");
961
+ async function getLatestVersion() {
962
+ const fetchResult = await fetch(`https://registry.npmjs.org/@zenstackhq/cli/${VERSION_CHECK_TAG}`, {
963
+ headers: {
964
+ accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"
965
+ },
966
+ signal: AbortSignal.timeout(CHECK_VERSION_TIMEOUT)
967
+ });
968
+ if (fetchResult.ok) {
969
+ const data = await fetchResult.json();
970
+ const latestVersion = data?.version;
971
+ if (typeof latestVersion === "string" && semver.valid(latestVersion)) {
972
+ return latestVersion;
973
+ }
974
+ }
975
+ throw new Error("invalid npm registry response");
976
+ }
977
+ __name(getLatestVersion, "getLatestVersion");
978
+
979
+ // src/actions/proxy.ts
980
+ import cors from "cors";
981
+ async function run9(options) {
982
+ const schemaFile = getSchemaFile(options.schema);
983
+ console.log(colors9.gray(`Loading ZModel schema from: ${schemaFile}`));
984
+ let outputPath = getOutputPath(options, schemaFile);
985
+ if (!path9.isAbsolute(outputPath)) {
986
+ outputPath = path9.resolve(process.cwd(), outputPath);
987
+ }
988
+ const model = await loadSchemaDocument(schemaFile);
989
+ const dataSource = model.declarations.find(isDataSource2);
990
+ let databaseUrl = options.databaseUrl;
991
+ if (!databaseUrl) {
992
+ const schemaUrl = dataSource?.fields.find((f) => f.name === "url")?.value;
993
+ if (!schemaUrl) {
994
+ throw new CliError(`The schema's "datasource" does not have a "url" field, please provide it with -d option.`);
995
+ }
996
+ const zModelGenerator = new ZModelCodeGenerator();
997
+ const url = zModelGenerator.generate(schemaUrl);
998
+ databaseUrl = evaluateUrl(url);
999
+ }
1000
+ const provider = getStringLiteral(dataSource?.fields.find((f) => f.name === "provider")?.value);
1001
+ const dialect = createDialect(provider, databaseUrl, outputPath);
1002
+ const jiti = createJiti2(import.meta.url);
1003
+ const schemaModule = await jiti.import(path9.join(outputPath, "schema"));
1004
+ const allowedLogLevels = [
1005
+ "error",
1006
+ "query"
1007
+ ];
1008
+ const log = options.logLevel?.filter((level) => allowedLogLevels.includes(level));
1009
+ const db = new ZenStackClient(schemaModule.schema, {
1010
+ dialect,
1011
+ log: log && log.length > 0 ? log : void 0
1012
+ });
1013
+ try {
1014
+ await db.$connect();
1015
+ } catch (err) {
1016
+ throw new CliError(`Failed to connect to the database: ${err instanceof Error ? err.message : String(err)}`);
1017
+ }
1018
+ startServer(db, schemaModule.schema, options);
1019
+ }
1020
+ __name(run9, "run");
1021
+ function evaluateUrl(value) {
1022
+ const env2 = /* @__PURE__ */ __name((varName) => {
1023
+ const envValue = process.env[varName];
1024
+ if (!envValue) {
1025
+ throw new CliError(`Environment variable ${varName} is not set`);
1026
+ }
1027
+ return envValue;
1028
+ }, "env");
1029
+ try {
1030
+ const urlFn = new Function("env", `return ${value}`);
1031
+ const url = urlFn(env2);
1032
+ return url;
1033
+ } catch (err) {
1034
+ if (err instanceof CliError) {
1035
+ throw err;
1036
+ }
1037
+ throw new CliError("Could not evaluate datasource url from schema, you could provide it via -d option.");
1038
+ }
1039
+ }
1040
+ __name(evaluateUrl, "evaluateUrl");
1041
+ function createDialect(provider, databaseUrl, outputPath) {
1042
+ switch (provider) {
1043
+ case "sqlite": {
1044
+ let resolvedUrl = databaseUrl.trim();
1045
+ if (resolvedUrl.startsWith("file:")) {
1046
+ const filePath = resolvedUrl.substring("file:".length);
1047
+ if (!path9.isAbsolute(filePath)) {
1048
+ resolvedUrl = path9.join(outputPath, filePath);
1049
+ }
1050
+ }
1051
+ console.log(colors9.gray(`Connecting to SQLite database at: ${resolvedUrl}`));
1052
+ return new SqliteDialect({
1053
+ database: new SQLite(resolvedUrl)
1054
+ });
1055
+ }
1056
+ case "postgresql":
1057
+ console.log(colors9.gray(`Connecting to PostgreSQL database at: ${databaseUrl}`));
1058
+ return new PostgresDialect({
1059
+ pool: new Pool({
1060
+ connectionString: databaseUrl
1061
+ })
1062
+ });
1063
+ default:
1064
+ throw new CliError(`Unsupported database provider: ${provider}`);
1065
+ }
1066
+ }
1067
+ __name(createDialect, "createDialect");
1068
+ function startServer(client, schema, options) {
1069
+ const app = express();
1070
+ app.use(cors());
1071
+ app.use(express.json({
1072
+ limit: "5mb"
1073
+ }));
1074
+ app.use(express.urlencoded({
1075
+ extended: true,
1076
+ limit: "5mb"
1077
+ }));
1078
+ app.use("/api/model", ZenStackMiddleware({
1079
+ apiHandler: new RPCApiHandler({
1080
+ schema
1081
+ }),
1082
+ getClient: /* @__PURE__ */ __name(() => client, "getClient")
1083
+ }));
1084
+ app.get("/api/schema", (_req, res) => {
1085
+ res.json({
1086
+ ...schema,
1087
+ zenstackVersion: getVersion()
1088
+ });
1089
+ });
1090
+ const server = app.listen(options.port, () => {
1091
+ console.log(`ZenStack proxy server is running on port: ${options.port}`);
1092
+ console.log(`You can visit ZenStack Studio at: ${colors9.blue("https://studio.zenstack.dev")}`);
1093
+ });
1094
+ process.on("SIGTERM", async () => {
1095
+ server.close(() => {
1096
+ console.log("\nZenStack proxy server closed");
1097
+ });
1098
+ await client.$disconnect();
1099
+ process.exit(0);
1100
+ });
1101
+ process.on("SIGINT", async () => {
1102
+ server.close(() => {
1103
+ console.log("\nZenStack proxy server closed");
1104
+ });
1105
+ await client.$disconnect();
1106
+ process.exit(0);
1107
+ });
1108
+ }
1109
+ __name(startServer, "startServer");
1110
+
915
1111
  // src/telemetry.ts
916
1112
  import { init } from "mixpanel";
917
1113
  import { randomUUID as randomUUID2 } from "crypto";
@@ -926,14 +1122,14 @@ import { env } from "process";
926
1122
  var isInCi = env["CI"] !== "0" && env["CI"] !== "false" && ("CI" in env || "CONTINUOUS_INTEGRATION" in env || Object.keys(env).some((key) => key.startsWith("CI_")));
927
1123
 
928
1124
  // src/utils/is-container.ts
929
- import fs10 from "fs";
1125
+ import fs11 from "fs";
930
1126
 
931
1127
  // src/utils/is-docker.ts
932
- import fs9 from "fs";
1128
+ import fs10 from "fs";
933
1129
  var isDockerCached;
934
1130
  function hasDockerEnv() {
935
1131
  try {
936
- fs9.statSync("/.dockerenv");
1132
+ fs10.statSync("/.dockerenv");
937
1133
  return true;
938
1134
  } catch {
939
1135
  return false;
@@ -942,7 +1138,7 @@ function hasDockerEnv() {
942
1138
  __name(hasDockerEnv, "hasDockerEnv");
943
1139
  function hasDockerCGroup() {
944
1140
  try {
945
- return fs9.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
1141
+ return fs10.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
946
1142
  } catch {
947
1143
  return false;
948
1144
  }
@@ -960,7 +1156,7 @@ __name(isDocker, "isDocker");
960
1156
  var cachedResult;
961
1157
  var hasContainerEnv = /* @__PURE__ */ __name(() => {
962
1158
  try {
963
- fs10.statSync("/run/.containerenv");
1159
+ fs11.statSync("/run/.containerenv");
964
1160
  return true;
965
1161
  } catch {
966
1162
  return false;
@@ -977,7 +1173,7 @@ __name(isInContainer, "isInContainer");
977
1173
  // src/utils/is-wsl.ts
978
1174
  import process2 from "process";
979
1175
  import os from "os";
980
- import fs11 from "fs";
1176
+ import fs12 from "fs";
981
1177
  var isWsl = /* @__PURE__ */ __name(() => {
982
1178
  if (process2.platform !== "linux") {
983
1179
  return false;
@@ -986,7 +1182,7 @@ var isWsl = /* @__PURE__ */ __name(() => {
986
1182
  return true;
987
1183
  }
988
1184
  try {
989
- return fs11.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
1185
+ return fs12.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft");
990
1186
  } catch {
991
1187
  return false;
992
1188
  }
@@ -1049,54 +1245,6 @@ function getMachineId() {
1049
1245
  }
1050
1246
  __name(getMachineId, "getMachineId");
1051
1247
 
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
1248
  // src/telemetry.ts
1101
1249
  var Telemetry = class {
1102
1250
  static {
@@ -1224,10 +1372,13 @@ var formatAction = /* @__PURE__ */ __name(async (options) => {
1224
1372
  var seedAction = /* @__PURE__ */ __name(async (options, args) => {
1225
1373
  await telemetry.trackCommand("db seed", () => run7(options, args));
1226
1374
  }, "seedAction");
1375
+ var proxyAction = /* @__PURE__ */ __name(async (options) => {
1376
+ await telemetry.trackCommand("proxy", () => run9(options));
1377
+ }, "proxyAction");
1227
1378
  function createProgram() {
1228
1379
  const program = new Command("zen").alias("zenstack").helpOption("-h, --help", "Show this help message").version(getVersion(), "-v --version", "Show CLI version");
1229
1380
  const schemaExtensions = ZModelLanguageMetaData2.fileExtensions.join(", ");
1230
- program.description(`${colors9.bold.blue("\u03B6")} ZenStack is the modern data layer for TypeScript apps.
1381
+ program.description(`${colors10.bold.blue("\u03B6")} ZenStack is the modern data layer for TypeScript apps.
1231
1382
 
1232
1383
  Documentation: https://zenstack.dev/docs`).showHelpAfterError().showSuggestionAfterError();
1233
1384
  const schemaOption = new Option("--schema <file>", `schema file (with extension ${schemaExtensions}). Defaults to "zenstack/schema.zmodel" unless specified in package.json.`);
@@ -1256,6 +1407,7 @@ Arguments following -- are passed to the seed script. E.g.: "zen db seed -- --us
1256
1407
  program.command("init").description("Initialize an existing project for ZenStack").argument("[path]", "project path", ".").addOption(noVersionCheckOption).action(initAction);
1257
1408
  program.command("check").description("Check a ZModel schema for syntax or semantic errors").addOption(schemaOption).addOption(noVersionCheckOption).action(checkAction);
1258
1409
  program.command("format").description("Format a ZModel schema file").addOption(schemaOption).addOption(noVersionCheckOption).action(formatAction);
1410
+ 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
1411
  program.addHelpCommand("help [command]", "Display help for a command");
1260
1412
  program.hook("preAction", async (_thisCommand, actionCommand) => {
1261
1413
  if (actionCommand.getOptionValue("versionCheck") !== false) {
@@ -1277,14 +1429,17 @@ async function main() {
1277
1429
  if (e instanceof CommanderError) {
1278
1430
  exitCode = e.exitCode;
1279
1431
  } else if (e instanceof CliError) {
1280
- console.error(colors9.red(e.message));
1432
+ console.error(colors10.red(e.message));
1281
1433
  exitCode = 1;
1282
1434
  } else {
1283
- console.error(colors9.red(`Unhandled error: ${e}`));
1435
+ console.error(colors10.red(`Unhandled error: ${e}`));
1284
1436
  exitCode = 1;
1285
1437
  }
1286
1438
  }
1287
- if (program.args.includes("generate") && (program.args.includes("-w") || program.args.includes("--watch"))) {
1439
+ if (program.args.includes("generate") && (program.args.includes("-w") || program.args.includes("--watch")) || [
1440
+ "proxy",
1441
+ "studio"
1442
+ ].some((cmd) => program.args.includes(cmd))) {
1288
1443
  return;
1289
1444
  }
1290
1445
  if (telemetry.isTracking) {