@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 +23 -12
- package/dist/index.js +298 -71
- package/package.json +1 -1
- package/skills/supacloud-cli/references/command-map.md +1 -1
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
|
|
165
|
-
supacloud-cli edge_functions deploy --ref abc123 --slug hello --
|
|
166
|
-
supacloud-cli edge_functions
|
|
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.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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,
|
|
238
|
-
separate 5-second, 64 KiB response-body boundary after receiving
|
|
239
|
-
A stalled, oversized, truncated, unreadable, or malformed body is
|
|
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
|
|
6785
|
+
async mutationWithResponseReader(method, path, serializedBody, responseReader) {
|
|
6786
6786
|
try {
|
|
6787
6787
|
const response = await fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
6788
|
-
method
|
|
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.
|
|
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.
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
8875
|
-
const config =
|
|
8876
|
-
if (!receipt || !config || !
|
|
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
|
-
|
|
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
|
-
|
|
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, {
|
|
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
|
-
|
|
9063
|
-
|
|
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
|
-
|
|
9075
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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" &&
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
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
|
|
12078
|
-
${preferredCommand} edge_functions deploy --ref abc123 --slug hello --
|
|
12079
|
-
${preferredCommand} edge_functions
|
|
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
|
@@ -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
|
|
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.
|