@supacloud/cli 0.14.2 → 0.14.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.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +90 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -68,8 +68,20 @@ supacloud-cli frontend list --ref abc123
68
68
  supacloud-cli branch create --name feature-orders --data_mode schema_only
69
69
  supacloud-cli branch promotion_plan --branch_ref preview123
70
70
  supacloud-cli branch promote --branch_ref preview123 --plan_checksum <sha256>
71
+ supacloud-cli edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello
72
+ supacloud-cli edge_functions deploy_bundle --ref abc123 --slug hello --files '{"index.ts":"export default { fetch: () => new Response(\"ok\") }"}'
73
+ supacloud-cli edge_functions source --ref abc123 --slug hello --output ./hello.ts
71
74
  ```
72
75
 
76
+ `edge_functions deploy --path` bundles local TypeScript and dependencies with
77
+ Bun and runs a local syntax check before upload. The Management API validates and
78
+ normalizes the final server-side artifact against the multi-tenant Edge Runtime
79
+ module policy consistently for CLI, Web Console, and direct API deployments.
80
+ `deploy_bundle --files` accepts a JSON object in shell usage.
81
+ Use `source --output <file>` for large Functions so terminal or automation output
82
+ limits cannot truncate the original TS/JS source code. The destination must not
83
+ already exist.
84
+
73
85
  Branch promotion is migration-first. `branch promotion_plan` prints pending
74
86
  versions, names, statement counts, and checksums without echoing SQL into terminal
75
87
  logs; review the migration files or the Web Console SQL view before approval.
package/dist/index.js CHANGED
@@ -6155,15 +6155,18 @@ async function runCli(cliTools, args, options = {}) {
6155
6155
  console.error(`❌ Unknown command: ${toolName}`);
6156
6156
  console.error(`Available commands:
6157
6157
  ${formatAvailableCommands()}`);
6158
- process.exit(1);
6158
+ process.exitCode = 1;
6159
+ return;
6159
6160
  }
6160
6161
  if (args.length === 1 || args[1] === "--help" || args[1] === "-h") {
6161
6162
  console.error(formatToolHelp(toolName, tool));
6162
- process.exit(0);
6163
+ process.exitCode = 0;
6164
+ return;
6163
6165
  }
6164
6166
  if (args.length > 2 && !args[1].startsWith("--") && (args[2] === "--help" || args[2] === "-h")) {
6165
6167
  console.error(formatActionHelp(toolName, args[1], tool));
6166
- process.exit(0);
6168
+ process.exitCode = 0;
6169
+ return;
6167
6170
  }
6168
6171
  const parsedArgs = {};
6169
6172
  let startIdx = 1;
@@ -6198,14 +6201,14 @@ async function runCli(cliTools, args, options = {}) {
6198
6201
  } else {
6199
6202
  console.log(JSON.stringify(result, null, 2));
6200
6203
  }
6201
- process.exit(cliToolResultIsError(result) ? 1 : 0);
6204
+ process.exitCode = cliToolResultIsError(result) ? 1 : 0;
6202
6205
  } catch (error) {
6203
6206
  const message = error instanceof Error ? error.message : String(error);
6204
6207
  console.error(`❌ Error: ${message}`);
6205
6208
  if (message.includes("required")) {
6206
6209
  console.error(`Hint: Pass arguments like --ref YOUR_REF`);
6207
6210
  }
6208
- process.exit(1);
6211
+ process.exitCode = 1;
6209
6212
  }
6210
6213
  }
6211
6214
 
@@ -7524,6 +7527,19 @@ function parseBackgroundRoutes(value) {
7524
7527
  }
7525
7528
  }
7526
7529
  var backgroundRoutesSchema = Type.Optional(decodedSchema(Type.Union([Type.String(), Type.Array(Type.String())]), Type.Array(Type.String()), parseBackgroundRoutes));
7530
+ var functionFilesRecordSchema = Type.Record(Type.String(), Type.String());
7531
+ function parseFunctionFiles(input) {
7532
+ if (typeof input !== "string")
7533
+ return input;
7534
+ try {
7535
+ return JSON.parse(input);
7536
+ } catch (error) {
7537
+ if (!(error instanceof SyntaxError))
7538
+ throw error;
7539
+ throw new Error("Invalid files JSON object");
7540
+ }
7541
+ }
7542
+ var functionFilesSchema = decodedSchema(Type.Union([Type.String(), functionFilesRecordSchema]), functionFilesRecordSchema, parseFunctionFiles);
7527
7543
  var secretListSchema = Type.Array(Type.Object({ name: Type.String(), value: Type.String() }));
7528
7544
  function parseSecrets(value) {
7529
7545
  if (Array.isArray(value))
@@ -7551,6 +7567,26 @@ function parseSecrets(value) {
7551
7567
  }).filter((entry) => entry.name);
7552
7568
  }
7553
7569
  var secretsSchema = Type.Optional(decodedSchema(Type.Union([Type.String(), secretListSchema]), secretListSchema, parseSecrets));
7570
+ function confirmedFunctionConfig(payload, expected) {
7571
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
7572
+ return false;
7573
+ const response = payload;
7574
+ if (expected.verify_jwt !== undefined && response.verify_jwt !== expected.verify_jwt)
7575
+ return false;
7576
+ if (expected.background_routes !== undefined) {
7577
+ if (!Array.isArray(response.background_routes))
7578
+ return false;
7579
+ if (JSON.stringify(response.background_routes) !== JSON.stringify(expected.background_routes))
7580
+ return false;
7581
+ }
7582
+ return true;
7583
+ }
7584
+ function functionSourceCode(payload) {
7585
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
7586
+ return null;
7587
+ const code = payload.code;
7588
+ return typeof code === "string" ? code : null;
7589
+ }
7554
7590
  function registerAdvancedTools(server, http) {
7555
7591
  server.tool("edge_functions", `Edge Function management (Deno/Bun serverless). Server auto-bundles dependencies.
7556
7592
  Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
@@ -7559,13 +7595,14 @@ Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
7559
7595
  slug: optional(Type.String(), "[deploy/deploy_bundle/config/source/delete/check] Function name"),
7560
7596
  code: optional(Type.String(), "[deploy/check] Function source code (TypeScript)"),
7561
7597
  path: optional(Type.String(), "[deploy/check] Local file path to read code from (alternative to code)"),
7562
- files: optional(Type.Record(Type.String(), Type.String()), "[deploy_bundle] File map: { 'index.ts': '...', '_shared/x.ts': '...' }"),
7598
+ output: optional(Type.String(), "[source] Write source to this local file instead of stdout; the file must not already exist"),
7599
+ files: optional(functionFilesSchema, "[deploy_bundle] File map as a JSON object: { 'index.ts': '...', '_shared/x.ts': '...' }"),
7563
7600
  entrypoint: optional(Type.String(), "[deploy_bundle] Entrypoint file (default: index.ts)"),
7564
7601
  minify: optional(Type.Boolean(), "[deploy/deploy_bundle] Minify bundle"),
7565
7602
  verify_jwt: optional(Type.Boolean(), "[deploy/deploy_bundle/config] Set JWT verification for this function"),
7566
7603
  background_routes: withDescription(backgroundRoutesSchema, "[deploy/deploy_bundle/config] Background route paths; pass comma-separated or JSON array in CLI")
7567
7604
  }, async (args) => {
7568
- const { action, ref, slug, path: pathArg, files, entrypoint, minify, verify_jwt, background_routes } = args;
7605
+ const { action, ref, slug, path: pathArg, output, files, entrypoint, minify, verify_jwt, background_routes } = args;
7569
7606
  let code = args.code;
7570
7607
  const need = (f, v) => {
7571
7608
  if (!v)
@@ -7585,6 +7622,17 @@ Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
7585
7622
  const cr = await http.patch(`/v1/projects/${ref}/functions/${slug}/config`, functionConfig());
7586
7623
  return cr.ok ? `✅ Function ${slug} config updated
7587
7624
  ${JSON.stringify(cr.data, null, 2)}` : `❌ Config update failed (${cr.status}): ${JSON.stringify(cr.data)}`;
7625
+ };
7626
+ const confirmedDeploymentText = async (successText, responsePayload) => {
7627
+ if (!hasFunctionConfig() || confirmedFunctionConfig(responsePayload, functionConfig())) {
7628
+ return successText;
7629
+ }
7630
+ const fallback = await http.patch(`/v1/projects/${ref}/functions/${slug}/config`, functionConfig());
7631
+ if (!fallback.ok || !confirmedFunctionConfig(fallback.data, functionConfig())) {
7632
+ return `❌ Partial deployment (unsafe): POST succeeded and the code/bundle was deployed, but the function policy was not confirmed; legacy PATCH fallback failed (${fallback.status}): ${JSON.stringify(fallback.data)}`;
7633
+ }
7634
+ return `${successText}
7635
+ ⚠️ Legacy non-atomic compatibility path: policy applied with follow-up PATCH`;
7588
7636
  };
7589
7637
  const checkSyntax = async (sourceCode) => {
7590
7638
  const tmpDir = mkdtempSync(join2(tmpdir(), "supacloud-edge-check-"));
@@ -7603,8 +7651,9 @@ ${e.stderr || e.message}` };
7603
7651
  if (pathArg && !code) {
7604
7652
  try {
7605
7653
  code = await bundleEdgeFunctionPath(pathArg);
7606
- } catch (e) {
7607
- throw new Error(`Failed to bundle/read path ${pathArg}: ${e.message}`);
7654
+ } catch (error) {
7655
+ const message = error instanceof Error ? error.message : String(error);
7656
+ throw new Error(`Failed to bundle/read path ${pathArg}: ${message}`);
7608
7657
  }
7609
7658
  }
7610
7659
  switch (action) {
@@ -7630,24 +7679,31 @@ ${checkRes.err}`;
7630
7679
  ${deployCheck.err}`;
7631
7680
  break;
7632
7681
  }
7633
- const dr = await http.post(`/v1/projects/${ref}/functions/${slug}`, { code, minify });
7682
+ const dr = await http.post(`/v1/projects/${ref}/functions/${slug}`, {
7683
+ code,
7684
+ minify,
7685
+ ...functionConfig()
7686
+ });
7634
7687
  if (!dr.ok) {
7635
7688
  text = `❌ Failed (${dr.status}): ${JSON.stringify(dr.data)}`;
7636
7689
  break;
7637
7690
  }
7638
- text = hasFunctionConfig() ? `✅ Function ${slug} deployed
7639
- ${await updateFunctionConfig()}` : `✅ Function ${slug} deployed`;
7691
+ text = await confirmedDeploymentText(`✅ Function ${slug} deployed`, dr.data);
7640
7692
  break;
7641
7693
  case "deploy_bundle":
7642
7694
  need("slug", slug);
7643
7695
  need("files", files);
7644
- const br = await http.post(`/v1/projects/${ref}/functions/${slug}/bundle`, { files, entrypoint, minify });
7696
+ const br = await http.post(`/v1/projects/${ref}/functions/${slug}/bundle`, {
7697
+ files,
7698
+ entrypoint,
7699
+ minify,
7700
+ ...functionConfig()
7701
+ });
7645
7702
  if (!br.ok) {
7646
7703
  text = `❌ Failed (${br.status}): ${JSON.stringify(br.data)}`;
7647
7704
  break;
7648
7705
  }
7649
- text = hasFunctionConfig() ? `✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)
7650
- ${await updateFunctionConfig()}` : `✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)`;
7706
+ text = await confirmedDeploymentText(`✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)`, br.data);
7651
7707
  break;
7652
7708
  case "config":
7653
7709
  text = await updateFunctionConfig();
@@ -7655,7 +7711,22 @@ ${await updateFunctionConfig()}` : `✅ Function ${slug} bundle deployed (${Obje
7655
7711
  case "source":
7656
7712
  need("slug", slug);
7657
7713
  const sr = await http.get(`/v1/projects/${ref}/functions/${slug}/source`);
7658
- text = sr.ok ? JSON.stringify(sr.data, null, 2) : `❌ Not found (${sr.status})`;
7714
+ if (!sr.ok) {
7715
+ text = `❌ Not found (${sr.status})`;
7716
+ break;
7717
+ }
7718
+ if (!output) {
7719
+ text = JSON.stringify(sr.data, null, 2);
7720
+ break;
7721
+ }
7722
+ const sourceCode = functionSourceCode(sr.data);
7723
+ if (sourceCode === null) {
7724
+ text = "❌ Source response did not contain a string code field";
7725
+ break;
7726
+ }
7727
+ const outputPath = resolve2(output);
7728
+ writeFileSync(outputPath, sourceCode, { flag: "wx" });
7729
+ text = `✅ Function ${slug} source written to ${outputPath} (${Buffer.byteLength(sourceCode)} bytes)`;
7659
7730
  break;
7660
7731
  case "delete":
7661
7732
  need("slug", slug);
@@ -9840,7 +9911,8 @@ async function main() {
9840
9911
  const args = process.argv.slice(2);
9841
9912
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
9842
9913
  printHelp();
9843
- process.exit(0);
9914
+ process.exitCode = 0;
9915
+ return;
9844
9916
  }
9845
9917
  const cliTools = createCliTools();
9846
9918
  if (args.length === 1 && !["ai", "supabase"].includes(args[0]) && cliTools[args[0]]) {
@@ -9862,5 +9934,5 @@ async function main() {
9862
9934
  }
9863
9935
  main().catch((error) => {
9864
9936
  console.error(`${commandName} failed:`, error);
9865
- process.exit(1);
9937
+ process.exitCode = 1;
9866
9938
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",