@supacloud/cli 0.14.1 → 0.14.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.
Files changed (2) hide show
  1. package/dist/index.js +67 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6335,29 +6335,46 @@ function resolveSupaCloudContext(env = process.env, cwd = process.cwd()) {
6335
6335
  var DEFAULT_TIMEOUT = 30000;
6336
6336
  var MAX_RETRIES = 2;
6337
6337
  var RETRY_BASE_DELAY = 500;
6338
- async function fetchWithRetry(url, options, retries = MAX_RETRIES) {
6338
+ function isRetryableMethod(method) {
6339
+ const normalizedMethod = (method ?? "GET").toUpperCase();
6340
+ return normalizedMethod === "GET" || normalizedMethod === "HEAD";
6341
+ }
6342
+ function isRetryableError(error) {
6343
+ if (!(error instanceof Error))
6344
+ return false;
6345
+ const networkError = error;
6346
+ return networkError.name === "AbortError" || networkError.code === "ECONNREFUSED" || networkError.code === "ECONNRESET";
6347
+ }
6348
+ async function fetchWithTimeout(url, options) {
6349
+ const controller = new AbortController;
6350
+ const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT);
6351
+ try {
6352
+ return await fetch(url, {
6353
+ ...options,
6354
+ signal: controller.signal
6355
+ });
6356
+ } finally {
6357
+ clearTimeout(timeout);
6358
+ }
6359
+ }
6360
+ async function fetchWithRetry(url, options) {
6361
+ const retries = isRetryableMethod(options.method) ? MAX_RETRIES : 0;
6339
6362
  for (let attempt = 0;attempt <= retries; attempt++) {
6340
6363
  try {
6341
- const controller = new AbortController;
6342
- const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT);
6343
- const res = await fetch(url, {
6344
- ...options,
6345
- signal: controller.signal
6346
- });
6347
- clearTimeout(timeout);
6348
- if (res.status >= 500 && attempt < retries) {
6364
+ const res = await fetchWithTimeout(url, options);
6365
+ if (res.status >= 500 && res.status < 600 && attempt < retries) {
6349
6366
  const delay = RETRY_BASE_DELAY * Math.pow(2, attempt);
6350
6367
  await new Promise((r) => setTimeout(r, delay));
6351
6368
  continue;
6352
6369
  }
6353
6370
  return res;
6354
- } catch (err) {
6355
- if (attempt < retries && (err.name === "AbortError" || err.code === "ECONNREFUSED" || err.code === "ECONNRESET")) {
6371
+ } catch (error) {
6372
+ if (attempt < retries && isRetryableError(error)) {
6356
6373
  const delay = RETRY_BASE_DELAY * Math.pow(2, attempt);
6357
6374
  await new Promise((r) => setTimeout(r, delay));
6358
6375
  continue;
6359
6376
  }
6360
- throw err;
6377
+ throw error;
6361
6378
  }
6362
6379
  }
6363
6380
  throw new Error("Unreachable");
@@ -7534,6 +7551,20 @@ function parseSecrets(value) {
7534
7551
  }).filter((entry) => entry.name);
7535
7552
  }
7536
7553
  var secretsSchema = Type.Optional(decodedSchema(Type.Union([Type.String(), secretListSchema]), secretListSchema, parseSecrets));
7554
+ function confirmedFunctionConfig(payload, expected) {
7555
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
7556
+ return false;
7557
+ const response = payload;
7558
+ if (expected.verify_jwt !== undefined && response.verify_jwt !== expected.verify_jwt)
7559
+ return false;
7560
+ if (expected.background_routes !== undefined) {
7561
+ if (!Array.isArray(response.background_routes))
7562
+ return false;
7563
+ if (JSON.stringify(response.background_routes) !== JSON.stringify(expected.background_routes))
7564
+ return false;
7565
+ }
7566
+ return true;
7567
+ }
7537
7568
  function registerAdvancedTools(server, http) {
7538
7569
  server.tool("edge_functions", `Edge Function management (Deno/Bun serverless). Server auto-bundles dependencies.
7539
7570
  Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
@@ -7568,6 +7599,17 @@ Actions: list, deploy, deploy_bundle, config, source, delete, check`, {
7568
7599
  const cr = await http.patch(`/v1/projects/${ref}/functions/${slug}/config`, functionConfig());
7569
7600
  return cr.ok ? `✅ Function ${slug} config updated
7570
7601
  ${JSON.stringify(cr.data, null, 2)}` : `❌ Config update failed (${cr.status}): ${JSON.stringify(cr.data)}`;
7602
+ };
7603
+ const confirmedDeploymentText = async (successText, responsePayload) => {
7604
+ if (!hasFunctionConfig() || confirmedFunctionConfig(responsePayload, functionConfig())) {
7605
+ return successText;
7606
+ }
7607
+ const fallback = await http.patch(`/v1/projects/${ref}/functions/${slug}/config`, functionConfig());
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`;
7571
7613
  };
7572
7614
  const checkSyntax = async (sourceCode) => {
7573
7615
  const tmpDir = mkdtempSync(join2(tmpdir(), "supacloud-edge-check-"));
@@ -7613,24 +7655,31 @@ ${checkRes.err}`;
7613
7655
  ${deployCheck.err}`;
7614
7656
  break;
7615
7657
  }
7616
- const dr = await http.post(`/v1/projects/${ref}/functions/${slug}`, { code, minify });
7658
+ const dr = await http.post(`/v1/projects/${ref}/functions/${slug}`, {
7659
+ code,
7660
+ minify,
7661
+ ...functionConfig()
7662
+ });
7617
7663
  if (!dr.ok) {
7618
7664
  text = `❌ Failed (${dr.status}): ${JSON.stringify(dr.data)}`;
7619
7665
  break;
7620
7666
  }
7621
- text = hasFunctionConfig() ? `✅ Function ${slug} deployed
7622
- ${await updateFunctionConfig()}` : `✅ Function ${slug} deployed`;
7667
+ text = await confirmedDeploymentText(`✅ Function ${slug} deployed`, dr.data);
7623
7668
  break;
7624
7669
  case "deploy_bundle":
7625
7670
  need("slug", slug);
7626
7671
  need("files", files);
7627
- const br = await http.post(`/v1/projects/${ref}/functions/${slug}/bundle`, { files, entrypoint, minify });
7672
+ const br = await http.post(`/v1/projects/${ref}/functions/${slug}/bundle`, {
7673
+ files,
7674
+ entrypoint,
7675
+ minify,
7676
+ ...functionConfig()
7677
+ });
7628
7678
  if (!br.ok) {
7629
7679
  text = `❌ Failed (${br.status}): ${JSON.stringify(br.data)}`;
7630
7680
  break;
7631
7681
  }
7632
- text = hasFunctionConfig() ? `✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)
7633
- ${await updateFunctionConfig()}` : `✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)`;
7682
+ text = await confirmedDeploymentText(`✅ Function ${slug} bundle deployed (${Object.keys(files).length} files)`, br.data);
7634
7683
  break;
7635
7684
  case "config":
7636
7685
  text = await updateFunctionConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.14.1",
3
+ "version": "0.14.3",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",