@supacloud/cli 0.14.3 → 0.14.5
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/README.md +12 -0
- package/dist/index.js +56 -21
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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))
|
|
@@ -7565,6 +7581,12 @@ function confirmedFunctionConfig(payload, expected) {
|
|
|
7565
7581
|
}
|
|
7566
7582
|
return true;
|
|
7567
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
|
+
}
|
|
7568
7590
|
function registerAdvancedTools(server, http) {
|
|
7569
7591
|
server.tool("edge_functions", `Edge Function management (Deno/Bun serverless). Server auto-bundles dependencies.
|
|
7570
7592
|
Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
|
|
@@ -7573,13 +7595,14 @@ Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
|
|
|
7573
7595
|
slug: optional(Type.String(), "[deploy/deploy_bundle/config/source/delete/check] Function name"),
|
|
7574
7596
|
code: optional(Type.String(), "[deploy/check] Function source code (TypeScript)"),
|
|
7575
7597
|
path: optional(Type.String(), "[deploy/check] Local file path to read code from (alternative to code)"),
|
|
7576
|
-
|
|
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': '...' }"),
|
|
7577
7600
|
entrypoint: optional(Type.String(), "[deploy_bundle] Entrypoint file (default: index.ts)"),
|
|
7578
7601
|
minify: optional(Type.Boolean(), "[deploy/deploy_bundle] Minify bundle"),
|
|
7579
7602
|
verify_jwt: optional(Type.Boolean(), "[deploy/deploy_bundle/config] Set JWT verification for this function"),
|
|
7580
7603
|
background_routes: withDescription(backgroundRoutesSchema, "[deploy/deploy_bundle/config] Background route paths; pass comma-separated or JSON array in CLI")
|
|
7581
7604
|
}, async (args) => {
|
|
7582
|
-
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;
|
|
7583
7606
|
let code = args.code;
|
|
7584
7607
|
const need = (f, v) => {
|
|
7585
7608
|
if (!v)
|
|
@@ -7600,16 +7623,11 @@ Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
|
|
|
7600
7623
|
return cr.ok ? `✅ Function ${slug} config updated
|
|
7601
7624
|
${JSON.stringify(cr.data, null, 2)}` : `❌ Config update failed (${cr.status}): ${JSON.stringify(cr.data)}`;
|
|
7602
7625
|
};
|
|
7603
|
-
const
|
|
7626
|
+
const deploymentPolicyReceiptText = (successText, responsePayload) => {
|
|
7604
7627
|
if (!hasFunctionConfig() || confirmedFunctionConfig(responsePayload, functionConfig())) {
|
|
7605
7628
|
return successText;
|
|
7606
7629
|
}
|
|
7607
|
-
|
|
7608
|
-
if (!fallback.ok || !confirmedFunctionConfig(fallback.data, functionConfig())) {
|
|
7609
|
-
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)}`;
|
|
7610
|
-
}
|
|
7611
|
-
return `${successText}
|
|
7612
|
-
⚠️ Legacy non-atomic compatibility path: policy applied with follow-up PATCH`;
|
|
7630
|
+
return "❌ Unsafe deployment receipt: POST succeeded but did not confirm the requested function policy. No follow-up PATCH was attempted because code and policy must be activated atomically.";
|
|
7613
7631
|
};
|
|
7614
7632
|
const checkSyntax = async (sourceCode) => {
|
|
7615
7633
|
const tmpDir = mkdtempSync(join2(tmpdir(), "supacloud-edge-check-"));
|
|
@@ -7628,8 +7646,9 @@ ${e.stderr || e.message}` };
|
|
|
7628
7646
|
if (pathArg && !code) {
|
|
7629
7647
|
try {
|
|
7630
7648
|
code = await bundleEdgeFunctionPath(pathArg);
|
|
7631
|
-
} catch (
|
|
7632
|
-
|
|
7649
|
+
} catch (error) {
|
|
7650
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7651
|
+
throw new Error(`Failed to bundle/read path ${pathArg}: ${message}`);
|
|
7633
7652
|
}
|
|
7634
7653
|
}
|
|
7635
7654
|
switch (action) {
|
|
@@ -7664,7 +7683,7 @@ ${deployCheck.err}`;
|
|
|
7664
7683
|
text = `❌ Failed (${dr.status}): ${JSON.stringify(dr.data)}`;
|
|
7665
7684
|
break;
|
|
7666
7685
|
}
|
|
7667
|
-
text =
|
|
7686
|
+
text = deploymentPolicyReceiptText(`✅ Function ${slug} deployed`, dr.data);
|
|
7668
7687
|
break;
|
|
7669
7688
|
case "deploy_bundle":
|
|
7670
7689
|
need("slug", slug);
|
|
@@ -7679,7 +7698,7 @@ ${deployCheck.err}`;
|
|
|
7679
7698
|
text = `❌ Failed (${br.status}): ${JSON.stringify(br.data)}`;
|
|
7680
7699
|
break;
|
|
7681
7700
|
}
|
|
7682
|
-
text =
|
|
7701
|
+
text = deploymentPolicyReceiptText(`✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)`, br.data);
|
|
7683
7702
|
break;
|
|
7684
7703
|
case "config":
|
|
7685
7704
|
text = await updateFunctionConfig();
|
|
@@ -7687,7 +7706,22 @@ ${deployCheck.err}`;
|
|
|
7687
7706
|
case "source":
|
|
7688
7707
|
need("slug", slug);
|
|
7689
7708
|
const sr = await http.get(`/v1/projects/${ref}/functions/${slug}/source`);
|
|
7690
|
-
|
|
7709
|
+
if (!sr.ok) {
|
|
7710
|
+
text = `❌ Not found (${sr.status})`;
|
|
7711
|
+
break;
|
|
7712
|
+
}
|
|
7713
|
+
if (!output) {
|
|
7714
|
+
text = JSON.stringify(sr.data, null, 2);
|
|
7715
|
+
break;
|
|
7716
|
+
}
|
|
7717
|
+
const sourceCode = functionSourceCode(sr.data);
|
|
7718
|
+
if (sourceCode === null) {
|
|
7719
|
+
text = "❌ Source response did not contain a string code field";
|
|
7720
|
+
break;
|
|
7721
|
+
}
|
|
7722
|
+
const outputPath = resolve2(output);
|
|
7723
|
+
writeFileSync(outputPath, sourceCode, { flag: "wx" });
|
|
7724
|
+
text = `✅ Function ${slug} source written to ${outputPath} (${Buffer.byteLength(sourceCode)} bytes)`;
|
|
7691
7725
|
break;
|
|
7692
7726
|
case "delete":
|
|
7693
7727
|
need("slug", slug);
|
|
@@ -9872,7 +9906,8 @@ async function main() {
|
|
|
9872
9906
|
const args = process.argv.slice(2);
|
|
9873
9907
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
9874
9908
|
printHelp();
|
|
9875
|
-
process.
|
|
9909
|
+
process.exitCode = 0;
|
|
9910
|
+
return;
|
|
9876
9911
|
}
|
|
9877
9912
|
const cliTools = createCliTools();
|
|
9878
9913
|
if (args.length === 1 && !["ai", "supabase"].includes(args[0]) && cliTools[args[0]]) {
|
|
@@ -9894,5 +9929,5 @@ async function main() {
|
|
|
9894
9929
|
}
|
|
9895
9930
|
main().catch((error) => {
|
|
9896
9931
|
console.error(`${commandName} failed:`, error);
|
|
9897
|
-
process.
|
|
9932
|
+
process.exitCode = 1;
|
|
9898
9933
|
});
|