@supacloud/cli 0.110.1 → 0.112.0
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 +13 -0
- package/dist/index.js +410 -77
- package/dist/shared/tools/frontend-release-control.d.ts +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -605,6 +605,19 @@ supacloud release release_canary_fixture_disable_replay --ref abc123 \
|
|
|
605
605
|
--issuer <issuer-url> --subject <central-subject-uuid>
|
|
606
606
|
```
|
|
607
607
|
|
|
608
|
+
A standard cross-component release contract can be validated without mutation:
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
supacloud release contract_plan --file supacloud.release.json \
|
|
612
|
+
--ref abc123 --environment_id production --require_current_source
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
The plan rejects project or environment binding mismatches and returns the
|
|
616
|
+
canonical `scope_sha256`; it does not apply migrations, Functions, Application
|
|
617
|
+
releases, or frontend assets. See
|
|
618
|
+
[docs/application-release-contract.md](../../docs/application-release-contract.md).
|
|
619
|
+
|
|
620
|
+
|
|
608
621
|
Backup creation reports success only after the CLI verifies exactly one new
|
|
609
622
|
logical-backup receipt against the inventory before and after the mutation.
|
|
610
623
|
Logical restore is project-scoped: pause the selected project first, obtain the
|
package/dist/index.js
CHANGED
|
@@ -9341,12 +9341,12 @@ var ACTION_POLICY = {
|
|
|
9341
9341
|
mutations: { read: ["status"] },
|
|
9342
9342
|
release: {
|
|
9343
9343
|
read: ["logical_backup_list", "logical_backup_status", "postgrest_status"],
|
|
9344
|
-
local: ["scope_inspect", "scope_rebind", "scope_create"],
|
|
9344
|
+
local: ["scope_inspect", "scope_rebind", "scope_create", "contract_plan"],
|
|
9345
9345
|
write: ["logical_backup_create", "logical_backup_restore", "postgrest_restart", "release_canary_fixture_stage_replay", "release_canary_fixture_disable_replay"]
|
|
9346
9346
|
},
|
|
9347
9347
|
secrets: { read: ["list"], write: ["upsert", "delete"] },
|
|
9348
9348
|
frontend: {
|
|
9349
|
-
read: ["list", "get", "build_logs", "list_frameworks", "list_records", "list_releases", "get_active_release", "get_release"],
|
|
9349
|
+
read: ["list", "get", "build_logs", "list_frameworks", "list_records", "list_releases", "get_active_release", "get_release", "verify_release"],
|
|
9350
9350
|
write: ["create", "update", "delete", "deploy_git", "deploy_upload", "redeploy", "add_domain", "remove_domain", "set_env", "upload_release", "activate_release", "rollback"]
|
|
9351
9351
|
},
|
|
9352
9352
|
task_events: { read: ["inspect_webhook"], write: ["register_webhook", "unregister_webhook"] },
|
|
@@ -9975,7 +9975,7 @@ class HttpTransport {
|
|
|
9975
9975
|
}
|
|
9976
9976
|
|
|
9977
9977
|
// src/shared/tools/database-tools.ts
|
|
9978
|
-
import { createHash as
|
|
9978
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
9979
9979
|
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
9980
9980
|
import { basename as basename3, join as join4 } from "node:path";
|
|
9981
9981
|
|
|
@@ -11486,6 +11486,108 @@ function buildDeliveryMigrationPlan(archive, inventory, ref) {
|
|
|
11486
11486
|
dataRecovery: "separate-required"
|
|
11487
11487
|
};
|
|
11488
11488
|
}
|
|
11489
|
+
// node_modules/@supacloud/delivery/src/release-contract.ts
|
|
11490
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
11491
|
+
var RELEASE_CONTRACT_SCHEMA = "supacloud.release-control.v2";
|
|
11492
|
+
var SHA256 = /^[0-9a-f]{64}$/;
|
|
11493
|
+
var COMMIT = /^[0-9a-f]{40}$/;
|
|
11494
|
+
var PROJECT_REF = /^[a-z0-9]{20}$/;
|
|
11495
|
+
var ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
11496
|
+
var FUNCTION_SLUG = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
11497
|
+
var MIGRATION_NAME = /^\d{14}_[a-z0-9_]+$/;
|
|
11498
|
+
function record3(value, label) {
|
|
11499
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11500
|
+
throw new Error(`${label} must be an object`);
|
|
11501
|
+
}
|
|
11502
|
+
return value;
|
|
11503
|
+
}
|
|
11504
|
+
function exactKeys(value, expected, label) {
|
|
11505
|
+
const actual = Object.keys(value).sort();
|
|
11506
|
+
const keys = [...expected].sort();
|
|
11507
|
+
if (actual.length !== keys.length || actual.some((key, index) => key !== keys[index])) {
|
|
11508
|
+
throw new Error(`${label} contains unsupported or missing fields`);
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
function nonEmptyString(value, label, pattern) {
|
|
11512
|
+
if (typeof value !== "string" || value.length === 0 || pattern && !pattern.test(value)) {
|
|
11513
|
+
throw new Error(`${label} is invalid`);
|
|
11514
|
+
}
|
|
11515
|
+
return value;
|
|
11516
|
+
}
|
|
11517
|
+
function parseComponent(value, index) {
|
|
11518
|
+
const candidate = record3(value, `Release component ${index}`);
|
|
11519
|
+
if (candidate["kind"] === "migration") {
|
|
11520
|
+
exactKeys(candidate, ["kind", "name"], `Release migration ${index}`);
|
|
11521
|
+
return { kind: "migration", name: nonEmptyString(candidate["name"], `Release migration ${index}`, MIGRATION_NAME) };
|
|
11522
|
+
}
|
|
11523
|
+
if (candidate["kind"] === "function") {
|
|
11524
|
+
exactKeys(candidate, ["artifactSha256", "kind", "slug"], `Release Function ${index}`);
|
|
11525
|
+
return {
|
|
11526
|
+
kind: "function",
|
|
11527
|
+
slug: nonEmptyString(candidate["slug"], `Release Function ${index}`, FUNCTION_SLUG),
|
|
11528
|
+
artifactSha256: nonEmptyString(candidate["artifactSha256"], `Release Function ${index}`, SHA256)
|
|
11529
|
+
};
|
|
11530
|
+
}
|
|
11531
|
+
if (candidate["kind"] === "web") {
|
|
11532
|
+
exactKeys(candidate, ["artifactSha256", "kind"], `Release Web ${index}`);
|
|
11533
|
+
return {
|
|
11534
|
+
kind: "web",
|
|
11535
|
+
artifactSha256: nonEmptyString(candidate["artifactSha256"], `Release Web ${index}`, SHA256)
|
|
11536
|
+
};
|
|
11537
|
+
}
|
|
11538
|
+
throw new Error(`Release component ${index} kind is invalid`);
|
|
11539
|
+
}
|
|
11540
|
+
function componentKey(component) {
|
|
11541
|
+
return component.kind === "migration" ? `${component.kind}:${component.name}` : component.kind === "function" ? `${component.kind}:${component.slug}` : component.kind;
|
|
11542
|
+
}
|
|
11543
|
+
function parseReleaseScope(value) {
|
|
11544
|
+
const candidate = record3(value, "Release scope");
|
|
11545
|
+
exactKeys(candidate, [
|
|
11546
|
+
"components",
|
|
11547
|
+
"releaseId",
|
|
11548
|
+
"schema",
|
|
11549
|
+
"sourceCommit",
|
|
11550
|
+
"targetEnvironmentId",
|
|
11551
|
+
"targetProjectRef"
|
|
11552
|
+
], "Release scope");
|
|
11553
|
+
if (candidate["schema"] !== RELEASE_CONTRACT_SCHEMA) {
|
|
11554
|
+
throw new Error("Release scope schema is invalid");
|
|
11555
|
+
}
|
|
11556
|
+
if (!Array.isArray(candidate["components"]) || candidate["components"].length === 0) {
|
|
11557
|
+
throw new Error("Release scope components must be non-empty");
|
|
11558
|
+
}
|
|
11559
|
+
const components = candidate["components"].map(parseComponent);
|
|
11560
|
+
const componentKeys = components.map(componentKey);
|
|
11561
|
+
if (new Set(componentKeys).size !== componentKeys.length || componentKeys.some((key, index) => key !== [...componentKeys].sort()[index])) {
|
|
11562
|
+
throw new Error("Release scope components must be unique and sorted");
|
|
11563
|
+
}
|
|
11564
|
+
return {
|
|
11565
|
+
schema: RELEASE_CONTRACT_SCHEMA,
|
|
11566
|
+
releaseId: nonEmptyString(candidate["releaseId"], "Release scope releaseId"),
|
|
11567
|
+
sourceCommit: nonEmptyString(candidate["sourceCommit"], "Release scope sourceCommit", COMMIT),
|
|
11568
|
+
targetEnvironmentId: nonEmptyString(candidate["targetEnvironmentId"], "Release scope targetEnvironmentId", ENVIRONMENT_ID),
|
|
11569
|
+
targetProjectRef: nonEmptyString(candidate["targetProjectRef"], "Release scope targetProjectRef", PROJECT_REF),
|
|
11570
|
+
components
|
|
11571
|
+
};
|
|
11572
|
+
}
|
|
11573
|
+
function canonical2(value) {
|
|
11574
|
+
if (Array.isArray(value))
|
|
11575
|
+
return value.map(canonical2);
|
|
11576
|
+
if (!value || typeof value !== "object")
|
|
11577
|
+
return value;
|
|
11578
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, canonical2(item)]));
|
|
11579
|
+
}
|
|
11580
|
+
function releaseScopeSha256(scope) {
|
|
11581
|
+
return createHash3("sha256").update(JSON.stringify(canonical2(scope))).digest("hex");
|
|
11582
|
+
}
|
|
11583
|
+
var ALLOWED_PHASE_TRANSITIONS = new Map([
|
|
11584
|
+
["planned", ["mutating", "rollback-required"]],
|
|
11585
|
+
["mutating", ["applied", "rollback-required"]],
|
|
11586
|
+
["applied", ["reconciled", "rollback-required"]],
|
|
11587
|
+
["reconciled", []],
|
|
11588
|
+
["rollback-required", ["rolled-back", "reconciled"]],
|
|
11589
|
+
["rolled-back", []]
|
|
11590
|
+
]);
|
|
11489
11591
|
|
|
11490
11592
|
// node_modules/@supacloud/delivery/src/index.ts
|
|
11491
11593
|
async function readDeliveryExecutableArchive(manifestPath) {
|
|
@@ -12276,7 +12378,7 @@ async function writeMigrationImportPlan(plan, outputDirectory) {
|
|
|
12276
12378
|
}
|
|
12277
12379
|
return written;
|
|
12278
12380
|
}
|
|
12279
|
-
function
|
|
12381
|
+
function record4(value) {
|
|
12280
12382
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
12281
12383
|
}
|
|
12282
12384
|
function sqlFile(file, root, source) {
|
|
@@ -12288,17 +12390,17 @@ function sqlFile(file, root, source) {
|
|
|
12288
12390
|
};
|
|
12289
12391
|
}
|
|
12290
12392
|
function hasuraMetadataSql(input) {
|
|
12291
|
-
const metadata =
|
|
12393
|
+
const metadata = record4(input["metadata"]) ?? input;
|
|
12292
12394
|
const sources = Array.isArray(metadata["sources"]) ? metadata["sources"] : [];
|
|
12293
12395
|
const sql = [];
|
|
12294
12396
|
for (const rawSource of sources) {
|
|
12295
|
-
const source =
|
|
12397
|
+
const source = record4(rawSource);
|
|
12296
12398
|
if (!source)
|
|
12297
12399
|
continue;
|
|
12298
12400
|
const tables = Array.isArray(source["tables"]) ? source["tables"] : [];
|
|
12299
12401
|
for (const rawTable of tables) {
|
|
12300
|
-
const table =
|
|
12301
|
-
const tableName =
|
|
12402
|
+
const table = record4(rawTable);
|
|
12403
|
+
const tableName = record4(table?.["table"]);
|
|
12302
12404
|
const schema = typeof tableName?.["schema"] === "string" ? tableName["schema"] : "public";
|
|
12303
12405
|
const name = typeof tableName?.["name"] === "string" ? tableName["name"] : null;
|
|
12304
12406
|
if (!name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(schema) || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
|
|
@@ -12341,7 +12443,7 @@ function createMigrationImportPlan(input) {
|
|
|
12341
12443
|
}
|
|
12342
12444
|
}
|
|
12343
12445
|
if (input.source === "hasura" && input.metadata !== undefined) {
|
|
12344
|
-
const metadata =
|
|
12446
|
+
const metadata = record4(input.metadata) ?? {};
|
|
12345
12447
|
const generated = hasuraMetadataSql(metadata);
|
|
12346
12448
|
if (generated.length > 0) {
|
|
12347
12449
|
files.push({ file: "999999999999_hasura_rls_review.sql", name: "hasura_rls_review", sql: `${generated.join(`
|
|
@@ -12354,10 +12456,10 @@ function createMigrationImportPlan(input) {
|
|
|
12354
12456
|
const actions = Array.isArray(metadata["actions"]) ? metadata["actions"] : [];
|
|
12355
12457
|
const remoteSchemas = Array.isArray(metadata["remote_schemas"]) ? metadata["remote_schemas"] : [];
|
|
12356
12458
|
const eventTriggers = Array.isArray(metadata["sources"]) ? metadata["sources"].flatMap((source) => {
|
|
12357
|
-
const sourceRecord =
|
|
12459
|
+
const sourceRecord = record4(source);
|
|
12358
12460
|
const tables = Array.isArray(sourceRecord?.["tables"]) ? sourceRecord["tables"] : [];
|
|
12359
12461
|
return tables.flatMap((table) => {
|
|
12360
|
-
const tableRecord =
|
|
12462
|
+
const tableRecord = record4(table);
|
|
12361
12463
|
return Array.isArray(tableRecord?.["event_triggers"]) ? tableRecord["event_triggers"] : [];
|
|
12362
12464
|
});
|
|
12363
12465
|
}) : [];
|
|
@@ -12407,7 +12509,7 @@ function isMigrationInventoryAppliedAt(appliedAt) {
|
|
|
12407
12509
|
return appliedAt === null || typeof appliedAt === "string" && appliedAt.trim() === appliedAt && appliedAt.length > 0 && !Number.isNaN(Date.parse(appliedAt));
|
|
12408
12510
|
}
|
|
12409
12511
|
function migrationInventoryChecksum(entry) {
|
|
12410
|
-
return
|
|
12512
|
+
return createHash4("sha256").update(JSON.stringify({
|
|
12411
12513
|
version: entry.version,
|
|
12412
12514
|
name: entry.name,
|
|
12413
12515
|
statements: entry.statements
|
|
@@ -12514,7 +12616,7 @@ function migrationVersionFromFilename(file) {
|
|
|
12514
12616
|
}
|
|
12515
12617
|
return version.toString();
|
|
12516
12618
|
}
|
|
12517
|
-
const digest =
|
|
12619
|
+
const digest = createHash4("sha256").update(basename3(file)).digest("hex");
|
|
12518
12620
|
const offset = BigInt(`0x${digest}`) % FALLBACK_MIGRATION_VERSION_RANGE;
|
|
12519
12621
|
return (FALLBACK_MIGRATION_VERSION_BASE + offset).toString();
|
|
12520
12622
|
}
|
|
@@ -12611,7 +12713,7 @@ function exactIdentityIsApplied(local, remote) {
|
|
|
12611
12713
|
return false;
|
|
12612
12714
|
if (statement === `baseline:${local.name}` || statement === `direct-apply:${local.name}`)
|
|
12613
12715
|
return true;
|
|
12614
|
-
const rawChecksum =
|
|
12716
|
+
const rawChecksum = createHash4("sha256").update(local.rawBytes).digest("hex");
|
|
12615
12717
|
return statement === `sha256:${rawChecksum}` || statement.trim() === local.sql.trim();
|
|
12616
12718
|
}
|
|
12617
12719
|
function legacyIdentityIsApplied(local, remote) {
|
|
@@ -15074,7 +15176,7 @@ Actions: status, list_buckets, get_bucket, create_bucket, update_bucket, delete_
|
|
|
15074
15176
|
}
|
|
15075
15177
|
|
|
15076
15178
|
// src/shared/tools/advanced-tools.ts
|
|
15077
|
-
import { createHash as
|
|
15179
|
+
import { createHash as createHash6, timingSafeEqual } from "node:crypto";
|
|
15078
15180
|
import {
|
|
15079
15181
|
closeSync,
|
|
15080
15182
|
constants as fsConstants,
|
|
@@ -15095,7 +15197,7 @@ import { basename as basename5, join as join5, relative as relative2, resolve as
|
|
|
15095
15197
|
|
|
15096
15198
|
// src/shared/tools/function-aot.ts
|
|
15097
15199
|
import { execFile } from "node:child_process";
|
|
15098
|
-
import { createHash as
|
|
15200
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
15099
15201
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
15100
15202
|
import { dirname as dirname3, resolve as resolve6 } from "node:path";
|
|
15101
15203
|
import { promisify } from "node:util";
|
|
@@ -15177,7 +15279,7 @@ async function buildFunctionAot(entrypoint, outfile, options = {}) {
|
|
|
15177
15279
|
const code = readFileSync5(outfile, "utf8");
|
|
15178
15280
|
if (!code.trim())
|
|
15179
15281
|
throw new Error("AOT build produced an empty bundle");
|
|
15180
|
-
return { code, prebundled: true, expectedSha256:
|
|
15282
|
+
return { code, prebundled: true, expectedSha256: createHash5("sha256").update(code).digest("hex") };
|
|
15181
15283
|
}
|
|
15182
15284
|
|
|
15183
15285
|
// src/shared/tools/edge-function-response.ts
|
|
@@ -15531,7 +15633,7 @@ function assertExpectedSha256(bytes, expectedSha256) {
|
|
|
15531
15633
|
if (!SHA256_HEX_PATTERN.test(expectedSha256)) {
|
|
15532
15634
|
throw new Error("'--expected-sha256' must be a lowercase 64-character SHA-256");
|
|
15533
15635
|
}
|
|
15534
|
-
const actualDigest =
|
|
15636
|
+
const actualDigest = createHash6("sha256").update(bytes).digest();
|
|
15535
15637
|
const expectedDigest = Buffer.from(expectedSha256, "hex");
|
|
15536
15638
|
if (!timingSafeEqual(actualDigest, expectedDigest)) {
|
|
15537
15639
|
throw new Error("Prebundled file SHA-256 does not match --expected-sha256");
|
|
@@ -16566,13 +16668,13 @@ ${JSON.stringify(r.data, null, 2)}` : `❌ Failed (${r.status}): ${JSON.stringif
|
|
|
16566
16668
|
}
|
|
16567
16669
|
|
|
16568
16670
|
// src/shared/tools/frontend-release-control.ts
|
|
16569
|
-
import { createHash as
|
|
16671
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
16570
16672
|
import { constants as fsConstants2 } from "node:fs";
|
|
16571
16673
|
import { open as open2 } from "node:fs/promises";
|
|
16572
16674
|
import { resolve as resolve8 } from "node:path";
|
|
16573
16675
|
|
|
16574
16676
|
// src/shared/mutation-protocol.ts
|
|
16575
|
-
import { createHash as
|
|
16677
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
16576
16678
|
var MUTATION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
16577
16679
|
var FINGERPRINT_PATTERN = /^[0-9a-f]{64}$/;
|
|
16578
16680
|
var OPERATION_PATTERN = /^[a-z0-9][a-z0-9._:-]{0,127}$/;
|
|
@@ -16651,7 +16753,7 @@ function stableStringifyNode(jsonValue) {
|
|
|
16651
16753
|
}
|
|
16652
16754
|
function mutationRequestFingerprint(normalizedRequest) {
|
|
16653
16755
|
assertCanonicalJsonNode(normalizedRequest, new WeakSet, 0);
|
|
16654
|
-
return
|
|
16756
|
+
return createHash7("sha256").update(stableStringifyNode(normalizedRequest)).digest("hex");
|
|
16655
16757
|
}
|
|
16656
16758
|
function objectRecord3(candidate) {
|
|
16657
16759
|
return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
|
|
@@ -16817,6 +16919,8 @@ var TIMESTAMP_PATTERN2 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
|
|
|
16817
16919
|
var ARCHIVE_MAX_BYTES = 100 * 1024 * 1024;
|
|
16818
16920
|
var ARCHIVE_CHUNK_BYTES = 64 * 1024;
|
|
16819
16921
|
var RESPONSE_MAX_BYTES = 1024 * 1024;
|
|
16922
|
+
var LIVE_RESPONSE_MAX_BYTES = 1024 * 1024;
|
|
16923
|
+
var LIVE_CHECK_TIMEOUT_MS = 1e4;
|
|
16820
16924
|
var UPLOAD_REQUEST_TIMEOUT_MS = 10 * 60000;
|
|
16821
16925
|
var MUTATION_RESPONSE_TIMEOUT_MS = 5000;
|
|
16822
16926
|
var RELEASE_LIST_LIMIT_MAX = 100;
|
|
@@ -16832,7 +16936,7 @@ function releaseFailure(operation, code, status) {
|
|
|
16832
16936
|
}]
|
|
16833
16937
|
};
|
|
16834
16938
|
}
|
|
16835
|
-
function
|
|
16939
|
+
function exactKeys2(candidate, keys) {
|
|
16836
16940
|
const actual = Object.keys(candidate).sort();
|
|
16837
16941
|
const expected = [...keys].sort();
|
|
16838
16942
|
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
@@ -16854,7 +16958,7 @@ function releaseRecord(candidate) {
|
|
|
16854
16958
|
"kind",
|
|
16855
16959
|
"archive_format"
|
|
16856
16960
|
];
|
|
16857
|
-
if (!
|
|
16961
|
+
if (!exactKeys2(record, keys) || record.schema !== FRONTEND_ARCHIVE_RELEASE_SCHEMA || record.archive_format !== FRONTEND_ARCHIVE_FORMAT || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || typeof record.release_id !== "string" || !RELEASE_ID_PATTERN.test(record.release_id) || record.sha256 !== record.release_id || typeof record.tree_sha256 !== "string" || !RELEASE_ID_PATTERN.test(record.tree_sha256) || !Number.isSafeInteger(record.size_bytes) || Number(record.size_bytes) < 1 || !Number.isSafeInteger(record.file_count) || Number(record.file_count) < 1 || !canonicalTimestamp2(record.created_at) || record.kind !== "prebuilt_static")
|
|
16858
16962
|
return null;
|
|
16859
16963
|
return {
|
|
16860
16964
|
project_ref: record.project_ref,
|
|
@@ -16873,7 +16977,7 @@ function releaseEnvelope(candidate, expected) {
|
|
|
16873
16977
|
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
|
|
16874
16978
|
return null;
|
|
16875
16979
|
const envelope = candidate;
|
|
16876
|
-
const release =
|
|
16980
|
+
const release = exactKeys2(envelope, ["project_ref", "deployment_id", "release"]) ? releaseRecord(envelope.release) : null;
|
|
16877
16981
|
if (!release || envelope.project_ref !== expected.projectRef || envelope.deployment_id !== expected.deploymentId || release.project_ref !== expected.projectRef || release.deployment_id !== expected.deploymentId || release.release_id !== expected.releaseId)
|
|
16878
16982
|
return null;
|
|
16879
16983
|
return release;
|
|
@@ -16901,7 +17005,7 @@ function releaseInventory(candidate, expected) {
|
|
|
16901
17005
|
"releases",
|
|
16902
17006
|
"next_cursor"
|
|
16903
17007
|
];
|
|
16904
|
-
if (!
|
|
17008
|
+
if (!exactKeys2(record, keys) || typeof record.project_ref !== "string" || !PROJECT_REF_PATTERN3.test(record.project_ref) || typeof record.deployment_id !== "string" || !DEPLOYMENT_ID_PATTERN.test(record.deployment_id) || !Array.isArray(record.releases))
|
|
16905
17009
|
return null;
|
|
16906
17010
|
const activeReleaseId = nullableIdentity(record.active_release_id, RELEASE_ID_PATTERN);
|
|
16907
17011
|
const activeActivationId = nullableIdentity(record.active_activation_id, MUTATION_ID_PATTERN2);
|
|
@@ -16940,7 +17044,7 @@ function sameArchiveIdentity(left, right) {
|
|
|
16940
17044
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
16941
17045
|
}
|
|
16942
17046
|
async function archiveSha256(handle, sizeBytes) {
|
|
16943
|
-
const hash =
|
|
17047
|
+
const hash = createHash8("sha256");
|
|
16944
17048
|
const chunk = new Uint8Array(Math.min(sizeBytes, ARCHIVE_CHUNK_BYTES));
|
|
16945
17049
|
for (let offset = 0;offset < sizeBytes; ) {
|
|
16946
17050
|
const requested = Math.min(chunk.byteLength, sizeBytes - offset);
|
|
@@ -17075,7 +17179,7 @@ function stableJson(candidate) {
|
|
|
17075
17179
|
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
|
|
17076
17180
|
}
|
|
17077
17181
|
function activationFingerprint(identity) {
|
|
17078
|
-
return
|
|
17182
|
+
return createHash8("sha256").update(stableJson({
|
|
17079
17183
|
project_ref: identity.projectRef,
|
|
17080
17184
|
deployment_id: identity.deploymentId,
|
|
17081
17185
|
release_id: identity.releaseId,
|
|
@@ -17092,7 +17196,7 @@ function publicMutationReceipt(candidate, expected) {
|
|
|
17092
17196
|
return null;
|
|
17093
17197
|
const envelope = candidate;
|
|
17094
17198
|
const mutation = safeMutationStatus(envelope.mutation);
|
|
17095
|
-
if (!mutation || !
|
|
17199
|
+
if (!mutation || !exactKeys2(envelope, ["project_ref", "mutation"]) || envelope.project_ref !== expected.projectRef || mutation.project_ref !== expected.projectRef || mutation.mutation_id !== expected.mutationId || mutation.resource_key !== expected.resourceKey || mutation.request_fingerprint !== expected.requestFingerprint)
|
|
17096
17200
|
return null;
|
|
17097
17201
|
return {
|
|
17098
17202
|
operation: mutation.operation,
|
|
@@ -17107,14 +17211,14 @@ function activationReceipt(candidate, input) {
|
|
|
17107
17211
|
const record = candidate;
|
|
17108
17212
|
const release = releaseRecord(record.release);
|
|
17109
17213
|
const mutation = record.mutation;
|
|
17110
|
-
if (!release || !mutation || !
|
|
17214
|
+
if (!release || !mutation || !exactKeys2(record, [
|
|
17111
17215
|
"project_ref",
|
|
17112
17216
|
"deployment_id",
|
|
17113
17217
|
"active_release_id",
|
|
17114
17218
|
"activation_id",
|
|
17115
17219
|
"release",
|
|
17116
17220
|
"mutation"
|
|
17117
|
-
]) || !
|
|
17221
|
+
]) || !exactKeys2(mutation, ["mutation_id", "status", "replayed"]) || record.project_ref !== input.projectRef || record.deployment_id !== input.deploymentId || record.active_release_id !== input.releaseId || record.activation_id !== input.mutationId || release.project_ref !== input.projectRef || release.deployment_id !== input.deploymentId || release.release_id !== input.releaseId || mutation.mutation_id !== input.mutationId || mutation.status !== "succeeded" || typeof mutation.replayed !== "boolean")
|
|
17118
17222
|
return null;
|
|
17119
17223
|
return release;
|
|
17120
17224
|
}
|
|
@@ -17174,6 +17278,129 @@ async function getActiveFrontendRelease(http, projectRef, deploymentId) {
|
|
|
17174
17278
|
const snapshot = await readActiveFrontendRelease(http, projectRef, deploymentId);
|
|
17175
17279
|
return "failure" in snapshot ? snapshot.failure : toolResponse(snapshot.inventory);
|
|
17176
17280
|
}
|
|
17281
|
+
function deploymentLiveUrl(candidate) {
|
|
17282
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
|
|
17283
|
+
return null;
|
|
17284
|
+
const value = candidate.deployment_url;
|
|
17285
|
+
if (typeof value !== "string" || !value.trim())
|
|
17286
|
+
return null;
|
|
17287
|
+
try {
|
|
17288
|
+
const url = new URL(value.trim());
|
|
17289
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.toString() : null;
|
|
17290
|
+
} catch {
|
|
17291
|
+
return null;
|
|
17292
|
+
}
|
|
17293
|
+
}
|
|
17294
|
+
async function verifyLiveDeployment(url) {
|
|
17295
|
+
try {
|
|
17296
|
+
const response = await fetch(url, {
|
|
17297
|
+
method: "GET",
|
|
17298
|
+
redirect: "follow",
|
|
17299
|
+
cache: "no-store",
|
|
17300
|
+
signal: AbortSignal.timeout(LIVE_CHECK_TIMEOUT_MS)
|
|
17301
|
+
});
|
|
17302
|
+
const contentLength = Number(response.headers.get("content-length") ?? "");
|
|
17303
|
+
if (Number.isFinite(contentLength) && contentLength > LIVE_RESPONSE_MAX_BYTES) {
|
|
17304
|
+
response.body?.cancel().catch(() => {
|
|
17305
|
+
return;
|
|
17306
|
+
});
|
|
17307
|
+
return {
|
|
17308
|
+
ok: false,
|
|
17309
|
+
status: response.status,
|
|
17310
|
+
content_type: response.headers.get("content-type"),
|
|
17311
|
+
bytes: contentLength,
|
|
17312
|
+
error_code: "LIVE_RESPONSE_TOO_LARGE"
|
|
17313
|
+
};
|
|
17314
|
+
}
|
|
17315
|
+
let bytes = 0;
|
|
17316
|
+
const reader = response.body?.getReader();
|
|
17317
|
+
if (reader) {
|
|
17318
|
+
try {
|
|
17319
|
+
while (true) {
|
|
17320
|
+
const { done, value } = await reader.read();
|
|
17321
|
+
if (done)
|
|
17322
|
+
break;
|
|
17323
|
+
bytes += value.byteLength;
|
|
17324
|
+
if (bytes > LIVE_RESPONSE_MAX_BYTES) {
|
|
17325
|
+
reader.cancel().catch(() => {
|
|
17326
|
+
return;
|
|
17327
|
+
});
|
|
17328
|
+
break;
|
|
17329
|
+
}
|
|
17330
|
+
}
|
|
17331
|
+
} finally {
|
|
17332
|
+
reader.releaseLock();
|
|
17333
|
+
}
|
|
17334
|
+
}
|
|
17335
|
+
const ok = response.status === 200 && bytes > 0 && bytes <= LIVE_RESPONSE_MAX_BYTES;
|
|
17336
|
+
return {
|
|
17337
|
+
ok,
|
|
17338
|
+
status: response.status,
|
|
17339
|
+
content_type: response.headers.get("content-type"),
|
|
17340
|
+
bytes,
|
|
17341
|
+
error_code: bytes > LIVE_RESPONSE_MAX_BYTES ? "LIVE_RESPONSE_TOO_LARGE" : ok ? null : bytes === 0 ? "LIVE_RESPONSE_EMPTY" : "LIVE_RESPONSE_INVALID"
|
|
17342
|
+
};
|
|
17343
|
+
} catch (error) {
|
|
17344
|
+
const timedOut = error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError");
|
|
17345
|
+
return {
|
|
17346
|
+
ok: false,
|
|
17347
|
+
status: 0,
|
|
17348
|
+
content_type: null,
|
|
17349
|
+
bytes: 0,
|
|
17350
|
+
error_code: timedOut ? "LIVE_CHECK_TIMEOUT" : "LIVE_CHECK_FAILED"
|
|
17351
|
+
};
|
|
17352
|
+
}
|
|
17353
|
+
}
|
|
17354
|
+
async function verifyFrontendRelease(http, projectRef, deploymentId) {
|
|
17355
|
+
const snapshot = await readActiveFrontendRelease(http, projectRef, deploymentId);
|
|
17356
|
+
if ("failure" in snapshot)
|
|
17357
|
+
return snapshot.failure;
|
|
17358
|
+
const inventory = snapshot.inventory;
|
|
17359
|
+
const release = inventory.releases[0];
|
|
17360
|
+
if (!release)
|
|
17361
|
+
return releaseFailure("frontend.verify_release", "NO_ACTIVE_RELEASE", 200);
|
|
17362
|
+
const deployment = await http.get(deploymentEndpoint(projectRef, deploymentId), {
|
|
17363
|
+
maxJsonBytes: RESPONSE_MAX_BYTES
|
|
17364
|
+
});
|
|
17365
|
+
const liveUrl = deployment.ok && deployment.status === 200 ? deploymentLiveUrl(deployment.data) : null;
|
|
17366
|
+
const live = liveUrl ? await verifyLiveDeployment(liveUrl) : { ok: false, status: 0, content_type: null, bytes: 0, error_code: "LIVE_URL_UNAVAILABLE" };
|
|
17367
|
+
let errorCode = live.error_code;
|
|
17368
|
+
if (live.ok) {
|
|
17369
|
+
try {
|
|
17370
|
+
const readback = await readActiveFrontendRelease(http, projectRef, deploymentId);
|
|
17371
|
+
if ("failure" in readback)
|
|
17372
|
+
errorCode = "ACTIVE_RELEASE_READBACK_FAILED";
|
|
17373
|
+
else if (readback.inventory.active_release_id !== inventory.active_release_id || readback.inventory.active_activation_id !== inventory.active_activation_id) {
|
|
17374
|
+
errorCode = "STALE_AUTHORITY";
|
|
17375
|
+
}
|
|
17376
|
+
} catch {
|
|
17377
|
+
errorCode = "ACTIVE_RELEASE_READBACK_FAILED";
|
|
17378
|
+
}
|
|
17379
|
+
}
|
|
17380
|
+
const ok = errorCode === null;
|
|
17381
|
+
const receipt = {
|
|
17382
|
+
schema: "supacloud.frontend-release-verification.v1",
|
|
17383
|
+
ok,
|
|
17384
|
+
error_code: errorCode,
|
|
17385
|
+
project_ref: projectRef,
|
|
17386
|
+
deployment_id: deploymentId,
|
|
17387
|
+
active_release_id: inventory.active_release_id,
|
|
17388
|
+
active_activation_id: inventory.active_activation_id,
|
|
17389
|
+
release,
|
|
17390
|
+
live: {
|
|
17391
|
+
url: liveUrl,
|
|
17392
|
+
status: live.status,
|
|
17393
|
+
content_type: live.content_type,
|
|
17394
|
+
bytes: live.bytes,
|
|
17395
|
+
error_code: live.error_code
|
|
17396
|
+
},
|
|
17397
|
+
checked_at: new Date().toISOString()
|
|
17398
|
+
};
|
|
17399
|
+
return {
|
|
17400
|
+
content: [{ type: "text", text: JSON.stringify(receipt, null, 2) }],
|
|
17401
|
+
...ok ? {} : { isError: true }
|
|
17402
|
+
};
|
|
17403
|
+
}
|
|
17177
17404
|
async function publishFrontendRelease(http, projectRef, deploymentId, archiveSource, expectedTreeSha256) {
|
|
17178
17405
|
const authority = await readActiveFrontendRelease(http, projectRef, deploymentId);
|
|
17179
17406
|
if ("failure" in authority)
|
|
@@ -17371,7 +17598,7 @@ async function readPreviousFrontendRelease(http, projectRef, deploymentId) {
|
|
|
17371
17598
|
const activeActivationId = value ? nullableIdentity(value.active_activation_id, MUTATION_ID_PATTERN2) : undefined;
|
|
17372
17599
|
const previousActivationId = value ? nullableIdentity(value.previous_activation_id, MUTATION_ID_PATTERN2) : undefined;
|
|
17373
17600
|
const previous = value?.previous_release === null ? null : releaseRecord(value?.previous_release);
|
|
17374
|
-
if (!response.ok || response.status !== 200 || !value || !
|
|
17601
|
+
if (!response.ok || response.status !== 200 || !value || !exactKeys2(value, [
|
|
17375
17602
|
"schema",
|
|
17376
17603
|
"project_ref",
|
|
17377
17604
|
"deployment_id",
|
|
@@ -17463,7 +17690,7 @@ async function activationReadback(http, input, mutationId, activationStatus) {
|
|
|
17463
17690
|
// src/shared/tools/frontend-tools.ts
|
|
17464
17691
|
function registerFrontendTools(server, http) {
|
|
17465
17692
|
registerTool(server, "frontend", `Frontend hosting and immutable prebuilt releases. Supports: static, react, vue, svelte, sveltekit, sveltekit-static, nextjs, nuxt, astro.
|
|
17466
|
-
Actions: list, get, create, update, delete, build_logs, add_domain, remove_domain, set_env, list_frameworks, list_records, list_releases, get_active_release, get_release, rollback. Publish with supacloud deploy.`, {
|
|
17693
|
+
Actions: list, get, create, update, delete, build_logs, add_domain, remove_domain, set_env, list_frameworks, list_records, list_releases, get_active_release, get_release, verify_release, rollback. Publish with supacloud deploy.`, {
|
|
17467
17694
|
action: withDescription(stringEnum2([
|
|
17468
17695
|
"list",
|
|
17469
17696
|
"get",
|
|
@@ -17479,6 +17706,7 @@ Actions: list, get, create, update, delete, build_logs, add_domain, remove_domai
|
|
|
17479
17706
|
"list_releases",
|
|
17480
17707
|
"get_active_release",
|
|
17481
17708
|
"get_release",
|
|
17709
|
+
"verify_release",
|
|
17482
17710
|
"rollback"
|
|
17483
17711
|
]), "Action"),
|
|
17484
17712
|
ref: optional(String2(), "Project ref"),
|
|
@@ -17609,6 +17837,10 @@ Actions: list, get, create, update, delete, build_logs, add_domain, remove_domai
|
|
|
17609
17837
|
need("ref", ref);
|
|
17610
17838
|
need("id", id);
|
|
17611
17839
|
return getActiveFrontendRelease(http, ref, id);
|
|
17840
|
+
case "verify_release":
|
|
17841
|
+
need("ref", ref);
|
|
17842
|
+
need("id", id);
|
|
17843
|
+
return verifyFrontendRelease(http, ref, id);
|
|
17612
17844
|
case "rollback":
|
|
17613
17845
|
need("ref", ref);
|
|
17614
17846
|
need("id", id);
|
|
@@ -20569,8 +20801,23 @@ var VALID_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
|
20569
20801
|
var VALID_MIGRATION_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]{0,100}$/;
|
|
20570
20802
|
var VALID_SCHEMA = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
20571
20803
|
var VALID_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
|
|
20804
|
+
var OFFICIAL_CLI_PG_TLS_ENV = new Set([
|
|
20805
|
+
"PGSSLMODE",
|
|
20806
|
+
"PGSSLROOTCERT",
|
|
20807
|
+
"PGSSLCERT",
|
|
20808
|
+
"PGSSLKEY",
|
|
20809
|
+
"PGSSLCRL",
|
|
20810
|
+
"PGSSLCRLDIR",
|
|
20811
|
+
"PGSSLSNI",
|
|
20812
|
+
"PGSSLNEGOTIATION",
|
|
20813
|
+
"PGSSLCOMPRESSION",
|
|
20814
|
+
"PGREQUIRESSL"
|
|
20815
|
+
]);
|
|
20572
20816
|
function isSensitiveEnvironmentKey(key) {
|
|
20573
|
-
|
|
20817
|
+
const upper = key.toUpperCase();
|
|
20818
|
+
if (OFFICIAL_CLI_PG_TLS_ENV.has(upper))
|
|
20819
|
+
return false;
|
|
20820
|
+
return upper.startsWith("PG") || SENSITIVE_ENV_KEY.test(upper);
|
|
20574
20821
|
}
|
|
20575
20822
|
function requireMigrationName(name) {
|
|
20576
20823
|
if (!name || !VALID_MIGRATION_NAME.test(name)) {
|
|
@@ -20857,7 +21104,7 @@ function resolveOfficialSupabaseCommand(workdir, environment = process.env) {
|
|
|
20857
21104
|
const version = environment.SUPABASE_CLI_VERSION?.trim();
|
|
20858
21105
|
if (version) {
|
|
20859
21106
|
if (!VALID_VERSION.test(version))
|
|
20860
|
-
throw new Error("Invalid SUPABASE_CLI_VERSION; use an exact version such as 2.
|
|
21107
|
+
throw new Error("Invalid SUPABASE_CLI_VERSION; use an exact version such as 2.122.0");
|
|
20861
21108
|
if (process.versions.bun)
|
|
20862
21109
|
return [process.execPath, "x", `supabase@${version}`];
|
|
20863
21110
|
if (process.platform === "win32") {
|
|
@@ -21725,9 +21972,9 @@ var starter_metadata_default = {
|
|
|
21725
21972
|
}
|
|
21726
21973
|
},
|
|
21727
21974
|
sdk: {
|
|
21728
|
-
version: "0.45.
|
|
21975
|
+
version: "0.45.3",
|
|
21729
21976
|
peerDependencies: {
|
|
21730
|
-
"@supabase/supabase-js": "^2.
|
|
21977
|
+
"@supabase/supabase-js": "^2.118.0"
|
|
21731
21978
|
}
|
|
21732
21979
|
}
|
|
21733
21980
|
}
|
|
@@ -29565,7 +29812,7 @@ import {
|
|
|
29565
29812
|
} from "@supacloud/db";
|
|
29566
29813
|
|
|
29567
29814
|
// src/shared/tools/db-workflow.ts
|
|
29568
|
-
import { createHash as
|
|
29815
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
29569
29816
|
import { lstat as lstat4, mkdir as mkdir6, mkdtemp, readFile as readFile5, readdir as readdir6, rename as rename3, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
29570
29817
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
29571
29818
|
import { dirname as dirname11, isAbsolute as isAbsolute7, join as join15, relative as relative8, resolve as resolve20 } from "node:path";
|
|
@@ -29586,8 +29833,23 @@ var VALID_VERSION2 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
|
29586
29833
|
var VALID_MIGRATION_NAME2 = /^[A-Za-z0-9][A-Za-z0-9_-]{0,100}$/;
|
|
29587
29834
|
var VALID_SCHEMA2 = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
29588
29835
|
var VALID_PROJECT_REF2 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
29836
|
+
var OFFICIAL_CLI_PG_TLS_ENV2 = new Set([
|
|
29837
|
+
"PGSSLMODE",
|
|
29838
|
+
"PGSSLROOTCERT",
|
|
29839
|
+
"PGSSLCERT",
|
|
29840
|
+
"PGSSLKEY",
|
|
29841
|
+
"PGSSLCRL",
|
|
29842
|
+
"PGSSLCRLDIR",
|
|
29843
|
+
"PGSSLSNI",
|
|
29844
|
+
"PGSSLNEGOTIATION",
|
|
29845
|
+
"PGSSLCOMPRESSION",
|
|
29846
|
+
"PGREQUIRESSL"
|
|
29847
|
+
]);
|
|
29589
29848
|
function isSensitiveEnvironmentKey2(key) {
|
|
29590
|
-
|
|
29849
|
+
const upper = key.toUpperCase();
|
|
29850
|
+
if (OFFICIAL_CLI_PG_TLS_ENV2.has(upper))
|
|
29851
|
+
return false;
|
|
29852
|
+
return upper.startsWith("PG") || SENSITIVE_ENV_KEY2.test(upper);
|
|
29591
29853
|
}
|
|
29592
29854
|
function requireMigrationName2(name) {
|
|
29593
29855
|
if (!name || !VALID_MIGRATION_NAME2.test(name)) {
|
|
@@ -29839,7 +30101,7 @@ function resolveOfficialSupabaseCommand2(workdir, environment = process.env) {
|
|
|
29839
30101
|
const version = environment.SUPABASE_CLI_VERSION?.trim();
|
|
29840
30102
|
if (version) {
|
|
29841
30103
|
if (!VALID_VERSION2.test(version))
|
|
29842
|
-
throw new Error("Invalid SUPABASE_CLI_VERSION; use an exact version such as 2.
|
|
30104
|
+
throw new Error("Invalid SUPABASE_CLI_VERSION; use an exact version such as 2.122.0");
|
|
29843
30105
|
if (process.versions.bun)
|
|
29844
30106
|
return [process.execPath, "x", `supabase@${version}`];
|
|
29845
30107
|
if (process.platform === "win32") {
|
|
@@ -29854,7 +30116,7 @@ function resolveOfficialSupabaseCommand2(workdir, environment = process.env) {
|
|
|
29854
30116
|
}
|
|
29855
30117
|
|
|
29856
30118
|
// src/shared/tools/db-workflow.ts
|
|
29857
|
-
var hash4 = (content) =>
|
|
30119
|
+
var hash4 = (content) => createHash9("sha256").update(content).digest("hex");
|
|
29858
30120
|
var result = (value, isError = false) => ({
|
|
29859
30121
|
isError,
|
|
29860
30122
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
@@ -30958,13 +31220,13 @@ var MUTATION_TOOL_SCHEMA = {
|
|
|
30958
31220
|
};
|
|
30959
31221
|
|
|
30960
31222
|
// src/shared/tools/release-tools.ts
|
|
30961
|
-
import { createHash as
|
|
31223
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
30962
31224
|
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
|
|
30963
31225
|
import { dirname as dirname12, join as join17, relative as relative9, resolve as resolve23 } from "node:path";
|
|
30964
31226
|
var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
|
|
30965
31227
|
var SAFE_ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
30966
31228
|
var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
|
|
30967
|
-
var
|
|
31229
|
+
var SHA2562 = /^[a-f0-9]{64}$/;
|
|
30968
31230
|
var SAFE_DATABASE = /^[^\u0000-\u001f\u007f]{1,128}$/;
|
|
30969
31231
|
var INVENTORY_MAX_BYTES = 1024 * 1024;
|
|
30970
31232
|
var MUTATION_MAX_BYTES = 64 * 1024;
|
|
@@ -30994,7 +31256,7 @@ function backupBelongsToProject(backupId, projectRef) {
|
|
|
30994
31256
|
return BACKUP_ID.test(backupId) && backupId.startsWith(prefix) && /^[a-f0-9]{32}$/.test(backupId.slice(prefix.length));
|
|
30995
31257
|
}
|
|
30996
31258
|
function verifiedBackup(value, projectRef) {
|
|
30997
|
-
if (!isRecord6(value) || typeof value.backup_id !== "string" || !backupBelongsToProject(value.backup_id, projectRef) || value.project_ref !== projectRef || typeof value.database !== "string" || !SAFE_DATABASE.test(value.database) || value.kind !== "logical-full" || !canonicalTimestamp4(value.created_at) || !canonicalTimestamp4(value.completed_at) || new Date(value.completed_at).valueOf() < new Date(value.created_at).valueOf() || typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || typeof value.sha256 !== "string" || !
|
|
31259
|
+
if (!isRecord6(value) || typeof value.backup_id !== "string" || !backupBelongsToProject(value.backup_id, projectRef) || value.project_ref !== projectRef || typeof value.database !== "string" || !SAFE_DATABASE.test(value.database) || value.kind !== "logical-full" || !canonicalTimestamp4(value.created_at) || !canonicalTimestamp4(value.completed_at) || new Date(value.completed_at).valueOf() < new Date(value.created_at).valueOf() || typeof value.bytes !== "number" || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || typeof value.sha256 !== "string" || !SHA2562.test(value.sha256))
|
|
30998
31260
|
return null;
|
|
30999
31261
|
return {
|
|
31000
31262
|
backup_id: value.backup_id,
|
|
@@ -31040,7 +31302,7 @@ function restoreRequest(projectRef, backupId, expectedSha256, restoreConfirmatio
|
|
|
31040
31302
|
if (typeof backupId !== "string" || !backupBelongsToProject(backupId, projectRef)) {
|
|
31041
31303
|
throw new Error("'backup_id' must identify a logical-full backup for 'ref'");
|
|
31042
31304
|
}
|
|
31043
|
-
if (typeof expectedSha256 !== "string" || !
|
|
31305
|
+
if (typeof expectedSha256 !== "string" || !SHA2562.test(expectedSha256)) {
|
|
31044
31306
|
throw new Error("'expected_sha256' must be a lowercase SHA-256 digest");
|
|
31045
31307
|
}
|
|
31046
31308
|
const confirmation = `RESTORE_PROJECT:${projectRef}:${backupId}:${expectedSha256}`;
|
|
@@ -31201,7 +31463,7 @@ function genericScopedReleaseScopeJson(scope) {
|
|
|
31201
31463
|
return JSON.stringify(canonicalValue(scope));
|
|
31202
31464
|
}
|
|
31203
31465
|
function genericScopedReleaseScopeSha256(scope) {
|
|
31204
|
-
return
|
|
31466
|
+
return createHash10("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
|
|
31205
31467
|
}
|
|
31206
31468
|
function scopeEnvironmentId(scope, action) {
|
|
31207
31469
|
const environmentId = isRecord6(scope) ? scope.targetEnvironmentId : undefined;
|
|
@@ -31470,6 +31732,73 @@ function handleScopeInspect(args) {
|
|
|
31470
31732
|
}
|
|
31471
31733
|
});
|
|
31472
31734
|
}
|
|
31735
|
+
function releaseComponentView(component) {
|
|
31736
|
+
if (component.kind === "migration")
|
|
31737
|
+
return { kind: component.kind, name: component.name };
|
|
31738
|
+
if (component.kind === "function") {
|
|
31739
|
+
return { kind: component.kind, slug: component.slug, artifact_sha256: component.artifactSha256 };
|
|
31740
|
+
}
|
|
31741
|
+
return { kind: component.kind, artifact_sha256: component.artifactSha256 };
|
|
31742
|
+
}
|
|
31743
|
+
function handleContractPlan(args, fallbackRef) {
|
|
31744
|
+
const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve23(args.cwd.trim()) : process.cwd();
|
|
31745
|
+
const requestedRef = typeof args.ref === "string" && args.ref.trim() || fallbackRef;
|
|
31746
|
+
if (!requestedRef || !validProjectRef(requestedRef)) {
|
|
31747
|
+
throw new Error("'ref' is required for contract_plan and must identify one project");
|
|
31748
|
+
}
|
|
31749
|
+
const requestedEnvironmentId = requestedScopeEnvironment(args, "contract_plan");
|
|
31750
|
+
const file = typeof args.file === "string" && args.file.trim() ? args.file.trim() : "supacloud.release.json";
|
|
31751
|
+
const filePath = resolve23(cwd, file);
|
|
31752
|
+
if (!existsSync14(filePath)) {
|
|
31753
|
+
throw new Error(`Release contract not found: ${file}`);
|
|
31754
|
+
}
|
|
31755
|
+
let parsedValue;
|
|
31756
|
+
try {
|
|
31757
|
+
parsedValue = JSON.parse(readFileSync11(filePath, "utf8"));
|
|
31758
|
+
} catch {
|
|
31759
|
+
throw new Error(`Release contract '${file}' is not valid JSON`);
|
|
31760
|
+
}
|
|
31761
|
+
const scope = parseReleaseScope(parsedValue);
|
|
31762
|
+
if (scope.targetProjectRef !== requestedRef) {
|
|
31763
|
+
throw new Error(`Release contract target project '${scope.targetProjectRef}' does not match requested project '${requestedRef}'`);
|
|
31764
|
+
}
|
|
31765
|
+
if (scope.targetEnvironmentId !== requestedEnvironmentId) {
|
|
31766
|
+
throw new Error(`Release contract target environment '${scope.targetEnvironmentId}' does not match requested environment '${requestedEnvironmentId}'`);
|
|
31767
|
+
}
|
|
31768
|
+
const git = gitCommitInfo(cwd);
|
|
31769
|
+
const sourceMatchesHead = git.head !== null && scope.sourceCommit === git.head;
|
|
31770
|
+
const sourceMatchesOriginMain = git.originMain !== null && scope.sourceCommit === git.originMain;
|
|
31771
|
+
if (args.require_current_source === true && !sourceMatchesHead) {
|
|
31772
|
+
throw new Error(`Release contract source commit '${scope.sourceCommit}' does not match current HEAD '${git.head ?? "unavailable"}'`);
|
|
31773
|
+
}
|
|
31774
|
+
const counts = scope.components.reduce((result, component) => {
|
|
31775
|
+
result[component.kind] += 1;
|
|
31776
|
+
return result;
|
|
31777
|
+
}, { migration: 0, function: 0, web: 0 });
|
|
31778
|
+
return releaseControlSuccess("release.contract_plan", {
|
|
31779
|
+
plan_schema: "supacloud.release-plan.v1",
|
|
31780
|
+
file: relative9(cwd, filePath),
|
|
31781
|
+
release_id: scope.releaseId,
|
|
31782
|
+
scope_sha256: releaseScopeSha256(scope),
|
|
31783
|
+
source_commit: scope.sourceCommit,
|
|
31784
|
+
target_project_ref: scope.targetProjectRef,
|
|
31785
|
+
target_environment_id: scope.targetEnvironmentId,
|
|
31786
|
+
components: scope.components.map(releaseComponentView),
|
|
31787
|
+
component_counts: counts,
|
|
31788
|
+
checks: {
|
|
31789
|
+
source_commit_matches_head: sourceMatchesHead,
|
|
31790
|
+
source_commit_matches_origin_main: sourceMatchesOriginMain,
|
|
31791
|
+
target_project_ref_matches_requested: true,
|
|
31792
|
+
target_environment_id_matches_requested: true,
|
|
31793
|
+
components_unique_and_sorted: true
|
|
31794
|
+
},
|
|
31795
|
+
current_head: git.head,
|
|
31796
|
+
origin_main: git.originMain,
|
|
31797
|
+
source_is_current: sourceMatchesHead,
|
|
31798
|
+
read_only: true,
|
|
31799
|
+
mutations_performed: false
|
|
31800
|
+
});
|
|
31801
|
+
}
|
|
31473
31802
|
function parseCommaList(value) {
|
|
31474
31803
|
if (!value)
|
|
31475
31804
|
return [];
|
|
@@ -31526,7 +31855,7 @@ function handleScopeCreate(args, fallbackRef) {
|
|
|
31526
31855
|
});
|
|
31527
31856
|
}
|
|
31528
31857
|
function registerReleaseTools(server, http, options = {}) {
|
|
31529
|
-
const localActions = ["scope_inspect", "scope_rebind", "scope_create"];
|
|
31858
|
+
const localActions = ["scope_inspect", "scope_rebind", "scope_create", "contract_plan"];
|
|
31530
31859
|
const remoteActions = [
|
|
31531
31860
|
"logical_backup_list",
|
|
31532
31861
|
"logical_backup_status",
|
|
@@ -31541,18 +31870,19 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
31541
31870
|
registerTool(server, "release", "Verified release controls. Scope management actions operate locally; management actions use the Management API; release canary stage/disable replay additionally require the selected project's SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.", {
|
|
31542
31871
|
action: withDescription(stringEnum2(allActions), "Release control action"),
|
|
31543
31872
|
ref: optional(String2(), options.projectRef ? "Optional override when not auto-linked" : "Project ref"),
|
|
31544
|
-
file: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Path to release scope JSON file"),
|
|
31873
|
+
file: optional(String2(), "[scope_inspect/scope_rebind/scope_create/contract_plan] Path to release scope or release contract JSON file"),
|
|
31545
31874
|
files: optional(String2(), "[scope_rebind] Comma-separated list of release scope JSON files"),
|
|
31546
31875
|
dir: optional(String2(), "[scope_rebind] Directory containing release scope JSON files (defaults to supacloud/fa/release-scopes, release-scopes, or scopes)"),
|
|
31547
31876
|
task: optional(String2(), "[scope_rebind/scope_create] Task ID or prefix filter for scope files"),
|
|
31548
31877
|
base_commit: optional(String2(), "[scope_rebind/scope_create] Target 40-character base commit SHA or git ref (defaults to origin/main -> main -> HEAD^ -> HEAD)"),
|
|
31549
|
-
environment_id: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Exact target deployment environment"),
|
|
31878
|
+
environment_id: optional(String2(), "[scope_inspect/scope_rebind/scope_create/contract_plan] Exact target deployment environment"),
|
|
31550
31879
|
functions: optional(String2(), "[scope_create] Comma-separated list of functions to deploy"),
|
|
31551
31880
|
excluded_functions: optional(String2(), "[scope_create] Comma-separated list of functions to exclude"),
|
|
31552
31881
|
migrations: optional(String2(), "[scope_create] Comma-separated list of migrations to apply"),
|
|
31553
31882
|
web: optional(Boolean2(), "[scope_create] Whether web assets are included in release scope"),
|
|
31554
31883
|
dry_run: optional(Boolean2(), "[scope_rebind] Perform calculation and validation without writing files"),
|
|
31555
|
-
cwd: optional(String2(), "[scope_inspect/scope_rebind/scope_create] Base directory for relative file and git resolution"),
|
|
31884
|
+
cwd: optional(String2(), "[scope_inspect/scope_rebind/scope_create/contract_plan] Base directory for relative file and git resolution"),
|
|
31885
|
+
require_current_source: optional(Boolean2(), "[contract_plan] Fail unless the contract sourceCommit equals current HEAD"),
|
|
31556
31886
|
backup_id: optional(String2(), "[logical_backup_create/status/restore] Stable project-bound backup ID; create generates one when omitted"),
|
|
31557
31887
|
expected_sha256: optional(String2(), "[logical_backup_restore] Exact lowercase SHA-256 from the selected project inventory"),
|
|
31558
31888
|
restore_confirmation: optional(String2(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation"),
|
|
@@ -31572,6 +31902,9 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
31572
31902
|
if (action === "scope_create") {
|
|
31573
31903
|
return handleScopeCreate(args, options.projectRef);
|
|
31574
31904
|
}
|
|
31905
|
+
if (action === "contract_plan") {
|
|
31906
|
+
return handleContractPlan(args, options.projectRef);
|
|
31907
|
+
}
|
|
31575
31908
|
if (options.localOnly || !http) {
|
|
31576
31909
|
return {
|
|
31577
31910
|
isError: true,
|
|
@@ -31745,7 +32078,7 @@ function registerReleaseTools(server, http, options = {}) {
|
|
|
31745
32078
|
// src/shared/tools/deploy-tools.ts
|
|
31746
32079
|
import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile6, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
|
|
31747
32080
|
import { spawn as spawn2 } from "node:child_process";
|
|
31748
|
-
import { createHash as
|
|
32081
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
31749
32082
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
31750
32083
|
import { basename as basename8, dirname as dirname13, join as join18, relative as relative10, resolve as resolve24, sep as sep5 } from "node:path";
|
|
31751
32084
|
|
|
@@ -31841,7 +32174,7 @@ var deployToolSchema = {
|
|
|
31841
32174
|
status: optional(String2({ format: "uuid" }), "Read a backend deployment status without publishing"),
|
|
31842
32175
|
json: optional(Boolean2(), "Print only the final JSON result")
|
|
31843
32176
|
};
|
|
31844
|
-
function
|
|
32177
|
+
function record5(candidate) {
|
|
31845
32178
|
return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
|
|
31846
32179
|
}
|
|
31847
32180
|
function requiredString(candidate, field) {
|
|
@@ -31851,7 +32184,7 @@ function requiredString(candidate, field) {
|
|
|
31851
32184
|
return candidate.trim();
|
|
31852
32185
|
}
|
|
31853
32186
|
function frontendDeployment(candidate) {
|
|
31854
|
-
const value =
|
|
32187
|
+
const value = record5(candidate);
|
|
31855
32188
|
if (!value)
|
|
31856
32189
|
throw new Error("Invalid frontend deployment response");
|
|
31857
32190
|
return {
|
|
@@ -31921,7 +32254,7 @@ function optionalStringArray(value, field) {
|
|
|
31921
32254
|
});
|
|
31922
32255
|
}
|
|
31923
32256
|
function deployTargetConfig(candidate, name) {
|
|
31924
|
-
const value =
|
|
32257
|
+
const value = record5(candidate);
|
|
31925
32258
|
if (!value || value.type !== "frontend" && value.type !== "edge_function") {
|
|
31926
32259
|
throw new Error(`supacloud.json targets.${name}.type must be 'frontend' or 'edge_function'`);
|
|
31927
32260
|
}
|
|
@@ -31950,10 +32283,10 @@ async function readDeployConfig(configRoot) {
|
|
|
31950
32283
|
const candidate = await readJson(configPath);
|
|
31951
32284
|
if (candidate === null)
|
|
31952
32285
|
return {};
|
|
31953
|
-
const config =
|
|
31954
|
-
const frontend =
|
|
31955
|
-
const application =
|
|
31956
|
-
const targetValues =
|
|
32286
|
+
const config = record5(candidate);
|
|
32287
|
+
const frontend = record5(config?.frontend);
|
|
32288
|
+
const application = record5(config?.application);
|
|
32289
|
+
const targetValues = record5(config?.targets);
|
|
31957
32290
|
if (!config || config.frontend !== undefined && !frontend || config.application !== undefined && !application || config.targets !== undefined && !targetValues) {
|
|
31958
32291
|
throw new Error("supacloud.json must contain object-valued 'application', 'frontend' or 'targets' configuration");
|
|
31959
32292
|
}
|
|
@@ -31993,7 +32326,7 @@ async function readDeployConfig(configRoot) {
|
|
|
31993
32326
|
};
|
|
31994
32327
|
}
|
|
31995
32328
|
async function packageName(projectDirectory) {
|
|
31996
|
-
const candidate =
|
|
32329
|
+
const candidate = record5(await readJson(join18(projectDirectory, "package.json")));
|
|
31997
32330
|
return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
|
|
31998
32331
|
}
|
|
31999
32332
|
async function pathExists(path) {
|
|
@@ -32019,8 +32352,8 @@ async function hasProjectReleaseManifest(root) {
|
|
|
32019
32352
|
return false;
|
|
32020
32353
|
}
|
|
32021
32354
|
async function defaultBuildCommand(projectDirectory, configRoot) {
|
|
32022
|
-
const packageJson =
|
|
32023
|
-
const scripts =
|
|
32355
|
+
const packageJson = record5(await readJson(join18(projectDirectory, "package.json")));
|
|
32356
|
+
const scripts = record5(packageJson?.scripts);
|
|
32024
32357
|
if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
|
|
32025
32358
|
throw new Error("No build command was configured and package.json has no build script");
|
|
32026
32359
|
}
|
|
@@ -32042,7 +32375,7 @@ async function defaultBuildCommand(projectDirectory, configRoot) {
|
|
|
32042
32375
|
return "npm run build";
|
|
32043
32376
|
}
|
|
32044
32377
|
function deploymentList(candidate) {
|
|
32045
|
-
const values = Array.isArray(candidate) ? candidate : Array.isArray(
|
|
32378
|
+
const values = Array.isArray(candidate) ? candidate : Array.isArray(record5(candidate)?.deployments) ? record5(candidate)?.deployments : null;
|
|
32046
32379
|
if (!values)
|
|
32047
32380
|
throw new Error("Invalid frontend deployment list response");
|
|
32048
32381
|
return values.map(frontendDeployment);
|
|
@@ -32202,9 +32535,9 @@ async function collectArchiveFiles(root) {
|
|
|
32202
32535
|
throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
|
|
32203
32536
|
const archivePath = relative10(root, path).split(sep5).join("/");
|
|
32204
32537
|
files.set(archivePath, data);
|
|
32205
|
-
digests.push({ path: archivePath, size: data.byteLength, sha256:
|
|
32538
|
+
digests.push({ path: archivePath, size: data.byteLength, sha256: createHash11("sha256").update(data).digest("hex") });
|
|
32206
32539
|
}
|
|
32207
|
-
const tree =
|
|
32540
|
+
const tree = createHash11("sha256");
|
|
32208
32541
|
for (const file of digests.sort((left, right) => left.path.localeCompare(right.path))) {
|
|
32209
32542
|
const pathBytes = Buffer.from(file.path, "utf8");
|
|
32210
32543
|
const frame = Buffer.alloc(12);
|
|
@@ -32220,7 +32553,7 @@ async function createCollectedFrontendArchive(outputDirectory, collected) {
|
|
|
32220
32553
|
try {
|
|
32221
32554
|
const archiveBytes = await createFrontendTarZstd(collected.files);
|
|
32222
32555
|
await writeFile6(archivePath, archiveBytes);
|
|
32223
|
-
const sha256 =
|
|
32556
|
+
const sha256 = createHash11("sha256").update(archiveBytes).digest("hex");
|
|
32224
32557
|
return {
|
|
32225
32558
|
archivePath,
|
|
32226
32559
|
sha256,
|
|
@@ -32342,7 +32675,7 @@ function registerDeployTools(server, http, options = {}) {
|
|
|
32342
32675
|
import { existsSync as existsSync15 } from "node:fs";
|
|
32343
32676
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
32344
32677
|
import { readdir as readdir7 } from "node:fs/promises";
|
|
32345
|
-
import { createHash as
|
|
32678
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
32346
32679
|
import { join as join19, resolve as resolve25 } from "node:path";
|
|
32347
32680
|
import { compileProject as compileProject2 } from "@supacloud/compiler";
|
|
32348
32681
|
var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
|
|
@@ -32441,7 +32774,7 @@ async function sourceFingerprint(root) {
|
|
|
32441
32774
|
}
|
|
32442
32775
|
};
|
|
32443
32776
|
await visit(root);
|
|
32444
|
-
const hash =
|
|
32777
|
+
const hash = createHash12("sha256");
|
|
32445
32778
|
for (const file of files) {
|
|
32446
32779
|
hash.update(file.slice(root.length).replace(/\\/g, "/"));
|
|
32447
32780
|
hash.update(await readFile7(file));
|
|
@@ -32841,7 +33174,7 @@ function textResult3(text, isError = false) {
|
|
|
32841
33174
|
function jsonResult(value, isError = false) {
|
|
32842
33175
|
return textResult3(JSON.stringify(value, null, 2), isError);
|
|
32843
33176
|
}
|
|
32844
|
-
function
|
|
33177
|
+
function record6(value) {
|
|
32845
33178
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
32846
33179
|
}
|
|
32847
33180
|
function packageDependencies(manifest) {
|
|
@@ -32864,12 +33197,12 @@ async function readPackageManifest(root) {
|
|
|
32864
33197
|
return {};
|
|
32865
33198
|
try {
|
|
32866
33199
|
const parsed = JSON.parse(await readFile8(path, "utf8"));
|
|
32867
|
-
const value =
|
|
33200
|
+
const value = record6(parsed);
|
|
32868
33201
|
if (!value)
|
|
32869
33202
|
throw new Error("package.json must contain an object");
|
|
32870
|
-
const scripts =
|
|
32871
|
-
const dependencies =
|
|
32872
|
-
const devDependencies =
|
|
33203
|
+
const scripts = record6(value.scripts);
|
|
33204
|
+
const dependencies = record6(value.dependencies);
|
|
33205
|
+
const devDependencies = record6(value.devDependencies);
|
|
32873
33206
|
return {
|
|
32874
33207
|
scripts: scripts ?? undefined,
|
|
32875
33208
|
dependencies: dependencies ?? undefined,
|
|
@@ -33027,9 +33360,9 @@ async function inspectProfile(profile) {
|
|
|
33027
33360
|
return "{}";
|
|
33028
33361
|
throw error;
|
|
33029
33362
|
});
|
|
33030
|
-
const config =
|
|
33031
|
-
const hasFrontend =
|
|
33032
|
-
const deploy = deployMissing(context,
|
|
33363
|
+
const config = record6(JSON.parse(configText));
|
|
33364
|
+
const hasFrontend = record6(config?.frontend) !== null || Object.values(record6(config?.targets) ?? {}).some((target) => record6(target)?.type === "frontend");
|
|
33365
|
+
const deploy = deployMissing(context, record6(config?.application), hasFrontend);
|
|
33033
33366
|
return {
|
|
33034
33367
|
name: profile.name,
|
|
33035
33368
|
path: profile.path,
|
|
@@ -33248,7 +33581,7 @@ async function runProjectDeployment(http, args) {
|
|
|
33248
33581
|
// package.json
|
|
33249
33582
|
var package_default = {
|
|
33250
33583
|
name: "@supacloud/cli",
|
|
33251
|
-
version: "0.
|
|
33584
|
+
version: "0.112.0",
|
|
33252
33585
|
description: "Project-scoped CLI for SupaCloud users",
|
|
33253
33586
|
type: "module",
|
|
33254
33587
|
main: "./dist/index.js",
|
|
@@ -33301,13 +33634,13 @@ var package_default = {
|
|
|
33301
33634
|
},
|
|
33302
33635
|
dependencies: {
|
|
33303
33636
|
"@supacloud/compiler": "^0.52.0",
|
|
33304
|
-
"@supacloud/delivery": "0.
|
|
33637
|
+
"@supacloud/delivery": "0.23.0",
|
|
33305
33638
|
"@supacloud/db": "0.20.1",
|
|
33306
33639
|
typebox: "1.4.2"
|
|
33307
33640
|
},
|
|
33308
33641
|
overrides: {
|
|
33309
33642
|
"@supacloud/contracts": "0.15.1",
|
|
33310
|
-
"@supacloud/delivery": "0.
|
|
33643
|
+
"@supacloud/delivery": "0.23.0",
|
|
33311
33644
|
"@graphql-tools/utils": "^12.0.4"
|
|
33312
33645
|
},
|
|
33313
33646
|
devDependencies: {
|
|
@@ -45,6 +45,7 @@ export declare function uploadFrontendRelease(http: FrontendReleaseHttp, project
|
|
|
45
45
|
export declare function uploadProjectWebRelease(http: FrontendReleaseHttp, projectRef: string, environmentId: string, deploymentId: string, archivePath: string): Promise<ToolResponse>;
|
|
46
46
|
export declare function uploadFrontendSource(http: FrontendReleaseHttp, projectRef: string, deploymentId: string, archivePath: string): Promise<ToolResponse>;
|
|
47
47
|
export declare function getActiveFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string): Promise<ToolResponse>;
|
|
48
|
+
export declare function verifyFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string): Promise<ToolResponse>;
|
|
48
49
|
export declare function readFrontendReleaseAuthority(http: FrontendReleaseHttp, projectRef: string, deploymentId: string): Promise<ToolResponse>;
|
|
49
50
|
export declare function publishFrontendRelease(http: FrontendReleaseHttp, projectRef: string, deploymentId: string, archiveSource: string | (() => Promise<{
|
|
50
51
|
archivePath: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.112.0",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@supacloud/compiler": "^0.52.0",
|
|
56
|
-
"@supacloud/delivery": "0.
|
|
56
|
+
"@supacloud/delivery": "0.23.0",
|
|
57
57
|
"@supacloud/db": "0.20.1",
|
|
58
58
|
"typebox": "1.4.2"
|
|
59
59
|
},
|
|
60
60
|
"overrides": {
|
|
61
61
|
"@supacloud/contracts": "0.15.1",
|
|
62
|
-
"@supacloud/delivery": "0.
|
|
62
|
+
"@supacloud/delivery": "0.23.0",
|
|
63
63
|
"@graphql-tools/utils": "^12.0.4"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|