@supacloud/cli 0.20.0 → 0.21.1

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 CHANGED
@@ -161,11 +161,13 @@ supacloud-cli frontend list --ref abc123
161
161
  supacloud-cli branch create --name feature-orders --data_mode schema_only
162
162
  supacloud-cli branch promotion_plan --branch_ref preview123
163
163
  supacloud-cli branch promote --branch_ref preview123 --plan_checksum <sha256>
164
- supacloud-cli edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent
165
- supacloud-cli edge_functions deploy --ref abc123 --slug hello --prebundled-path ./dist/hello.js --expected-sha256 <sha256> --expected-active-version 4
166
- supacloud-cli edge_functions deploy_bundle --ref abc123 --slug hello --files '{"index.ts":"export default { fetch: () => new Response(\"ok\") }"}' --expected-active-version 7
164
+ supacloud-cli edge_functions get_config --ref abc123 --slug hello
165
+ supacloud-cli edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent --expected-activation-id legacy
166
+ supacloud-cli edge_functions deploy --ref abc123 --slug hello --prebundled-path ./dist/hello.js --expected-sha256 <sha256> --expected-active-version 4 --expected-activation-id <uuid>
167
+ supacloud-cli edge_functions deploy_bundle --ref abc123 --slug hello --files '{"index.ts":"export default { fetch: () => new Response(\"ok\") }"}' --expected-active-version 7 --expected-activation-id <uuid>
167
168
  supacloud-cli edge_functions source --ref abc123 --slug hello --version 7 --output ./hello-v7.ts
168
- supacloud-cli edge_functions activate --ref abc123 --slug hello --version 3 --expected-active-version 8
169
+ supacloud-cli edge_functions activate --ref abc123 --slug hello --version 3 --expected-active-version 8 --expected-activation-id <uuid>
170
+ supacloud-cli edge_functions delete --ref abc123 --slug hello --expected-activation-id <uuid>
169
171
  supacloud-cli scheduled_functions list --ref abc123
170
172
  supacloud-cli secrets upsert --ref abc123 --from-env API_KEY,WEBHOOK_SECRET
171
173
  supacloud-cli storage list_buckets --ref abc123
@@ -221,10 +223,17 @@ pointer; this remains correct across an active-version A→B→A transition.
221
223
  `--expected-active-version <N|absent>`. Read the current non-negative integer
222
224
  version from `edge_functions list`; use `0` for a listed legacy Function and
223
225
  `absent` only when creating a slug that does not yet exist. A stale value returns
224
- HTTP 409 without building, preheating, or activating another version. List output remains a JSON array with string
225
- `slug` and numeric `version` fields, while source output is exactly
226
- `{ "code": "..." }`. Release automation must use `source --version <N>` for a
227
- version-bound backup.
226
+ HTTP 409 without building, preheating, or activating another version. Every
227
+ Function mutation also requires `--expected-activation-id <uuid|legacy>` from
228
+ the same `list` or `get_config` snapshot. Use `get_config` for a single atomic
229
+ read of `active_version`, `activation_id`, and policy, including an `absent`
230
+ tombstone after deletion. Use `legacy` only for a never-created slug or a listed
231
+ legacy Function; recreating a deleted slug must use the tombstone UUID returned
232
+ by `delete` or `get_config`. This second token prevents an A→B→A version cycle
233
+ from satisfying a stale mutation. List output remains a JSON array with string
234
+ `slug`, numeric `version`, and canonical `activation_id` fields, while source
235
+ output is exactly `{ "code": "..." }`. Release automation must use
236
+ `source --version <N>` for a version-bound backup.
228
237
 
229
238
  `edge_functions activate` restores an existing immutable Function version and
230
239
  returns a machine-readable receipt containing the activated version and JWT
@@ -234,10 +243,10 @@ server response body.
234
243
  Mutation receipts use schema `supacloud.cli.release-control.v1`. An
235
244
  `OUTCOME_UNKNOWN` error means the server may have committed the mutation before
236
245
  the response was lost or failed validation; read back current state before any
237
- retry. For Function deploy, bundle deploy, and activation, the CLI applies a
238
- separate 5-second, 64 KiB response-body boundary after receiving HTTP headers.
239
- A stalled, oversized, truncated, unreadable, or malformed body is always
240
- `OUTCOME_UNKNOWN` and its content is never included in CLI output.
246
+ retry. For Function deploy, bundle deploy, activation, config, and delete, the
247
+ CLI applies a separate 5-second, 64 KiB response-body boundary after receiving
248
+ HTTP headers. A stalled, oversized, truncated, unreadable, or malformed body is
249
+ always `OUTCOME_UNKNOWN` and its content is never included in CLI output.
241
250
  Version `0` is reserved as the active-version CAS token for legacy Functions. It
242
251
  can be passed only as `--expected-active-version`; immutable source reads and
243
252
  activation targets still require a positive version.
@@ -250,6 +259,8 @@ activation targets still require a positive version.
250
259
  "project_ref": "abc123",
251
260
  "slug": "hello",
252
261
  "previous_active_version": "7",
262
+ "expected_activation_id": "9dc0e8da-207f-4f25-ae74-b1de4e66784d",
263
+ "activation_id": "6417591d-c038-46e8-91ca-4c8080514144",
253
264
  "active_version": "8",
254
265
  "version": "8",
255
266
  "verify_jwt": true
package/dist/index.js CHANGED
@@ -6478,7 +6478,7 @@ var ACTION_POLICY = {
6478
6478
  write: ["create_bucket", "update_bucket", "delete_bucket", "upload_base64", "delete_file"]
6479
6479
  },
6480
6480
  edge_functions: {
6481
- read: ["list", "source"],
6481
+ read: ["list", "get_config", "source"],
6482
6482
  local: ["check"],
6483
6483
  write: ["deploy", "deploy_bundle", "config", "activate", "delete"]
6484
6484
  },
@@ -6782,10 +6782,10 @@ class HttpTransport {
6782
6782
  "Content-Type": "application/json"
6783
6783
  };
6784
6784
  }
6785
- async postWithResponseReader(path, serializedBody, responseReader) {
6785
+ async mutationWithResponseReader(method, path, serializedBody, responseReader) {
6786
6786
  try {
6787
6787
  const response = await fetchWithRetry(`${this.baseUrl}${path}`, {
6788
- method: "POST",
6788
+ method,
6789
6789
  headers: this.headers(),
6790
6790
  body: serializedBody
6791
6791
  });
@@ -6810,13 +6810,19 @@ class HttpTransport {
6810
6810
  }
6811
6811
  async post(path, body) {
6812
6812
  try {
6813
- return await this.postWithResponseReader(path, serializedRequestBody(body), responseJsonOrNull);
6813
+ return await this.mutationWithResponseReader("POST", path, serializedRequestBody(body), responseJsonOrNull);
6814
6814
  } catch (error) {
6815
6815
  return transportFailure(error);
6816
6816
  }
6817
6817
  }
6818
6818
  async postReleaseMutation(path, body) {
6819
- return this.postWithResponseReader(path, serializedRequestBody(body), releaseMutationResponseJson);
6819
+ return this.mutationWithResponseReader("POST", path, serializedRequestBody(body), releaseMutationResponseJson);
6820
+ }
6821
+ async patchReleaseMutation(path, body) {
6822
+ return this.mutationWithResponseReader("PATCH", path, serializedRequestBody(body), releaseMutationResponseJson);
6823
+ }
6824
+ async deleteReleaseMutation(path, body) {
6825
+ return this.mutationWithResponseReader("DELETE", path, serializedRequestBody(body), releaseMutationResponseJson);
6820
6826
  }
6821
6827
  async postMultipart(path, formData) {
6822
6828
  try {
@@ -6858,11 +6864,12 @@ class HttpTransport {
6858
6864
  return transportFailure(error);
6859
6865
  }
6860
6866
  }
6861
- async delete(path) {
6867
+ async delete(path, body) {
6862
6868
  try {
6863
6869
  const res = await fetchWithRetry(`${this.baseUrl}${path}`, {
6864
6870
  method: "DELETE",
6865
- headers: this.headers()
6871
+ headers: this.headers(),
6872
+ body: serializedRequestBody(body)
6866
6873
  });
6867
6874
  const data = await res.json().catch(() => null);
6868
6875
  return { ok: res.ok, status: res.status, data };
@@ -8436,6 +8443,155 @@ import { tmpdir } from "node:os";
8436
8443
  import { basename as basename2, join as join2, resolve as resolve2 } from "node:path";
8437
8444
  import { promisify } from "node:util";
8438
8445
  import { execFile } from "node:child_process";
8446
+
8447
+ // src/shared/tools/edge-function-response.ts
8448
+ var CANONICAL_VERSION_PATTERN = /^(?:0|[1-9][0-9]*)$/;
8449
+ var SAFE_SLUG_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
8450
+ var ACTIVATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
8451
+ var LEGACY_ACTIVATION_ID = "legacy";
8452
+ var LIST_STRING_FIELDS = [
8453
+ "id",
8454
+ "name",
8455
+ "status",
8456
+ "entrypoint_path",
8457
+ "created_at",
8458
+ "updated_at"
8459
+ ];
8460
+ var LIST_BOOLEAN_FIELDS = ["verify_jwt", "import_map"];
8461
+ function objectRecord(candidate) {
8462
+ return candidate !== null && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
8463
+ }
8464
+ function canonicalVersion(candidate) {
8465
+ return typeof candidate === "string" && CANONICAL_VERSION_PATTERN.test(candidate) && Number.isSafeInteger(Number(candidate));
8466
+ }
8467
+ function stringRoutes(candidate) {
8468
+ return Array.isArray(candidate) && candidate.every((route) => typeof route === "string");
8469
+ }
8470
+ function optionalTypedFields(source, fields, expectedType) {
8471
+ return fields.every((field) => source[field] === undefined || typeof source[field] === expectedType);
8472
+ }
8473
+ function validObservedFunctionActivationId(candidate) {
8474
+ return candidate === LEGACY_ACTIVATION_ID || typeof candidate === "string" && ACTIVATION_ID_PATTERN.test(candidate);
8475
+ }
8476
+ function validCommittedFunctionActivationId(candidate) {
8477
+ return typeof candidate === "string" && ACTIVATION_ID_PATTERN.test(candidate);
8478
+ }
8479
+ function projectedFunctionListEntry(candidate) {
8480
+ const functionRecord = objectRecord(candidate);
8481
+ if (!functionRecord || typeof functionRecord.slug !== "string" || !SAFE_SLUG_PATTERN.test(functionRecord.slug) || typeof functionRecord.version !== "number" || !Number.isSafeInteger(functionRecord.version) || functionRecord.version < 0 || !validObservedFunctionActivationId(functionRecord.activation_id) || !optionalTypedFields(functionRecord, LIST_STRING_FIELDS, "string") || !optionalTypedFields(functionRecord, LIST_BOOLEAN_FIELDS, "boolean") || functionRecord.background_routes !== undefined && !stringRoutes(functionRecord.background_routes))
8482
+ return null;
8483
+ const projected = {
8484
+ slug: functionRecord.slug,
8485
+ version: functionRecord.version,
8486
+ activation_id: functionRecord.activation_id
8487
+ };
8488
+ for (const field of [...LIST_STRING_FIELDS, ...LIST_BOOLEAN_FIELDS]) {
8489
+ if (functionRecord[field] !== undefined)
8490
+ projected[field] = functionRecord[field];
8491
+ }
8492
+ if (functionRecord.background_routes !== undefined) {
8493
+ projected.background_routes = functionRecord.background_routes;
8494
+ }
8495
+ return projected;
8496
+ }
8497
+ function projectedFunctionList(payload) {
8498
+ if (!Array.isArray(payload))
8499
+ return null;
8500
+ const slugs = new Set;
8501
+ const projected = [];
8502
+ for (const candidate of payload) {
8503
+ const functionRecord = projectedFunctionListEntry(candidate);
8504
+ if (!functionRecord)
8505
+ return null;
8506
+ const slug = functionRecord.slug;
8507
+ if (typeof slug !== "string" || slugs.has(slug))
8508
+ return null;
8509
+ slugs.add(slug);
8510
+ projected.push(functionRecord);
8511
+ }
8512
+ return projected;
8513
+ }
8514
+ function optionalConfigFields(response) {
8515
+ if (response.version !== undefined && !canonicalVersion(response.version))
8516
+ return null;
8517
+ if (response.import_map !== undefined && typeof response.import_map !== "string")
8518
+ return null;
8519
+ if (response.entrypoint !== undefined && typeof response.entrypoint !== "string")
8520
+ return null;
8521
+ return {
8522
+ ...response.version === undefined ? {} : { version: response.version },
8523
+ ...response.import_map === undefined ? {} : { import_map: response.import_map },
8524
+ ...response.entrypoint === undefined ? {} : { entrypoint: response.entrypoint }
8525
+ };
8526
+ }
8527
+ function coherentFunctionVersion(activeVersion, version) {
8528
+ if (activeVersion === "absent")
8529
+ return version === undefined;
8530
+ if (activeVersion === "0")
8531
+ return version === undefined || version === "0";
8532
+ return version === activeVersion;
8533
+ }
8534
+ function projectedFunctionIdentity(payload, expectedProjectRef, expectedSlug) {
8535
+ const response = objectRecord(payload);
8536
+ if (!response || response.project_ref !== expectedProjectRef || response.slug !== expectedSlug || response.active_version !== "absent" && !canonicalVersion(response.active_version) || !validObservedFunctionActivationId(response.activation_id) || typeof response.verify_jwt !== "boolean" || !stringRoutes(response.background_routes))
8537
+ return null;
8538
+ const optionalFields = optionalConfigFields(response);
8539
+ if (!optionalFields || !coherentFunctionVersion(response.active_version, optionalFields.version))
8540
+ return null;
8541
+ return {
8542
+ project_ref: expectedProjectRef,
8543
+ slug: expectedSlug,
8544
+ active_version: response.active_version,
8545
+ verify_jwt: response.verify_jwt,
8546
+ background_routes: response.background_routes,
8547
+ ...optionalFields,
8548
+ activation_id: response.activation_id
8549
+ };
8550
+ }
8551
+ function mutationIdentityMatches(response, expectation) {
8552
+ return response.success === true && response.project_ref === expectation.projectRef && response.slug === expectation.slug && response.expected_activation_id === expectation.expectedActivationId && validCommittedFunctionActivationId(response.activation_id) && response.activation_id !== expectation.expectedActivationId;
8553
+ }
8554
+ function configMatchesExpectation(response, expected) {
8555
+ if (typeof response.verify_jwt !== "boolean" || !stringRoutes(response.background_routes)) {
8556
+ return false;
8557
+ }
8558
+ if (expected.verify_jwt !== undefined && response.verify_jwt !== expected.verify_jwt)
8559
+ return false;
8560
+ return expected.background_routes === undefined || JSON.stringify(response.background_routes) === JSON.stringify(expected.background_routes);
8561
+ }
8562
+ function confirmedFunctionConfigMutation(payload, expectation) {
8563
+ const response = objectRecord(payload);
8564
+ if (!response || !mutationIdentityMatches(response, expectation) || !configMatchesExpectation(response, expectation.config))
8565
+ return null;
8566
+ const optionalFields = optionalConfigFields(response);
8567
+ if (!optionalFields)
8568
+ return null;
8569
+ return {
8570
+ project_ref: expectation.projectRef,
8571
+ slug: expectation.slug,
8572
+ expected_activation_id: expectation.expectedActivationId,
8573
+ activation_id: response.activation_id,
8574
+ verify_jwt: response.verify_jwt,
8575
+ background_routes: response.background_routes,
8576
+ ...optionalFields
8577
+ };
8578
+ }
8579
+ function confirmedFunctionDeletion(payload, expectation) {
8580
+ const response = objectRecord(payload);
8581
+ const config = objectRecord(response?.config);
8582
+ if (!response || !config || !mutationIdentityMatches(response, expectation) || response.previous_active_version !== "absent" && !canonicalVersion(response.previous_active_version) || response.active_version !== "absent" || config.version !== undefined || config.activation_id !== response.activation_id || typeof config.verify_jwt !== "boolean")
8583
+ return null;
8584
+ return {
8585
+ project_ref: expectation.projectRef,
8586
+ slug: expectation.slug,
8587
+ expected_activation_id: expectation.expectedActivationId,
8588
+ activation_id: response.activation_id,
8589
+ previous_active_version: response.previous_active_version,
8590
+ active_version: "absent"
8591
+ };
8592
+ }
8593
+
8594
+ // src/shared/tools/advanced-tools.ts
8439
8595
  var execFileAsync = promisify(execFile);
8440
8596
  var SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
8441
8597
  function openFileIdentity(descriptor) {
@@ -8633,12 +8789,22 @@ function activeFunctionVersionToken(input) {
8633
8789
  var CANONICAL_FUNCTION_VERSION_PATTERN = /^(?:0|[1-9][0-9]*)$/;
8634
8790
  var POSITIVE_FUNCTION_VERSION_PATTERN = /^[1-9][0-9]*$/;
8635
8791
  var SAFE_FUNCTION_SLUG_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
8792
+ var FUNCTION_ACTIVATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
8793
+ var LEGACY_FUNCTION_ACTIVATION_ID = "legacy";
8636
8794
  var FUNCTION_ACTIVATION_ARGUMENTS = new Set([
8637
8795
  "action",
8638
8796
  "ref",
8639
8797
  "slug",
8640
8798
  "version",
8641
- "expected-active-version"
8799
+ "expected-active-version",
8800
+ "expected-activation-id"
8801
+ ]);
8802
+ var FUNCTION_IDENTITY_MUTATIONS = new Set([
8803
+ "deploy",
8804
+ "deploy_bundle",
8805
+ "config",
8806
+ "activate",
8807
+ "delete"
8642
8808
  ]);
8643
8809
  var functionVersionSchema = Type.Optional(decodedSchema(Type.Union([
8644
8810
  Type.Integer({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER }),
@@ -8657,6 +8823,10 @@ var expectedActiveVersionSchema = Type.Optional(decodedSchema(Type.Union([
8657
8823
  Type.Literal("absent"),
8658
8824
  Type.String({ pattern: CANONICAL_FUNCTION_VERSION_PATTERN.source, maxLength: 16 })
8659
8825
  ]), parseExpectedActiveVersion));
8826
+ var expectedActivationIdSchema = Type.Optional(Type.Union([
8827
+ Type.Literal(LEGACY_FUNCTION_ACTIVATION_ID),
8828
+ Type.String({ pattern: FUNCTION_ACTIVATION_ID_PATTERN.source, minLength: 36, maxLength: 36 })
8829
+ ]));
8660
8830
  var secretListSchema = Type.Array(Type.Object({ name: Type.String(), value: Type.String() }));
8661
8831
  var ENVIRONMENT_SECRET_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]{0,255}$/;
8662
8832
  var MAX_SECRET_COUNT = 1024;
@@ -8779,28 +8949,14 @@ var INVALID_FUNCTION_SOURCE_RESPONSE = "❌ Edge Function source response is inv
8779
8949
  function invalidFunctionReadResponse(message) {
8780
8950
  return { isError: true, content: [{ type: "text", text: message }] };
8781
8951
  }
8782
- function safeFunctionList(payload) {
8783
- if (!Array.isArray(payload))
8784
- return null;
8785
- const functionSlugs = new Set;
8786
- for (const candidate of payload) {
8787
- const edgeFunction = objectRecord(candidate);
8788
- const slug = edgeFunction?.slug;
8789
- const version = edgeFunction?.version;
8790
- if (typeof slug !== "string" || !SAFE_FUNCTION_SLUG_PATTERN.test(slug) || typeof version !== "number" || !Number.isSafeInteger(version) || version < 0 || functionSlugs.has(slug))
8791
- return null;
8792
- functionSlugs.add(slug);
8793
- }
8794
- return payload;
8795
- }
8796
8952
  function functionListResponse(response) {
8797
8953
  if (!response.ok)
8798
8954
  return invalidFunctionReadResponse(`❌ Failed (${response.status})`);
8799
- const functions = safeFunctionList(response.data);
8955
+ const functions = projectedFunctionList(response.data);
8800
8956
  return functions ? { content: [{ type: "text", text: JSON.stringify(functions, null, 2) }] } : invalidFunctionReadResponse(INVALID_FUNCTION_LIST_RESPONSE);
8801
8957
  }
8802
8958
  function confirmedFunctionConfig(payload, expected) {
8803
- const response = objectRecord(payload);
8959
+ const response = objectRecord2(payload);
8804
8960
  if (!response)
8805
8961
  return false;
8806
8962
  if (expected.verify_jwt !== undefined && response.verify_jwt !== expected.verify_jwt)
@@ -8835,7 +8991,7 @@ function functionSourceOutput(slug, sourceCode, output) {
8835
8991
  }]
8836
8992
  };
8837
8993
  }
8838
- function objectRecord(candidate) {
8994
+ function objectRecord2(candidate) {
8839
8995
  return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
8840
8996
  }
8841
8997
  function edgeFunctionResourcePath(ref, slug) {
@@ -8857,8 +9013,31 @@ async function readFunctionSource(http, request) {
8857
9013
  const sourceCode = functionSourceCode(response.data, sourceVersion === undefined ? "code" : "source_code");
8858
9014
  return sourceCode === null ? invalidFunctionReadResponse(INVALID_FUNCTION_SOURCE_RESPONSE) : functionSourceOutput(request.slug, sourceCode, request.output);
8859
9015
  }
8860
- function mutationIdentityMatches(receipt, expectation) {
8861
- return receipt.success === true && receipt.project_ref === expectation.projectRef && receipt.slug === expectation.slug && receipt.previous_active_version === expectation.expectedActiveVersion;
9016
+ function mutationIdentityMatches2(receipt, expectation) {
9017
+ return receipt.success === true && receipt.project_ref === expectation.projectRef && receipt.slug === expectation.slug && receipt.previous_active_version === expectation.expectedActiveVersion && receipt.expected_activation_id === expectation.expectedActivationId;
9018
+ }
9019
+ async function readFunctionIdentity(http, projectRef, slug) {
9020
+ const resourcePath = edgeFunctionResourcePath(projectRef, slug);
9021
+ const response = await http.get(`${resourcePath}/config`);
9022
+ if (!response.ok) {
9023
+ return releaseControlFailure("edge_functions.get_config", "HTTP_ERROR", response.status);
9024
+ }
9025
+ const identity = projectedFunctionIdentity(response.data, projectRef, slug);
9026
+ return identity ? { content: [{ type: "text", text: JSON.stringify(identity, null, 2) }] } : releaseControlFailure("edge_functions.get_config", "INVALID_RESPONSE", response.status);
9027
+ }
9028
+ async function updateFunctionConfiguration(http, request) {
9029
+ const response = await http.patchReleaseMutation(`${edgeFunctionResourcePath(request.projectRef, request.slug)}/config`, { ...request.config, expected_activation_id: request.expectedActivationId });
9030
+ if (!response.ok)
9031
+ return releaseControlMutationFailure("edge_functions.config", response);
9032
+ const confirmed = confirmedFunctionConfigMutation(response.data, request);
9033
+ return confirmed ? releaseControlSuccess("edge_functions.config", confirmed) : releaseControlFailure("edge_functions.config", "OUTCOME_UNKNOWN", response.status);
9034
+ }
9035
+ async function deleteFunction(http, request) {
9036
+ const response = await http.deleteReleaseMutation(edgeFunctionResourcePath(request.projectRef, request.slug), { expected_activation_id: request.expectedActivationId });
9037
+ if (!response.ok)
9038
+ return releaseControlMutationFailure("edge_functions.delete", response);
9039
+ const confirmed = confirmedFunctionDeletion(response.data, request);
9040
+ return confirmed ? releaseControlSuccess("edge_functions.delete", confirmed) : releaseControlFailure("edge_functions.delete", "OUTCOME_UNKNOWN", response.status);
8862
9041
  }
8863
9042
  function validReceiptVersion(activeVersion) {
8864
9043
  return typeof activeVersion === "string" && POSITIVE_FUNCTION_VERSION_PATTERN.test(activeVersion) && Number.isSafeInteger(Number(activeVersion));
@@ -8871,14 +9050,15 @@ function confirmedMutationVersion(receipt, config, expectation) {
8871
9050
  return activeVersion;
8872
9051
  }
8873
9052
  function confirmedFunctionMutation(expectation, payload) {
8874
- const receipt = objectRecord(payload);
8875
- const config = objectRecord(receipt?.config);
8876
- if (!receipt || !config || !mutationIdentityMatches(receipt, expectation))
9053
+ const receipt = objectRecord2(payload);
9054
+ const config = objectRecord2(receipt?.config);
9055
+ if (!receipt || !config || !mutationIdentityMatches2(receipt, expectation))
8877
9056
  return null;
8878
9057
  const activeVersion = confirmedMutationVersion(receipt, config, expectation);
8879
- if (activeVersion === null || typeof config.verify_jwt !== "boolean" || !confirmedFunctionConfig(config, expectation.config ?? {}))
9058
+ const activationId = receipt.activation_id;
9059
+ if (activeVersion === null || !validCommittedFunctionActivationId(activationId) || config.activation_id !== activationId || typeof config.verify_jwt !== "boolean" || !confirmedFunctionConfig(config, expectation.config ?? {}))
8880
9060
  return null;
8881
- return { activeVersion, verifyJwt: config.verify_jwt };
9061
+ return { activeVersion, activationId, verifyJwt: config.verify_jwt };
8882
9062
  }
8883
9063
  function functionMutationResponse(expectation, response) {
8884
9064
  if (!response.ok)
@@ -8891,6 +9071,8 @@ function functionMutationResponse(expectation, response) {
8891
9071
  project_ref: expectation.projectRef,
8892
9072
  slug: expectation.slug,
8893
9073
  previous_active_version: expectation.expectedActiveVersion,
9074
+ expected_activation_id: expectation.expectedActivationId,
9075
+ activation_id: confirmed.activationId,
8894
9076
  active_version: confirmed.activeVersion,
8895
9077
  version: confirmed.activeVersion,
8896
9078
  verify_jwt: confirmed.verifyJwt
@@ -8910,7 +9092,8 @@ function functionActivationTarget(args) {
8910
9092
  if (!SAFE_FUNCTION_SLUG_PATTERN.test(functionSlug))
8911
9093
  throw new Error("'slug' is invalid for 'activate'");
8912
9094
  const expectedActiveVersion = requiredExpectedActiveVersion(args, "activate");
8913
- return { projectRef, functionSlug, version, expectedActiveVersion };
9095
+ const expectedActivationId = requiredExpectedActivationId(args, "activate");
9096
+ return { projectRef, functionSlug, version, expectedActiveVersion, expectedActivationId };
8914
9097
  }
8915
9098
  function requiredExpectedActiveVersion(args, action) {
8916
9099
  const expected = args["expected-active-version"];
@@ -8922,28 +9105,42 @@ function requiredExpectedActiveVersion(args, action) {
8922
9105
  throw new Error("Expected active version is invalid");
8923
9106
  return parsed;
8924
9107
  }
9108
+ function requiredExpectedActivationId(args, action) {
9109
+ const expected = args["expected-activation-id"];
9110
+ if (expected === undefined) {
9111
+ throw new Error(`'--expected-activation-id' required for '${action}'`);
9112
+ }
9113
+ if (!validObservedFunctionActivationId(expected)) {
9114
+ throw new Error("Expected activation ID must be a canonical UUID or 'legacy'");
9115
+ }
9116
+ return expected;
9117
+ }
8925
9118
  async function activateFunctionVersion(http, args, readOnly = false) {
8926
9119
  if (readOnly)
8927
9120
  return readOnlyActivationResult();
8928
9121
  const unsupported = Object.keys(args).filter((name) => !FUNCTION_ACTIVATION_ARGUMENTS.has(name));
8929
9122
  if (unsupported.length > 0)
8930
9123
  throw new Error(`'${unsupported[0]}' is not supported for 'activate'`);
8931
- const { projectRef, functionSlug, version, expectedActiveVersion } = functionActivationTarget(args);
9124
+ const { projectRef, functionSlug, version, expectedActiveVersion, expectedActivationId } = functionActivationTarget(args);
8932
9125
  const endpoint = edgeFunctionResourcePath(projectRef, functionSlug) + `/versions/${encodeURIComponent(version)}/activate`;
8933
9126
  return functionMutationResponse({
8934
9127
  operation: "edge_functions.activate",
8935
9128
  projectRef,
8936
9129
  slug: functionSlug,
8937
9130
  expectedActiveVersion,
9131
+ expectedActivationId,
8938
9132
  targetVersion: version
8939
- }, await http.postReleaseMutation(endpoint, { expected_active_version: expectedActiveVersion }));
9133
+ }, await http.postReleaseMutation(endpoint, {
9134
+ expected_active_version: expectedActiveVersion,
9135
+ expected_activation_id: expectedActivationId
9136
+ }));
8940
9137
  }
8941
9138
  function registerAdvancedTools(server, http, environment = process.env, options = {}) {
8942
9139
  server.tool("edge_functions", `Edge Function management (Deno/Bun serverless). Source deploys are bundled; verified prebuilt artifacts stay byte-exact.
8943
- Actions: list, deploy, deploy_bundle, config, source, activate, delete, check`, {
8944
- action: withDescription(stringEnum(["list", "deploy", "deploy_bundle", "config", "source", "activate", "delete", "check"]), "Action"),
9140
+ Actions: list, get_config, deploy, deploy_bundle, config, source, activate, delete, check`, {
9141
+ action: withDescription(stringEnum(["list", "get_config", "deploy", "deploy_bundle", "config", "source", "activate", "delete", "check"]), "Action"),
8945
9142
  ref: withDescription(Type.String(), "Project ref"),
8946
- slug: optional(Type.String(), "[deploy/deploy_bundle/config/source/activate/delete/check] Function name"),
9143
+ slug: optional(Type.String(), "[get_config/deploy/deploy_bundle/config/source/activate/delete/check] Function name"),
8947
9144
  version: withDescription(functionVersionSchema, "[source/activate] Existing immutable Function version; source requires a positive version"),
8948
9145
  code: optional(Type.String(), "[deploy/check] Function source code (TypeScript)"),
8949
9146
  path: optional(Type.String(), "[deploy/check] Local file path to read code from (alternative to code)"),
@@ -8955,13 +9152,15 @@ Actions: list, deploy, deploy_bundle, config, source, activate, delete, check`,
8955
9152
  minify: optional(Type.Boolean(), "[deploy/deploy_bundle] Minify bundle"),
8956
9153
  verify_jwt: optional(Type.Boolean(), "[deploy/deploy_bundle/config] Set JWT verification for this function"),
8957
9154
  background_routes: withDescription(backgroundRoutesSchema, "[deploy/deploy_bundle/config] Background route paths; pass comma-separated or JSON array in CLI"),
8958
- "expected-active-version": withDescription(expectedActiveVersionSchema, "[deploy/deploy_bundle/activate] Required current active version, or 'absent' when none exists")
9155
+ "expected-active-version": withDescription(expectedActiveVersionSchema, "[deploy/deploy_bundle/activate] Required current active version, or 'absent' when none exists"),
9156
+ "expected-activation-id": withDescription(expectedActivationIdSchema, "[deploy/deploy_bundle/config/activate/delete] Required activation ID from list, or 'legacy' for a new or legacy function")
8959
9157
  }, async (args) => {
8960
9158
  if (args.action === "activate")
8961
9159
  return activateFunctionVersion(http, args, options.readOnly);
8962
9160
  const { action, ref, slug, path: pathArg, output, files, entrypoint, minify, verify_jwt, background_routes } = args;
8963
9161
  rejectPrebundledFlagsOutsideDeploy(action, args);
8964
9162
  const expectedActiveVersion = action === "deploy" || action === "deploy_bundle" ? requiredExpectedActiveVersion(args, action) : undefined;
9163
+ const expectedActivationId = FUNCTION_IDENTITY_MUTATIONS.has(action) ? requiredExpectedActivationId(args, action) : undefined;
8965
9164
  let code = args.code;
8966
9165
  const need = (f, v) => {
8967
9166
  if (!v)
@@ -8973,15 +9172,6 @@ Actions: list, deploy, deploy_bundle, config, source, activate, delete, check`,
8973
9172
  ...Array.isArray(background_routes) ? { background_routes } : {}
8974
9173
  });
8975
9174
  const hasFunctionConfig = () => Object.keys(functionConfig()).length > 0;
8976
- const updateFunctionConfig = async () => {
8977
- need("slug", slug);
8978
- if (!hasFunctionConfig()) {
8979
- throw new Error("'verify_jwt' or 'background_routes' required for 'config'");
8980
- }
8981
- const cr = await http.patch(`${edgeFunctionResourcePath(ref, slug)}/config`, functionConfig());
8982
- return cr.ok ? `✅ Function ${slug} config updated
8983
- ${JSON.stringify(cr.data, null, 2)}` : `❌ Config update failed (${cr.status}): ${JSON.stringify(cr.data)}`;
8984
- };
8985
9175
  const checkSyntax = async (sourceCode) => {
8986
9176
  const tmpDir = mkdtempSync(join2(tmpdir(), "supacloud-edge-check-"));
8987
9177
  const tmpFile = join2(tmpDir, "index.ts");
@@ -9007,6 +9197,9 @@ ${e.stderr || e.message}` };
9007
9197
  switch (action) {
9008
9198
  case "list":
9009
9199
  return functionListResponse(await http.get(edgeFunctionResourcePath(ref)));
9200
+ case "get_config":
9201
+ need("slug", slug);
9202
+ return readFunctionIdentity(http, ref, slug);
9010
9203
  case "check":
9011
9204
  need("code (or path)", code);
9012
9205
  const checkRes = await checkSyntax(code);
@@ -9032,6 +9225,7 @@ ${deployCheck.err}`;
9032
9225
  code: deployCode.code,
9033
9226
  ...deployCode.prebundled ? { prebundled: true, expected_sha256: deployCode.expectedSha256 } : { minify },
9034
9227
  expected_active_version: expectedActiveVersion,
9228
+ expected_activation_id: expectedActivationId,
9035
9229
  ...functionConfig()
9036
9230
  });
9037
9231
  return functionMutationResponse({
@@ -9039,6 +9233,7 @@ ${deployCheck.err}`;
9039
9233
  projectRef: ref,
9040
9234
  slug,
9041
9235
  expectedActiveVersion,
9236
+ expectedActivationId,
9042
9237
  config: functionConfig()
9043
9238
  }, deploymentResponse);
9044
9239
  case "deploy_bundle":
@@ -9049,6 +9244,7 @@ ${deployCheck.err}`;
9049
9244
  entrypoint,
9050
9245
  minify,
9051
9246
  expected_active_version: expectedActiveVersion,
9247
+ expected_activation_id: expectedActivationId,
9052
9248
  ...functionConfig()
9053
9249
  });
9054
9250
  return functionMutationResponse({
@@ -9056,11 +9252,20 @@ ${deployCheck.err}`;
9056
9252
  projectRef: ref,
9057
9253
  slug,
9058
9254
  expectedActiveVersion,
9255
+ expectedActivationId,
9059
9256
  config: functionConfig()
9060
9257
  }, bundleResponse);
9061
9258
  case "config":
9062
- text = await updateFunctionConfig();
9063
- break;
9259
+ need("slug", slug);
9260
+ if (!hasFunctionConfig()) {
9261
+ throw new Error("'verify_jwt' or 'background_routes' required for 'config'");
9262
+ }
9263
+ return updateFunctionConfiguration(http, {
9264
+ projectRef: ref,
9265
+ slug,
9266
+ expectedActivationId,
9267
+ config: functionConfig()
9268
+ });
9064
9269
  case "source":
9065
9270
  need("slug", slug);
9066
9271
  return readFunctionSource(http, {
@@ -9071,8 +9276,11 @@ ${deployCheck.err}`;
9071
9276
  });
9072
9277
  case "delete":
9073
9278
  need("slug", slug);
9074
- text = (await http.delete(edgeFunctionResourcePath(ref, slug))).ok ? `✅ Function ${slug} deleted` : `❌ Failed`;
9075
- break;
9279
+ return deleteFunction(http, {
9280
+ projectRef: ref,
9281
+ slug,
9282
+ expectedActivationId
9283
+ });
9076
9284
  default:
9077
9285
  text = `❌ Unknown action`;
9078
9286
  }
@@ -11188,7 +11396,7 @@ var FORBIDDEN_HEADER_NAMES = new Set([
11188
11396
  "x-project-ref"
11189
11397
  ]);
11190
11398
  var SCHEDULE_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
11191
- var SAFE_SLUG_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
11399
+ var SAFE_SLUG_PATTERN2 = /^[A-Za-z0-9_-]{1,128}$/;
11192
11400
  var CRON_PART_PATTERN = /^(\*|([0-9]+)(?:-([0-9]+))?)(?:\/([0-9]+))?$/;
11193
11401
  var CRON_FIELD_BOUNDS = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 7]];
11194
11402
  var MAX_CRON_EXPRESSION_LENGTH = 256;
@@ -11217,7 +11425,7 @@ function parseHeaderEnvironment(input) {
11217
11425
  }
11218
11426
  }
11219
11427
  var headerEnvironmentSchema = Type.Optional(decodedSchema(Type.Union([Type.String(), headerEnvironmentRecord]), headerEnvironmentRecord, parseHeaderEnvironment));
11220
- function objectRecord2(candidate) {
11428
+ function objectRecord3(candidate) {
11221
11429
  return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
11222
11430
  }
11223
11431
  function boundedCronInteger(input, minimum, maximum) {
@@ -11262,7 +11470,7 @@ function readScheduleBodyFile(bodyPathInput) {
11262
11470
  throw error;
11263
11471
  throw new Error("Scheduled Function body file must contain exact JSON");
11264
11472
  }
11265
- const body = objectRecord2(payload);
11473
+ const body = objectRecord3(payload);
11266
11474
  if (!body)
11267
11475
  throw new Error("Scheduled Function body file must contain a JSON object");
11268
11476
  return body;
@@ -11303,7 +11511,7 @@ function headerValueIsStable(name, value) {
11303
11511
  function scheduleHeaders(mapping, environment) {
11304
11512
  if (mapping === undefined)
11305
11513
  return;
11306
- const headerEnvironment = objectRecord2(mapping);
11514
+ const headerEnvironment = objectRecord3(mapping);
11307
11515
  if (!headerEnvironment)
11308
11516
  throw new Error("'header_env' must be a JSON object");
11309
11517
  const entries = Object.entries(headerEnvironment).map(([headerName, environmentName]) => resolvedHeaderEntry(headerName, environmentName, environment));
@@ -11317,7 +11525,7 @@ function validSafeSchedule(schedule) {
11317
11525
  return validScheduleIdentity(schedule) && validScheduleDefinition(schedule) && validScheduleMetadata(schedule);
11318
11526
  }
11319
11527
  function validScheduleIdentity(schedule) {
11320
- return typeof schedule.id === "string" && SCHEDULE_ID_PATTERN.test(schedule.id) && typeof schedule.name === "string" && schedule.name.trim().length > 0 && schedule.name.length <= MAX_SCHEDULE_NAME_LENGTH && typeof schedule.slug === "string" && SAFE_SLUG_PATTERN.test(schedule.slug);
11528
+ return typeof schedule.id === "string" && SCHEDULE_ID_PATTERN.test(schedule.id) && typeof schedule.name === "string" && schedule.name.trim().length > 0 && schedule.name.length <= MAX_SCHEDULE_NAME_LENGTH && typeof schedule.slug === "string" && SAFE_SLUG_PATTERN2.test(schedule.slug);
11321
11529
  }
11322
11530
  function validScheduleDefinition(schedule) {
11323
11531
  return typeof schedule.cron === "string" && validScheduledFunctionCron(schedule.cron) && (schedule.method === "GET" || schedule.method === "POST") && typeof schedule.enabled === "boolean";
@@ -11332,11 +11540,28 @@ function isCanonicalTimestamp(candidate) {
11332
11540
  return Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === candidate;
11333
11541
  }
11334
11542
  function safeSchedulePayload(schedule) {
11543
+ const hasProjectedBody = Object.hasOwn(schedule, "body_empty");
11544
+ const hasProjectedHeaders = Object.hasOwn(schedule, "header_names");
11545
+ if (!hasProjectedBody && !hasProjectedHeaders)
11546
+ return legacySchedulePayload(schedule);
11547
+ if (!hasProjectedBody || !hasProjectedHeaders)
11548
+ return null;
11335
11549
  const headerNames = safeHeaderNames(schedule.header_names);
11336
11550
  if (typeof schedule.body_empty !== "boolean" || !headerNames)
11337
11551
  return null;
11338
11552
  return { body_empty: schedule.body_empty, header_names: headerNames };
11339
11553
  }
11554
+ function legacySchedulePayload(schedule) {
11555
+ const body = objectRecord3(schedule.body);
11556
+ const headers = objectRecord3(schedule.headers);
11557
+ if (!body || !headers)
11558
+ return null;
11559
+ const headerNames = safeHeaderNames(Object.keys(headers));
11560
+ const stableHeaderValues = Object.entries(headers).every(([name, value]) => typeof value === "string" && headerValueIsStable(name.toLowerCase(), value));
11561
+ if (!headerNames || !stableHeaderValues)
11562
+ return null;
11563
+ return { body_empty: Object.keys(body).length === 0, header_names: headerNames };
11564
+ }
11340
11565
  function safeHeaderNames(candidate) {
11341
11566
  if (!Array.isArray(candidate) || candidate.length > MAX_HEADER_COUNT)
11342
11567
  return null;
@@ -11345,7 +11570,7 @@ function safeHeaderNames(candidate) {
11345
11570
  return valid && new Set(names).size === names.length ? names.sort() : null;
11346
11571
  }
11347
11572
  function safeSchedule(candidate) {
11348
- const schedule = objectRecord2(candidate);
11573
+ const schedule = objectRecord3(candidate);
11349
11574
  if (!schedule || !validSafeSchedule(schedule))
11350
11575
  return null;
11351
11576
  const safePayload = safeSchedulePayload(schedule);
@@ -11391,7 +11616,7 @@ function listResponse(ref, response) {
11391
11616
  const operation = "scheduled_functions.list";
11392
11617
  if (!response.ok)
11393
11618
  return scheduleFailure(operation, response);
11394
- const payload = objectRecord2(response.data);
11619
+ const payload = objectRecord3(response.data);
11395
11620
  const rawSchedules = payload?.schedules;
11396
11621
  const schedules = Array.isArray(rawSchedules) ? rawSchedules.map(safeSchedule) : null;
11397
11622
  if (payload?.project_ref !== ref || !schedules || schedules.some((schedule) => !schedule)) {
@@ -11406,7 +11631,7 @@ function getResponse(ref, scheduleId, response) {
11406
11631
  const operation = "scheduled_functions.get";
11407
11632
  if (!response.ok)
11408
11633
  return scheduleFailure(operation, response);
11409
- const payload = objectRecord2(response.data);
11634
+ const payload = objectRecord3(response.data);
11410
11635
  const schedule = safeSchedule(payload?.schedule);
11411
11636
  if (payload?.project_ref !== ref || !schedule || schedule.id !== scheduleId) {
11412
11637
  return releaseControlFailure(operation, "INVALID_RESPONSE", null);
@@ -11419,7 +11644,7 @@ function mutationResponse(expectation, response) {
11419
11644
  const operation = `scheduled_functions.${action}`;
11420
11645
  if (!response.ok)
11421
11646
  return releaseControlMutationFailure(operation, response);
11422
- const payload = objectRecord2(response.data);
11647
+ const payload = objectRecord3(response.data);
11423
11648
  const schedule = safeSchedule(payload?.schedule);
11424
11649
  const confirmsRequest = schedule && Object.entries(expectedFields).every(([field, expected]) => isDeepStrictEqual(schedule[field], expected));
11425
11650
  const confirmsRevision = action === "create" || payload?.previous_updated_at === expectation.expectedUpdatedAt && schedule !== null && schedule.updated_at > expectation.expectedUpdatedAt;
@@ -11437,7 +11662,7 @@ function deleteResponse(ref, scheduleId, expectedUpdatedAt, response) {
11437
11662
  const operation = "scheduled_functions.delete";
11438
11663
  if (!response.ok)
11439
11664
  return releaseControlMutationFailure(operation, response);
11440
- const payload = objectRecord2(response.data);
11665
+ const payload = objectRecord3(response.data);
11441
11666
  if (payload?.deleted !== true || payload.project_ref !== ref || payload.schedule_id !== scheduleId || payload.deleted_updated_at !== expectedUpdatedAt) {
11442
11667
  return releaseControlFailure(operation, "OUTCOME_UNKNOWN", response.status);
11443
11668
  }
@@ -11483,7 +11708,7 @@ function requiredName(args, action) {
11483
11708
  }
11484
11709
  function requiredSlug(args, action) {
11485
11710
  const slug = requiredText2(args, "slug", action);
11486
- if (!SAFE_SLUG_PATTERN.test(slug))
11711
+ if (!SAFE_SLUG_PATTERN2.test(slug))
11487
11712
  throw new Error(`'slug' is invalid for '${action}'`);
11488
11713
  return slug;
11489
11714
  }
@@ -11620,17 +11845,17 @@ var LEASE_KEYS = ["owner", "expires_at", "fencing_epoch"];
11620
11845
  function isMutationId(candidate) {
11621
11846
  return typeof candidate === "string" && MUTATION_ID_PATTERN.test(candidate);
11622
11847
  }
11623
- function objectRecord3(candidate) {
11848
+ function objectRecord4(candidate) {
11624
11849
  return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
11625
11850
  }
11626
11851
  function exactRecord(candidate, keys) {
11627
- const record = objectRecord3(candidate);
11852
+ const record = objectRecord4(candidate);
11628
11853
  if (!record || Object.keys(record).length !== keys.length)
11629
11854
  return null;
11630
11855
  return keys.every((key) => Object.hasOwn(record, key)) ? record : null;
11631
11856
  }
11632
11857
  function emptyProjection(candidate) {
11633
- const record = objectRecord3(candidate);
11858
+ const record = objectRecord4(candidate);
11634
11859
  return record && Object.keys(record).length === 0 ? record : null;
11635
11860
  }
11636
11861
  function canonicalTimestamp2(candidate) {
@@ -11814,7 +12039,7 @@ var MUTATION_TOOL_SCHEMA = {
11814
12039
  // package.json
11815
12040
  var package_default = {
11816
12041
  name: "@supacloud/cli",
11817
- version: "0.20.0",
12042
+ version: "0.21.1",
11818
12043
  description: "Project-scoped CLI for SupaCloud users",
11819
12044
  type: "module",
11820
12045
  main: "./dist/index.js",
@@ -12074,12 +12299,14 @@ EXAMPLES
12074
12299
  ${preferredCommand} branch promote --branch_ref preview123 --plan_checksum <sha256>
12075
12300
  ${preferredCommand} ai show_skill
12076
12301
  ${preferredCommand} ai install_skill --dry_run
12077
- ${preferredCommand} edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent
12078
- ${preferredCommand} edge_functions deploy --ref abc123 --slug hello --prebundled-path ./dist/hello.js --expected-sha256 <sha256> --expected-active-version 4
12079
- ${preferredCommand} edge_functions activate --ref abc123 --slug hello --version 3 --expected-active-version 4
12302
+ ${preferredCommand} edge_functions get_config --ref abc123 --slug hello
12303
+ ${preferredCommand} edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello --expected-active-version absent --expected-activation-id legacy
12304
+ ${preferredCommand} edge_functions deploy --ref abc123 --slug hello --prebundled-path ./dist/hello.js --expected-sha256 <sha256> --expected-active-version 4 --expected-activation-id <uuid>
12305
+ ${preferredCommand} edge_functions activate --ref abc123 --slug hello --version 3 --expected-active-version 4 --expected-activation-id <uuid>
12080
12306
  ${preferredCommand} scheduled_functions list --ref abc123
12081
12307
  ${preferredCommand} mutations status --ref abc123 --mutation_id 00000000-0000-4000-8000-000000000001
12082
- ${preferredCommand} edge_functions config --ref abc123 --slug hello --verify_jwt false --background_routes "/queue/*,/render/*"
12308
+ ${preferredCommand} edge_functions config --ref abc123 --slug hello --verify_jwt false --background_routes "/queue/*,/render/*" --expected-activation-id <uuid>
12309
+ ${preferredCommand} edge_functions delete --ref abc123 --slug hello --expected-activation-id <uuid>
12083
12310
  ${preferredCommand} secrets upsert --ref abc123 --from-env API_KEY,WEBHOOK_SECRET
12084
12311
  ${preferredCommand} gateway routes --ref abc123
12085
12312
  ${preferredCommand} gateway upsert_route --ref abc123 --route_id webhook --hosts "api.example.com" --paths "/webhook/*" --upstream 10.0.0.5:8080
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.20.0",
3
+ "version": "0.21.1",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,7 +31,7 @@ until a project-scoped context is resolved.
31
31
  - `supabase`: allowlisted official CLI adapter for migration authoring, local reset/diff, explicit-DSN inspection/backup/type generation, and SupaCloud-controlled migration push.
32
32
  - `auth`: provider and authentication configuration.
33
33
  - `storage`: buckets and object-management workflows.
34
- - `edge_functions`: list, read immutable source, deploy, activate, and configure Edge Functions. Pass the non-negative version read from `list` as `--expected-active-version`; use `absent` only for a new slug. Version `0` is a legacy CAS token and cannot be used as a source or activation target.
34
+ - `edge_functions`: list, atomically read one active or deleted identity with `get_config`, read immutable source, deploy, activate, configure, and delete Edge Functions. For every mutation, pass the `activation_id` read from the same `list` or `get_config` snapshot as `--expected-activation-id`; use `legacy` only for a never-created or listed legacy Function, not for a deleted slug with a tombstone UUID. Deploy and activate actions also require the non-negative observed version as `--expected-active-version`; use `absent` for a never-created slug or a `get_config` tombstone. Version `0` is a legacy version token and cannot be used as a source or activation target.
35
35
  - `frontend`: list, build/deploy, domain, and deployment workflows.
36
36
  - `secrets`: project secret management; never print values after write.
37
37
  - `queue`, `task_events`, `diagnostics`: asynchronous workload operations and bounded diagnostics.