@zveltio/cli 3.0.0-beta.78 → 3.0.0-beta.80

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 +98 -33
  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);
@@ -4146,7 +4146,7 @@ import { join as join10 } from "path";
4146
4146
 
4147
4147
  // ../sdk/dist/validate/migration-parse.js
4148
4148
  function parseMigrationSql(raw) {
4149
- const downIdx = raw.search(/^--\s*DOWN\b/im);
4149
+ const downIdx = raw.search(/^--\s*DOWN\s*$/im);
4150
4150
  if (downIdx < 0) {
4151
4151
  return { up: raw.trim(), down: null };
4152
4152
  }
@@ -4374,6 +4374,7 @@ var SHARED_MESSAGE_KEYS = new Set([
4374
4374
  ]);
4375
4375
 
4376
4376
  // ../sdk/dist/validate/index.js
4377
+ var CREDENTIAL_HEADERS = ["authorization", "cookie", "proxy-authorization", "x-api-key"];
4377
4378
  var SEMVER_RE = /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/;
4378
4379
  var KNOWN_CATEGORIES = new Set([
4379
4380
  "auth",
@@ -4464,13 +4465,38 @@ function validateManifest(input) {
4464
4465
  }
4465
4466
  }
4466
4467
  if (obj.apiKeyRoutes !== undefined) {
4467
- 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
+ }
4468
4472
  for (const r of routes) {
4469
4473
  if (typeof r !== "string" || !/^(GET|POST|PUT|PATCH|DELETE) \S+$/.test(r)) {
4470
4474
  out.push(err("MANIFEST_BAD_API_KEY_ROUTE", `apiKeyRoutes entry ${JSON.stringify(r)} must be "<GET|POST|PUT|PATCH|DELETE> <pattern>"`));
4471
4475
  }
4472
4476
  }
4473
4477
  }
4478
+ if (obj.forwardCredentials !== undefined) {
4479
+ const fwd = obj.forwardCredentials;
4480
+ const publicRoutes = Array.isArray(obj.publicRoutes) ? obj.publicRoutes : [];
4481
+ if (!fwd || typeof fwd !== "object" || Array.isArray(fwd)) {
4482
+ out.push(err("MANIFEST_BAD_FORWARD_CREDENTIALS", '"forwardCredentials" must map a route to headers'));
4483
+ } else {
4484
+ for (const [route, names] of Object.entries(fwd)) {
4485
+ if (!publicRoutes.includes(route)) {
4486
+ out.push(err("MANIFEST_BAD_FORWARD_CREDENTIALS", `forwardCredentials names "${route}", which is not a publicRoutes entry`));
4487
+ }
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) {
4493
+ if (typeof n !== "string" || !CREDENTIAL_HEADERS.includes(n)) {
4494
+ out.push(err("MANIFEST_BAD_FORWARD_CREDENTIALS", `forwardCredentials "${route}": ${JSON.stringify(n)} is not one of ${CREDENTIAL_HEADERS.join(", ")}`));
4495
+ }
4496
+ }
4497
+ }
4498
+ }
4499
+ }
4474
4500
  if (typeof obj.category === "string" && !KNOWN_CATEGORIES.has(obj.category)) {
4475
4501
  out.push(warn("MANIFEST_UNKNOWN_CATEGORY", `category "${obj.category}" isn't in the known set \u2014 typo? Known: ${[...KNOWN_CATEGORIES].sort().join(", ")}`));
4476
4502
  }
@@ -6041,7 +6067,7 @@ function typesFromFiles(collection, dir, outputPath) {
6041
6067
 
6042
6068
  // src/commands/schema.ts
6043
6069
  import { existsSync as existsSync16, mkdirSync as mkdirSync6, readdirSync as readdirSync7, readFileSync as readFileSync8, rmSync, writeFileSync as writeFileSync6 } from "fs";
6044
- import { dirname as dirname5, join as join15 } from "path";
6070
+ import { dirname as dirname5, join as join15, resolve as resolve4, sep } from "path";
6045
6071
  import { createInterface as createInterface2 } from "readline";
6046
6072
  async function schemaPullCommand(opts) {
6047
6073
  const dir = opts.dir || "./schema";
@@ -6057,8 +6083,17 @@ async function schemaPullCommand(opts) {
6057
6083
  process.exit(1);
6058
6084
  }
6059
6085
  const { files } = await res.json();
6060
- for (const [path, content] of Object.entries(files)) {
6061
- 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) {
6062
6097
  mkdirSync6(dirname5(target), { recursive: true });
6063
6098
  writeFileSync6(target, content);
6064
6099
  }
@@ -6090,18 +6125,24 @@ function readSchemaDir(dir) {
6090
6125
  }
6091
6126
  return files;
6092
6127
  }
6093
- async function postSchema(engineUrl, route, files, allowDestructive = false) {
6128
+ async function postSchema(engineUrl, route, files, allowDestructive = false, adoptRules = false) {
6094
6129
  const res = await fetch(`${engineUrl}/api/admin/schema/${route}`, {
6095
6130
  method: "POST",
6096
6131
  headers: {
6097
6132
  Authorization: `Bearer ${process.env.ZVELTIO_API_KEY || ""}`,
6098
6133
  "Content-Type": "application/json"
6099
6134
  },
6100
- body: JSON.stringify({ files, allowDestructive })
6135
+ body: JSON.stringify({ files, allowDestructive, ...adoptRules ? { adopt_rules: true } : {} })
6101
6136
  });
6102
6137
  if (!res.ok) {
6103
6138
  const body = await res.json().catch(() => null);
6104
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
+ }
6105
6146
  if (res.status === 401 || res.status === 403) {
6106
6147
  console.error(" Set ZVELTIO_API_KEY to a key of an instance admin.");
6107
6148
  }
@@ -6146,15 +6187,15 @@ The plan has destructive steps; nothing was changed. Rerun with --allow-destruct
6146
6187
  }
6147
6188
  if (!opts.yes) {
6148
6189
  const rl2 = createInterface2({ input: process.stdin, output: process.stdout });
6149
- const answer = await new Promise((resolve4) => rl2.question(`
6150
- 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));
6151
6192
  rl2.close();
6152
6193
  if (answer.trim().toLowerCase() !== "yes") {
6153
6194
  console.log("Cancelled; nothing was changed.");
6154
6195
  return;
6155
6196
  }
6156
6197
  }
6157
- const applied = await postSchema(engineUrl, "apply", files, opts.allowDestructive);
6198
+ const applied = await postSchema(engineUrl, "apply", files, opts.allowDestructive, opts.adoptRules);
6158
6199
  console.log(`Applied ${applied.length} change(s).`);
6159
6200
  }
6160
6201
 
@@ -6171,7 +6212,7 @@ async function installCommand(name, opts) {
6171
6212
  await installFromPath(name, opts.path, engineUrl, opts.force ?? false);
6172
6213
  return;
6173
6214
  }
6174
- await installFromCatalog(name, engineUrl);
6215
+ await installFromCatalog(name, engineUrl, opts.approveForwardCredentials ?? false);
6175
6216
  }
6176
6217
  async function installFromPath(name, sourcePath, engineUrl, force) {
6177
6218
  if (!existsSync17(join16(sourcePath, "manifest.json"))) {
@@ -6233,7 +6274,7 @@ async function installFromPath(name, sourcePath, engineUrl, force) {
6233
6274
  console.log(` ZVELTIO_EXTENSIONS=...,${category}/${extName}
6234
6275
  `);
6235
6276
  }
6236
- async function installFromCatalog(name, engineUrl) {
6277
+ async function installFromCatalog(name, engineUrl, approveForward) {
6237
6278
  console.log(` Checking engine catalog at ${engineUrl}...`);
6238
6279
  let catalogRes;
6239
6280
  try {
@@ -6284,11 +6325,30 @@ Available extensions:`);
6284
6325
  \u2705 Extension "${entry.displayName || entry.name}" is already active.`);
6285
6326
  return;
6286
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;
6287
6331
  if (entry.is_installed) {
6288
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
+ }
6289
6345
  } else {
6290
6346
  const installRes = await fetch(`${engineUrl}/api/marketplace/${entry.name}/install`, {
6291
- method: "POST"
6347
+ method: "POST",
6348
+ ...approval && {
6349
+ headers: { "Content-Type": "application/json" },
6350
+ body: JSON.stringify(approval)
6351
+ }
6292
6352
  }).catch(() => null);
6293
6353
  if (!installRes?.ok) {
6294
6354
  const err2 = await installRes?.json().catch(() => ({}));
@@ -6302,6 +6362,11 @@ Available extensions:`);
6302
6362
  console.log(` \u2713 Marked as installed`);
6303
6363
  }
6304
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
+ }
6305
6370
  const enableRes = await fetch(`${engineUrl}/api/marketplace/${entry.name}/enable`, {
6306
6371
  method: "POST"
6307
6372
  }).catch(() => null);
@@ -6345,12 +6410,12 @@ async function activateViaApi(name, category, engineUrl) {
6345
6410
  // src/commands/create-god.ts
6346
6411
  import { createInterface as createInterface3 } from "readline";
6347
6412
  async function prompt(rl2, question) {
6348
- return new Promise((resolve4) => {
6349
- rl2.question(question, resolve4);
6413
+ return new Promise((resolve5) => {
6414
+ rl2.question(question, resolve5);
6350
6415
  });
6351
6416
  }
6352
6417
  async function promptHidden(question) {
6353
- return new Promise((resolve4) => {
6418
+ return new Promise((resolve5) => {
6354
6419
  process.stdout.write(question);
6355
6420
  const stdin = process.stdin;
6356
6421
  const wasPaused = stdin.isPaused();
@@ -6369,7 +6434,7 @@ async function promptHidden(question) {
6369
6434
  stdin.off("data", handler);
6370
6435
  process.stdout.write(`
6371
6436
  `);
6372
- resolve4(value);
6437
+ resolve5(value);
6373
6438
  } else if (char === "\x7F") {
6374
6439
  if (value.length > 0)
6375
6440
  value = value.slice(0, -1);
@@ -6622,9 +6687,9 @@ var rollbackCommand = new Command("rollback").description("Rollback database mig
6622
6687
  input: process.stdin,
6623
6688
  output: process.stdout
6624
6689
  });
6625
- const answer = await new Promise((resolve4) => {
6690
+ const answer = await new Promise((resolve5) => {
6626
6691
  rl2.question(`
6627
- \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);
6628
6693
  });
6629
6694
  rl2.close();
6630
6695
  if (answer.trim().toLowerCase() !== "yes") {
@@ -6941,8 +7006,8 @@ function compareVersions(a, b) {
6941
7006
  return 0;
6942
7007
  }
6943
7008
  async function readLine() {
6944
- return new Promise((resolve4) => {
6945
- process.stdin.once("data", (data) => resolve4(data.toString().trim()));
7009
+ return new Promise((resolve5) => {
7010
+ process.stdin.once("data", (data) => resolve5(data.toString().trim()));
6946
7011
  });
6947
7012
  }
6948
7013
 
@@ -7074,7 +7139,7 @@ async function getLocalVersion() {
7074
7139
  // package.json
7075
7140
  var package_default = {
7076
7141
  name: "@zveltio/cli",
7077
- version: "3.0.0-beta.78",
7142
+ version: "3.0.0-beta.80",
7078
7143
  type: "module",
7079
7144
  bin: {
7080
7145
  zveltio: "./dist/index.js"
@@ -7089,7 +7154,7 @@ var package_default = {
7089
7154
  dev: "bun run --watch src/index.ts"
7090
7155
  },
7091
7156
  dependencies: {
7092
- "@zveltio/sdk": "3.0.0-beta.78",
7157
+ "@zveltio/sdk": "3.0.0-beta.80",
7093
7158
  commander: "^12.1.0"
7094
7159
  },
7095
7160
  devDependencies: {
@@ -7121,11 +7186,11 @@ program2.command("generate-types [collection]").description("Generate TypeScript
7121
7186
  var schema = program2.command("schema").description("Schema as code: the live schema as reviewable JSON files");
7122
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);
7123
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);
7124
- 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);
7125
- 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));
7126
7191
  var extensions = program2.command("extensions").description("Manage Zveltio extensions");
7127
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));
7128
- 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));
7129
7194
  extensions.command("enable <name>").description("Enable an installed extension").option("--url <url>", "Engine URL", "http://localhost:3000").action(async (name, opts) => {
7130
7195
  const url = opts.url || "http://localhost:3000";
7131
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.78",
3
+ "version": "3.0.0-beta.80",
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.78",
18
+ "@zveltio/sdk": "3.0.0-beta.80",
19
19
  "commander": "^12.1.0"
20
20
  },
21
21
  "devDependencies": {