@zveltio/cli 3.0.0-beta.79 → 3.0.0-beta.81

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 (2) hide show
  1. package/dist/index.js +79 -34
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -4028,7 +4028,7 @@ var CORE_FIELD_TS_TYPES = {
4028
4028
  smallint: "number",
4029
4029
  real: "number",
4030
4030
  decimal: "number",
4031
- money: "number",
4031
+ money: "string",
4032
4032
  varchar: "string",
4033
4033
  char: "string",
4034
4034
  time: "string",
@@ -4042,12 +4042,12 @@ var CORE_FIELD_TS_TYPES = {
4042
4042
  xml: "string",
4043
4043
  bit: "string",
4044
4044
  varbit: "string",
4045
- int4range: "{ lower: number; upper: number }",
4046
- int8range: "{ lower: number; upper: number }",
4047
- numrange: "{ lower: number; upper: number }",
4048
- daterange: "{ lower: string; upper: string }",
4049
- tsrange: "{ lower: string; upper: string }",
4050
- tstzrange: "{ lower: string; upper: string }"
4045
+ int4range: "string",
4046
+ int8range: "string",
4047
+ numrange: "string",
4048
+ daterange: "string",
4049
+ tsrange: "string",
4050
+ tstzrange: "string"
4051
4051
  };
4052
4052
  function emitCollectionTypes(collection, tsTypeOf = (t) => CORE_FIELD_TS_TYPES[t]) {
4053
4053
  const typeName = collection.name.charAt(0).toUpperCase() + collection.name.slice(1);
@@ -4465,7 +4465,10 @@ function validateManifest(input) {
4465
4465
  }
4466
4466
  }
4467
4467
  if (obj.apiKeyRoutes !== undefined) {
4468
- const routes = Array.isArray(obj.apiKeyRoutes) ? obj.apiKeyRoutes : [obj.apiKeyRoutes];
4468
+ const routes = Array.isArray(obj.apiKeyRoutes) ? obj.apiKeyRoutes : [];
4469
+ if (!Array.isArray(obj.apiKeyRoutes)) {
4470
+ out.push(err("MANIFEST_BAD_API_KEY_ROUTE", '"apiKeyRoutes" must be an array'));
4471
+ }
4469
4472
  for (const r of routes) {
4470
4473
  if (typeof r !== "string" || !/^(GET|POST|PUT|PATCH|DELETE) \S+$/.test(r)) {
4471
4474
  out.push(err("MANIFEST_BAD_API_KEY_ROUTE", `apiKeyRoutes entry ${JSON.stringify(r)} must be "<GET|POST|PUT|PATCH|DELETE> <pattern>"`));
@@ -4482,8 +4485,11 @@ function validateManifest(input) {
4482
4485
  if (!publicRoutes.includes(route)) {
4483
4486
  out.push(err("MANIFEST_BAD_FORWARD_CREDENTIALS", `forwardCredentials names "${route}", which is not a publicRoutes entry`));
4484
4487
  }
4485
- const list = Array.isArray(names) ? names : [names];
4486
- for (const n of list) {
4488
+ if (!Array.isArray(names)) {
4489
+ out.push(err("MANIFEST_BAD_FORWARD_CREDENTIALS", `forwardCredentials "${route}" must be an array of header names`));
4490
+ continue;
4491
+ }
4492
+ for (const n of names) {
4487
4493
  if (typeof n !== "string" || !CREDENTIAL_HEADERS.includes(n)) {
4488
4494
  out.push(err("MANIFEST_BAD_FORWARD_CREDENTIALS", `forwardCredentials "${route}": ${JSON.stringify(n)} is not one of ${CREDENTIAL_HEADERS.join(", ")}`));
4489
4495
  }
@@ -6061,7 +6067,7 @@ function typesFromFiles(collection, dir, outputPath) {
6061
6067
 
6062
6068
  // src/commands/schema.ts
6063
6069
  import { existsSync as existsSync16, mkdirSync as mkdirSync6, readdirSync as readdirSync7, readFileSync as readFileSync8, rmSync, writeFileSync as writeFileSync6 } from "fs";
6064
- import { dirname as dirname5, join as join15 } from "path";
6070
+ import { dirname as dirname5, join as join15, resolve as resolve4, sep } from "path";
6065
6071
  import { createInterface as createInterface2 } from "readline";
6066
6072
  async function schemaPullCommand(opts) {
6067
6073
  const dir = opts.dir || "./schema";
@@ -6077,8 +6083,17 @@ async function schemaPullCommand(opts) {
6077
6083
  process.exit(1);
6078
6084
  }
6079
6085
  const { files } = await res.json();
6080
- for (const [path, content] of Object.entries(files)) {
6081
- const target = join15(dir, path);
6086
+ const root = resolve4(dir);
6087
+ const inside = root.endsWith(sep) ? root : root + sep;
6088
+ const writes = Object.entries(files).map(([path, content]) => {
6089
+ const target = resolve4(root, path);
6090
+ if (!target.startsWith(inside)) {
6091
+ console.error(`Schema export named a path outside ${dir}: ${path}`);
6092
+ process.exit(1);
6093
+ }
6094
+ return [target, content];
6095
+ });
6096
+ for (const [target, content] of writes) {
6082
6097
  mkdirSync6(dirname5(target), { recursive: true });
6083
6098
  writeFileSync6(target, content);
6084
6099
  }
@@ -6110,18 +6125,24 @@ function readSchemaDir(dir) {
6110
6125
  }
6111
6126
  return files;
6112
6127
  }
6113
- async function postSchema(engineUrl, route, files, allowDestructive = false) {
6128
+ async function postSchema(engineUrl, route, files, allowDestructive = false, adoptRules = false) {
6114
6129
  const res = await fetch(`${engineUrl}/api/admin/schema/${route}`, {
6115
6130
  method: "POST",
6116
6131
  headers: {
6117
6132
  Authorization: `Bearer ${process.env.ZVELTIO_API_KEY || ""}`,
6118
6133
  "Content-Type": "application/json"
6119
6134
  },
6120
- body: JSON.stringify({ files, allowDestructive })
6135
+ body: JSON.stringify({ files, allowDestructive, ...adoptRules ? { adopt_rules: true } : {} })
6121
6136
  });
6122
6137
  if (!res.ok) {
6123
6138
  const body = await res.json().catch(() => null);
6124
6139
  console.error(`Schema ${route} failed: ${res.status} ${body?.detail ?? res.statusText}`);
6140
+ if (body?.code === "existing_rules") {
6141
+ for (const r of body.errors ?? []) {
6142
+ console.error(` ${r.object}: ${r.subject} ${r.action} in ${r.domain}`);
6143
+ }
6144
+ console.error(" Delete these rules, or rerun with --adopt-rules to keep them on the new collection.");
6145
+ }
6125
6146
  if (res.status === 401 || res.status === 403) {
6126
6147
  console.error(" Set ZVELTIO_API_KEY to a key of an instance admin.");
6127
6148
  }
@@ -6166,15 +6187,15 @@ The plan has destructive steps; nothing was changed. Rerun with --allow-destruct
6166
6187
  }
6167
6188
  if (!opts.yes) {
6168
6189
  const rl2 = createInterface2({ input: process.stdin, output: process.stdout });
6169
- const answer = await new Promise((resolve4) => rl2.question(`
6170
- Apply these changes? Type "yes" to continue: `, resolve4));
6190
+ const answer = await new Promise((resolve5) => rl2.question(`
6191
+ Apply these changes? Type "yes" to continue: `, resolve5));
6171
6192
  rl2.close();
6172
6193
  if (answer.trim().toLowerCase() !== "yes") {
6173
6194
  console.log("Cancelled; nothing was changed.");
6174
6195
  return;
6175
6196
  }
6176
6197
  }
6177
- const applied = await postSchema(engineUrl, "apply", files, opts.allowDestructive);
6198
+ const applied = await postSchema(engineUrl, "apply", files, opts.allowDestructive, opts.adoptRules);
6178
6199
  console.log(`Applied ${applied.length} change(s).`);
6179
6200
  }
6180
6201
 
@@ -6191,7 +6212,7 @@ async function installCommand(name, opts) {
6191
6212
  await installFromPath(name, opts.path, engineUrl, opts.force ?? false);
6192
6213
  return;
6193
6214
  }
6194
- await installFromCatalog(name, engineUrl);
6215
+ await installFromCatalog(name, engineUrl, opts.approveForwardCredentials ?? false);
6195
6216
  }
6196
6217
  async function installFromPath(name, sourcePath, engineUrl, force) {
6197
6218
  if (!existsSync17(join16(sourcePath, "manifest.json"))) {
@@ -6253,7 +6274,7 @@ async function installFromPath(name, sourcePath, engineUrl, force) {
6253
6274
  console.log(` ZVELTIO_EXTENSIONS=...,${category}/${extName}
6254
6275
  `);
6255
6276
  }
6256
- async function installFromCatalog(name, engineUrl) {
6277
+ async function installFromCatalog(name, engineUrl, approveForward) {
6257
6278
  console.log(` Checking engine catalog at ${engineUrl}...`);
6258
6279
  let catalogRes;
6259
6280
  try {
@@ -6304,11 +6325,30 @@ Available extensions:`);
6304
6325
  \u2705 Extension "${entry.displayName || entry.name}" is already active.`);
6305
6326
  return;
6306
6327
  }
6328
+ const forward = entry.forwarded_credentials ?? {};
6329
+ const forwardPairs = Object.entries(forward).flatMap(([route, headers]) => headers.map((h) => `${route}: ${h}`));
6330
+ const approval = approveForward ? { forward_credentials: forward } : undefined;
6307
6331
  if (entry.is_installed) {
6308
6332
  console.log(` Already installed. Enabling...`);
6333
+ if (approval && forwardPairs.length > 0) {
6334
+ const res = await fetch(`${engineUrl}/api/marketplace/${entry.name}/approve-forward-credentials`, {
6335
+ method: "POST",
6336
+ headers: { "Content-Type": "application/json" },
6337
+ body: JSON.stringify(approval)
6338
+ }).catch(() => null);
6339
+ if (!res?.ok) {
6340
+ console.error(`\u274C Credential forwarding approval failed: HTTP ${res?.status ?? "unreachable"}`);
6341
+ process.exit(1);
6342
+ }
6343
+ console.log(` \u2713 Credential forwarding approved (${forwardPairs.join(", ")})`);
6344
+ }
6309
6345
  } else {
6310
6346
  const installRes = await fetch(`${engineUrl}/api/marketplace/${entry.name}/install`, {
6311
- method: "POST"
6347
+ method: "POST",
6348
+ ...approval && {
6349
+ headers: { "Content-Type": "application/json" },
6350
+ body: JSON.stringify(approval)
6351
+ }
6312
6352
  }).catch(() => null);
6313
6353
  if (!installRes?.ok) {
6314
6354
  const err2 = await installRes?.json().catch(() => ({}));
@@ -6322,6 +6362,11 @@ Available extensions:`);
6322
6362
  console.log(` \u2713 Marked as installed`);
6323
6363
  }
6324
6364
  }
6365
+ if (forwardPairs.length > 0 && !approval) {
6366
+ console.log(` \u26A0\uFE0F Asks for the caller's credentials on: ${forwardPairs.join(", ")}`);
6367
+ console.log(` Not forwarded until approved: re-run with --approve-forward-credentials,`);
6368
+ console.log(` or approve it in Studio \u2192 Marketplace.`);
6369
+ }
6325
6370
  const enableRes = await fetch(`${engineUrl}/api/marketplace/${entry.name}/enable`, {
6326
6371
  method: "POST"
6327
6372
  }).catch(() => null);
@@ -6365,12 +6410,12 @@ async function activateViaApi(name, category, engineUrl) {
6365
6410
  // src/commands/create-god.ts
6366
6411
  import { createInterface as createInterface3 } from "readline";
6367
6412
  async function prompt(rl2, question) {
6368
- return new Promise((resolve4) => {
6369
- rl2.question(question, resolve4);
6413
+ return new Promise((resolve5) => {
6414
+ rl2.question(question, resolve5);
6370
6415
  });
6371
6416
  }
6372
6417
  async function promptHidden(question) {
6373
- return new Promise((resolve4) => {
6418
+ return new Promise((resolve5) => {
6374
6419
  process.stdout.write(question);
6375
6420
  const stdin = process.stdin;
6376
6421
  const wasPaused = stdin.isPaused();
@@ -6389,7 +6434,7 @@ async function promptHidden(question) {
6389
6434
  stdin.off("data", handler);
6390
6435
  process.stdout.write(`
6391
6436
  `);
6392
- resolve4(value);
6437
+ resolve5(value);
6393
6438
  } else if (char === "\x7F") {
6394
6439
  if (value.length > 0)
6395
6440
  value = value.slice(0, -1);
@@ -6642,9 +6687,9 @@ var rollbackCommand = new Command("rollback").description("Rollback database mig
6642
6687
  input: process.stdin,
6643
6688
  output: process.stdout
6644
6689
  });
6645
- const answer = await new Promise((resolve4) => {
6690
+ const answer = await new Promise((resolve5) => {
6646
6691
  rl2.question(`
6647
- \u26A0\uFE0F This will rollback schema changes. Type "yes" to continue: `, resolve4);
6692
+ \u26A0\uFE0F This will rollback schema changes. Type "yes" to continue: `, resolve5);
6648
6693
  });
6649
6694
  rl2.close();
6650
6695
  if (answer.trim().toLowerCase() !== "yes") {
@@ -6961,8 +7006,8 @@ function compareVersions(a, b) {
6961
7006
  return 0;
6962
7007
  }
6963
7008
  async function readLine() {
6964
- return new Promise((resolve4) => {
6965
- process.stdin.once("data", (data) => resolve4(data.toString().trim()));
7009
+ return new Promise((resolve5) => {
7010
+ process.stdin.once("data", (data) => resolve5(data.toString().trim()));
6966
7011
  });
6967
7012
  }
6968
7013
 
@@ -7094,7 +7139,7 @@ async function getLocalVersion() {
7094
7139
  // package.json
7095
7140
  var package_default = {
7096
7141
  name: "@zveltio/cli",
7097
- version: "3.0.0-beta.79",
7142
+ version: "3.0.0-beta.81",
7098
7143
  type: "module",
7099
7144
  bin: {
7100
7145
  zveltio: "./dist/index.js"
@@ -7109,7 +7154,7 @@ var package_default = {
7109
7154
  dev: "bun run --watch src/index.ts"
7110
7155
  },
7111
7156
  dependencies: {
7112
- "@zveltio/sdk": "3.0.0-beta.79",
7157
+ "@zveltio/sdk": "3.0.0-beta.81",
7113
7158
  commander: "^12.1.0"
7114
7159
  },
7115
7160
  devDependencies: {
@@ -7141,11 +7186,11 @@ program2.command("generate-types [collection]").description("Generate TypeScript
7141
7186
  var schema = program2.command("schema").description("Schema as code: the live schema as reviewable JSON files");
7142
7187
  schema.command("pull").description("Write the live schema to ./schema (collections, roles)").option("--dir <path>", "Schema directory", "./schema").option("--url <url>", "Engine URL (env: ZVELTIO_URL)").action(schemaPullCommand);
7143
7188
  schema.command("diff").description("Print what apply would change to match ./schema; exits 1 on drift").option("--dir <path>", "Schema directory", "./schema").option("--url <url>", "Engine URL (env: ZVELTIO_URL)").action(schemaDiffCommand);
7144
- schema.command("apply").description("Make the instance match ./schema; shows the plan and asks first").option("--dir <path>", "Schema directory", "./schema").option("--url <url>", "Engine URL (env: ZVELTIO_URL)").option("--yes", "Apply without asking").option("--allow-destructive", "Run drops and type changes that a migration in migrations/ names").action(schemaApplyCommand);
7145
- program2.command("install <name>").description("Install a Zveltio extension from marketplace or local path").option("--path <path>", "Install from local directory (offline)").option("--url <url>", "Engine URL for marketplace install", "http://localhost:3000").option("--force", "Overwrite existing extension (local install only)").action((name, opts) => installCommand(name, opts));
7189
+ schema.command("apply").description("Make the instance match ./schema; shows the plan and asks first").option("--dir <path>", "Schema directory", "./schema").option("--url <url>", "Engine URL (env: ZVELTIO_URL)").option("--yes", "Apply without asking").option("--allow-destructive", "Run drops and type changes that a migration in migrations/ names").option("--adopt-rules", "Create collections even when access rules already name them").action(schemaApplyCommand);
7190
+ program2.command("install <name>").description("Install a Zveltio extension from marketplace or local path").option("--path <path>", "Install from local directory (offline)").option("--url <url>", "Engine URL for marketplace install", "http://localhost:3000").option("--force", "Overwrite existing extension (local install only)").option("--approve-forward-credentials", "Approve the caller-credential forwarding the extension's manifest declares").action((name, opts) => installCommand(name, opts));
7146
7191
  var extensions = program2.command("extensions").description("Manage Zveltio extensions");
7147
7192
  extensions.command("list").description("List all available and installed extensions").option("--url <url>", "Engine URL", "http://localhost:3000").option("--category <category>", "Filter by category").option("--json", "Output as JSON").action((opts) => extensionsListCommand(opts));
7148
- extensions.command("install <name>").description("Install an extension from the marketplace or local path").option("--path <path>", "Install from local directory (offline)").option("--url <url>", "Engine URL", "http://localhost:3000").option("--force", "Overwrite existing extension (local install only)").action((name, opts) => installCommand(name, opts));
7193
+ extensions.command("install <name>").description("Install an extension from the marketplace or local path").option("--path <path>", "Install from local directory (offline)").option("--url <url>", "Engine URL", "http://localhost:3000").option("--force", "Overwrite existing extension (local install only)").option("--approve-forward-credentials", "Approve the caller-credential forwarding the extension's manifest declares").action((name, opts) => installCommand(name, opts));
7149
7194
  extensions.command("enable <name>").description("Enable an installed extension").option("--url <url>", "Engine URL", "http://localhost:3000").action(async (name, opts) => {
7150
7195
  const url = opts.url || "http://localhost:3000";
7151
7196
  const res = await fetch(`${url}/api/marketplace/${name}/enable`, { method: "POST" }).catch(() => null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zveltio/cli",
3
- "version": "3.0.0-beta.79",
3
+ "version": "3.0.0-beta.81",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zveltio": "./dist/index.js"
@@ -15,7 +15,7 @@
15
15
  "dev": "bun run --watch src/index.ts"
16
16
  },
17
17
  "dependencies": {
18
- "@zveltio/sdk": "3.0.0-beta.79",
18
+ "@zveltio/sdk": "3.0.0-beta.81",
19
19
  "commander": "^12.1.0"
20
20
  },
21
21
  "devDependencies": {