@supacloud/cli 0.110.1 → 0.111.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.
Files changed (3) hide show
  1. package/README.md +13 -0
  2. package/dist/index.js +242 -69
  3. 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,7 +9341,7 @@ 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"] },
@@ -9975,7 +9975,7 @@ class HttpTransport {
9975
9975
  }
9976
9976
 
9977
9977
  // src/shared/tools/database-tools.ts
9978
- import { createHash as createHash3 } from "node:crypto";
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 record3(value) {
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 = record3(input["metadata"]) ?? input;
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 = record3(rawSource);
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 = record3(rawTable);
12301
- const tableName = record3(table?.["table"]);
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 = record3(input.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 = record3(source);
12459
+ const sourceRecord = record4(source);
12358
12460
  const tables = Array.isArray(sourceRecord?.["tables"]) ? sourceRecord["tables"] : [];
12359
12461
  return tables.flatMap((table) => {
12360
- const tableRecord = record3(table);
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 createHash3("sha256").update(JSON.stringify({
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 = createHash3("sha256").update(basename3(file)).digest("hex");
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 = createHash3("sha256").update(local.rawBytes).digest("hex");
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 createHash5, timingSafeEqual } from "node:crypto";
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 createHash4 } from "node:crypto";
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: createHash4("sha256").update(code).digest("hex") };
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 = createHash5("sha256").update(bytes).digest();
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 createHash7 } from "node:crypto";
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 createHash6 } from "node:crypto";
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 createHash6("sha256").update(stableStringifyNode(normalizedRequest)).digest("hex");
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;
@@ -16832,7 +16934,7 @@ function releaseFailure(operation, code, status) {
16832
16934
  }]
16833
16935
  };
16834
16936
  }
16835
- function exactKeys(candidate, keys) {
16937
+ function exactKeys2(candidate, keys) {
16836
16938
  const actual = Object.keys(candidate).sort();
16837
16939
  const expected = [...keys].sort();
16838
16940
  return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
@@ -16854,7 +16956,7 @@ function releaseRecord(candidate) {
16854
16956
  "kind",
16855
16957
  "archive_format"
16856
16958
  ];
16857
- if (!exactKeys(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")
16959
+ 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
16960
  return null;
16859
16961
  return {
16860
16962
  project_ref: record.project_ref,
@@ -16873,7 +16975,7 @@ function releaseEnvelope(candidate, expected) {
16873
16975
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
16874
16976
  return null;
16875
16977
  const envelope = candidate;
16876
- const release = exactKeys(envelope, ["project_ref", "deployment_id", "release"]) ? releaseRecord(envelope.release) : null;
16978
+ const release = exactKeys2(envelope, ["project_ref", "deployment_id", "release"]) ? releaseRecord(envelope.release) : null;
16877
16979
  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
16980
  return null;
16879
16981
  return release;
@@ -16901,7 +17003,7 @@ function releaseInventory(candidate, expected) {
16901
17003
  "releases",
16902
17004
  "next_cursor"
16903
17005
  ];
16904
- if (!exactKeys(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))
17006
+ 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
17007
  return null;
16906
17008
  const activeReleaseId = nullableIdentity(record.active_release_id, RELEASE_ID_PATTERN);
16907
17009
  const activeActivationId = nullableIdentity(record.active_activation_id, MUTATION_ID_PATTERN2);
@@ -16940,7 +17042,7 @@ function sameArchiveIdentity(left, right) {
16940
17042
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
16941
17043
  }
16942
17044
  async function archiveSha256(handle, sizeBytes) {
16943
- const hash = createHash7("sha256");
17045
+ const hash = createHash8("sha256");
16944
17046
  const chunk = new Uint8Array(Math.min(sizeBytes, ARCHIVE_CHUNK_BYTES));
16945
17047
  for (let offset = 0;offset < sizeBytes; ) {
16946
17048
  const requested = Math.min(chunk.byteLength, sizeBytes - offset);
@@ -17075,7 +17177,7 @@ function stableJson(candidate) {
17075
17177
  return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
17076
17178
  }
17077
17179
  function activationFingerprint(identity) {
17078
- return createHash7("sha256").update(stableJson({
17180
+ return createHash8("sha256").update(stableJson({
17079
17181
  project_ref: identity.projectRef,
17080
17182
  deployment_id: identity.deploymentId,
17081
17183
  release_id: identity.releaseId,
@@ -17092,7 +17194,7 @@ function publicMutationReceipt(candidate, expected) {
17092
17194
  return null;
17093
17195
  const envelope = candidate;
17094
17196
  const mutation = safeMutationStatus(envelope.mutation);
17095
- if (!mutation || !exactKeys(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)
17197
+ 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
17198
  return null;
17097
17199
  return {
17098
17200
  operation: mutation.operation,
@@ -17107,14 +17209,14 @@ function activationReceipt(candidate, input) {
17107
17209
  const record = candidate;
17108
17210
  const release = releaseRecord(record.release);
17109
17211
  const mutation = record.mutation;
17110
- if (!release || !mutation || !exactKeys(record, [
17212
+ if (!release || !mutation || !exactKeys2(record, [
17111
17213
  "project_ref",
17112
17214
  "deployment_id",
17113
17215
  "active_release_id",
17114
17216
  "activation_id",
17115
17217
  "release",
17116
17218
  "mutation"
17117
- ]) || !exactKeys(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")
17219
+ ]) || !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
17220
  return null;
17119
17221
  return release;
17120
17222
  }
@@ -17371,7 +17473,7 @@ async function readPreviousFrontendRelease(http, projectRef, deploymentId) {
17371
17473
  const activeActivationId = value ? nullableIdentity(value.active_activation_id, MUTATION_ID_PATTERN2) : undefined;
17372
17474
  const previousActivationId = value ? nullableIdentity(value.previous_activation_id, MUTATION_ID_PATTERN2) : undefined;
17373
17475
  const previous = value?.previous_release === null ? null : releaseRecord(value?.previous_release);
17374
- if (!response.ok || response.status !== 200 || !value || !exactKeys(value, [
17476
+ if (!response.ok || response.status !== 200 || !value || !exactKeys2(value, [
17375
17477
  "schema",
17376
17478
  "project_ref",
17377
17479
  "deployment_id",
@@ -29565,7 +29667,7 @@ import {
29565
29667
  } from "@supacloud/db";
29566
29668
 
29567
29669
  // src/shared/tools/db-workflow.ts
29568
- import { createHash as createHash8 } from "node:crypto";
29670
+ import { createHash as createHash9 } from "node:crypto";
29569
29671
  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
29672
  import { tmpdir as tmpdir2 } from "node:os";
29571
29673
  import { dirname as dirname11, isAbsolute as isAbsolute7, join as join15, relative as relative8, resolve as resolve20 } from "node:path";
@@ -29854,7 +29956,7 @@ function resolveOfficialSupabaseCommand2(workdir, environment = process.env) {
29854
29956
  }
29855
29957
 
29856
29958
  // src/shared/tools/db-workflow.ts
29857
- var hash4 = (content) => createHash8("sha256").update(content).digest("hex");
29959
+ var hash4 = (content) => createHash9("sha256").update(content).digest("hex");
29858
29960
  var result = (value, isError = false) => ({
29859
29961
  isError,
29860
29962
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
@@ -30958,13 +31060,13 @@ var MUTATION_TOOL_SCHEMA = {
30958
31060
  };
30959
31061
 
30960
31062
  // src/shared/tools/release-tools.ts
30961
- import { createHash as createHash9 } from "node:crypto";
31063
+ import { createHash as createHash10 } from "node:crypto";
30962
31064
  import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
30963
31065
  import { dirname as dirname12, join as join17, relative as relative9, resolve as resolve23 } from "node:path";
30964
31066
  var SAFE_PROJECT_REF = /^[A-Za-z0-9_-]{1,64}$/;
30965
31067
  var SAFE_ENVIRONMENT_ID = /^[A-Za-z0-9_-]{1,64}$/;
30966
31068
  var BACKUP_ID = /^logical-full_[A-Za-z0-9_-]{1,64}_[a-f0-9]{32}$/;
30967
- var SHA256 = /^[a-f0-9]{64}$/;
31069
+ var SHA2562 = /^[a-f0-9]{64}$/;
30968
31070
  var SAFE_DATABASE = /^[^\u0000-\u001f\u007f]{1,128}$/;
30969
31071
  var INVENTORY_MAX_BYTES = 1024 * 1024;
30970
31072
  var MUTATION_MAX_BYTES = 64 * 1024;
@@ -30994,7 +31096,7 @@ function backupBelongsToProject(backupId, projectRef) {
30994
31096
  return BACKUP_ID.test(backupId) && backupId.startsWith(prefix) && /^[a-f0-9]{32}$/.test(backupId.slice(prefix.length));
30995
31097
  }
30996
31098
  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" || !SHA256.test(value.sha256))
31099
+ 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
31100
  return null;
30999
31101
  return {
31000
31102
  backup_id: value.backup_id,
@@ -31040,7 +31142,7 @@ function restoreRequest(projectRef, backupId, expectedSha256, restoreConfirmatio
31040
31142
  if (typeof backupId !== "string" || !backupBelongsToProject(backupId, projectRef)) {
31041
31143
  throw new Error("'backup_id' must identify a logical-full backup for 'ref'");
31042
31144
  }
31043
- if (typeof expectedSha256 !== "string" || !SHA256.test(expectedSha256)) {
31145
+ if (typeof expectedSha256 !== "string" || !SHA2562.test(expectedSha256)) {
31044
31146
  throw new Error("'expected_sha256' must be a lowercase SHA-256 digest");
31045
31147
  }
31046
31148
  const confirmation = `RESTORE_PROJECT:${projectRef}:${backupId}:${expectedSha256}`;
@@ -31201,7 +31303,7 @@ function genericScopedReleaseScopeJson(scope) {
31201
31303
  return JSON.stringify(canonicalValue(scope));
31202
31304
  }
31203
31305
  function genericScopedReleaseScopeSha256(scope) {
31204
- return createHash9("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
31306
+ return createHash10("sha256").update(genericScopedReleaseScopeJson(scope)).digest("hex");
31205
31307
  }
31206
31308
  function scopeEnvironmentId(scope, action) {
31207
31309
  const environmentId = isRecord6(scope) ? scope.targetEnvironmentId : undefined;
@@ -31470,6 +31572,73 @@ function handleScopeInspect(args) {
31470
31572
  }
31471
31573
  });
31472
31574
  }
31575
+ function releaseComponentView(component) {
31576
+ if (component.kind === "migration")
31577
+ return { kind: component.kind, name: component.name };
31578
+ if (component.kind === "function") {
31579
+ return { kind: component.kind, slug: component.slug, artifact_sha256: component.artifactSha256 };
31580
+ }
31581
+ return { kind: component.kind, artifact_sha256: component.artifactSha256 };
31582
+ }
31583
+ function handleContractPlan(args, fallbackRef) {
31584
+ const cwd = typeof args.cwd === "string" && args.cwd.trim() ? resolve23(args.cwd.trim()) : process.cwd();
31585
+ const requestedRef = typeof args.ref === "string" && args.ref.trim() || fallbackRef;
31586
+ if (!requestedRef || !validProjectRef(requestedRef)) {
31587
+ throw new Error("'ref' is required for contract_plan and must identify one project");
31588
+ }
31589
+ const requestedEnvironmentId = requestedScopeEnvironment(args, "contract_plan");
31590
+ const file = typeof args.file === "string" && args.file.trim() ? args.file.trim() : "supacloud.release.json";
31591
+ const filePath = resolve23(cwd, file);
31592
+ if (!existsSync14(filePath)) {
31593
+ throw new Error(`Release contract not found: ${file}`);
31594
+ }
31595
+ let parsedValue;
31596
+ try {
31597
+ parsedValue = JSON.parse(readFileSync11(filePath, "utf8"));
31598
+ } catch {
31599
+ throw new Error(`Release contract '${file}' is not valid JSON`);
31600
+ }
31601
+ const scope = parseReleaseScope(parsedValue);
31602
+ if (scope.targetProjectRef !== requestedRef) {
31603
+ throw new Error(`Release contract target project '${scope.targetProjectRef}' does not match requested project '${requestedRef}'`);
31604
+ }
31605
+ if (scope.targetEnvironmentId !== requestedEnvironmentId) {
31606
+ throw new Error(`Release contract target environment '${scope.targetEnvironmentId}' does not match requested environment '${requestedEnvironmentId}'`);
31607
+ }
31608
+ const git = gitCommitInfo(cwd);
31609
+ const sourceMatchesHead = git.head !== null && scope.sourceCommit === git.head;
31610
+ const sourceMatchesOriginMain = git.originMain !== null && scope.sourceCommit === git.originMain;
31611
+ if (args.require_current_source === true && !sourceMatchesHead) {
31612
+ throw new Error(`Release contract source commit '${scope.sourceCommit}' does not match current HEAD '${git.head ?? "unavailable"}'`);
31613
+ }
31614
+ const counts = scope.components.reduce((result, component) => {
31615
+ result[component.kind] += 1;
31616
+ return result;
31617
+ }, { migration: 0, function: 0, web: 0 });
31618
+ return releaseControlSuccess("release.contract_plan", {
31619
+ plan_schema: "supacloud.release-plan.v1",
31620
+ file: relative9(cwd, filePath),
31621
+ release_id: scope.releaseId,
31622
+ scope_sha256: releaseScopeSha256(scope),
31623
+ source_commit: scope.sourceCommit,
31624
+ target_project_ref: scope.targetProjectRef,
31625
+ target_environment_id: scope.targetEnvironmentId,
31626
+ components: scope.components.map(releaseComponentView),
31627
+ component_counts: counts,
31628
+ checks: {
31629
+ source_commit_matches_head: sourceMatchesHead,
31630
+ source_commit_matches_origin_main: sourceMatchesOriginMain,
31631
+ target_project_ref_matches_requested: true,
31632
+ target_environment_id_matches_requested: true,
31633
+ components_unique_and_sorted: true
31634
+ },
31635
+ current_head: git.head,
31636
+ origin_main: git.originMain,
31637
+ source_is_current: sourceMatchesHead,
31638
+ read_only: true,
31639
+ mutations_performed: false
31640
+ });
31641
+ }
31473
31642
  function parseCommaList(value) {
31474
31643
  if (!value)
31475
31644
  return [];
@@ -31526,7 +31695,7 @@ function handleScopeCreate(args, fallbackRef) {
31526
31695
  });
31527
31696
  }
31528
31697
  function registerReleaseTools(server, http, options = {}) {
31529
- const localActions = ["scope_inspect", "scope_rebind", "scope_create"];
31698
+ const localActions = ["scope_inspect", "scope_rebind", "scope_create", "contract_plan"];
31530
31699
  const remoteActions = [
31531
31700
  "logical_backup_list",
31532
31701
  "logical_backup_status",
@@ -31541,18 +31710,19 @@ function registerReleaseTools(server, http, options = {}) {
31541
31710
  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
31711
  action: withDescription(stringEnum2(allActions), "Release control action"),
31543
31712
  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"),
31713
+ file: optional(String2(), "[scope_inspect/scope_rebind/scope_create/contract_plan] Path to release scope or release contract JSON file"),
31545
31714
  files: optional(String2(), "[scope_rebind] Comma-separated list of release scope JSON files"),
31546
31715
  dir: optional(String2(), "[scope_rebind] Directory containing release scope JSON files (defaults to supacloud/fa/release-scopes, release-scopes, or scopes)"),
31547
31716
  task: optional(String2(), "[scope_rebind/scope_create] Task ID or prefix filter for scope files"),
31548
31717
  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"),
31718
+ environment_id: optional(String2(), "[scope_inspect/scope_rebind/scope_create/contract_plan] Exact target deployment environment"),
31550
31719
  functions: optional(String2(), "[scope_create] Comma-separated list of functions to deploy"),
31551
31720
  excluded_functions: optional(String2(), "[scope_create] Comma-separated list of functions to exclude"),
31552
31721
  migrations: optional(String2(), "[scope_create] Comma-separated list of migrations to apply"),
31553
31722
  web: optional(Boolean2(), "[scope_create] Whether web assets are included in release scope"),
31554
31723
  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"),
31724
+ cwd: optional(String2(), "[scope_inspect/scope_rebind/scope_create/contract_plan] Base directory for relative file and git resolution"),
31725
+ require_current_source: optional(Boolean2(), "[contract_plan] Fail unless the contract sourceCommit equals current HEAD"),
31556
31726
  backup_id: optional(String2(), "[logical_backup_create/status/restore] Stable project-bound backup ID; create generates one when omitted"),
31557
31727
  expected_sha256: optional(String2(), "[logical_backup_restore] Exact lowercase SHA-256 from the selected project inventory"),
31558
31728
  restore_confirmation: optional(String2(), "[logical_backup_restore] Exact RESTORE_PROJECT:<ref>:<backup_id>:<sha256> confirmation"),
@@ -31572,6 +31742,9 @@ function registerReleaseTools(server, http, options = {}) {
31572
31742
  if (action === "scope_create") {
31573
31743
  return handleScopeCreate(args, options.projectRef);
31574
31744
  }
31745
+ if (action === "contract_plan") {
31746
+ return handleContractPlan(args, options.projectRef);
31747
+ }
31575
31748
  if (options.localOnly || !http) {
31576
31749
  return {
31577
31750
  isError: true,
@@ -31745,7 +31918,7 @@ function registerReleaseTools(server, http, options = {}) {
31745
31918
  // src/shared/tools/deploy-tools.ts
31746
31919
  import { access, lstat as lstat5, mkdtemp as mkdtemp2, opendir, readFile as readFile6, rm as rm3, writeFile as writeFile6 } from "node:fs/promises";
31747
31920
  import { spawn as spawn2 } from "node:child_process";
31748
- import { createHash as createHash10 } from "node:crypto";
31921
+ import { createHash as createHash11 } from "node:crypto";
31749
31922
  import { tmpdir as tmpdir3 } from "node:os";
31750
31923
  import { basename as basename8, dirname as dirname13, join as join18, relative as relative10, resolve as resolve24, sep as sep5 } from "node:path";
31751
31924
 
@@ -31841,7 +32014,7 @@ var deployToolSchema = {
31841
32014
  status: optional(String2({ format: "uuid" }), "Read a backend deployment status without publishing"),
31842
32015
  json: optional(Boolean2(), "Print only the final JSON result")
31843
32016
  };
31844
- function record4(candidate) {
32017
+ function record5(candidate) {
31845
32018
  return candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : null;
31846
32019
  }
31847
32020
  function requiredString(candidate, field) {
@@ -31851,7 +32024,7 @@ function requiredString(candidate, field) {
31851
32024
  return candidate.trim();
31852
32025
  }
31853
32026
  function frontendDeployment(candidate) {
31854
- const value = record4(candidate);
32027
+ const value = record5(candidate);
31855
32028
  if (!value)
31856
32029
  throw new Error("Invalid frontend deployment response");
31857
32030
  return {
@@ -31921,7 +32094,7 @@ function optionalStringArray(value, field) {
31921
32094
  });
31922
32095
  }
31923
32096
  function deployTargetConfig(candidate, name) {
31924
- const value = record4(candidate);
32097
+ const value = record5(candidate);
31925
32098
  if (!value || value.type !== "frontend" && value.type !== "edge_function") {
31926
32099
  throw new Error(`supacloud.json targets.${name}.type must be 'frontend' or 'edge_function'`);
31927
32100
  }
@@ -31950,10 +32123,10 @@ async function readDeployConfig(configRoot) {
31950
32123
  const candidate = await readJson(configPath);
31951
32124
  if (candidate === null)
31952
32125
  return {};
31953
- const config = record4(candidate);
31954
- const frontend = record4(config?.frontend);
31955
- const application = record4(config?.application);
31956
- const targetValues = record4(config?.targets);
32126
+ const config = record5(candidate);
32127
+ const frontend = record5(config?.frontend);
32128
+ const application = record5(config?.application);
32129
+ const targetValues = record5(config?.targets);
31957
32130
  if (!config || config.frontend !== undefined && !frontend || config.application !== undefined && !application || config.targets !== undefined && !targetValues) {
31958
32131
  throw new Error("supacloud.json must contain object-valued 'application', 'frontend' or 'targets' configuration");
31959
32132
  }
@@ -31993,7 +32166,7 @@ async function readDeployConfig(configRoot) {
31993
32166
  };
31994
32167
  }
31995
32168
  async function packageName(projectDirectory) {
31996
- const candidate = record4(await readJson(join18(projectDirectory, "package.json")));
32169
+ const candidate = record5(await readJson(join18(projectDirectory, "package.json")));
31997
32170
  return typeof candidate?.name === "string" && candidate.name.trim() ? candidate.name.trim() : null;
31998
32171
  }
31999
32172
  async function pathExists(path) {
@@ -32019,8 +32192,8 @@ async function hasProjectReleaseManifest(root) {
32019
32192
  return false;
32020
32193
  }
32021
32194
  async function defaultBuildCommand(projectDirectory, configRoot) {
32022
- const packageJson = record4(await readJson(join18(projectDirectory, "package.json")));
32023
- const scripts = record4(packageJson?.scripts);
32195
+ const packageJson = record5(await readJson(join18(projectDirectory, "package.json")));
32196
+ const scripts = record5(packageJson?.scripts);
32024
32197
  if (typeof scripts?.build !== "string" || !scripts.build.trim()) {
32025
32198
  throw new Error("No build command was configured and package.json has no build script");
32026
32199
  }
@@ -32042,7 +32215,7 @@ async function defaultBuildCommand(projectDirectory, configRoot) {
32042
32215
  return "npm run build";
32043
32216
  }
32044
32217
  function deploymentList(candidate) {
32045
- const values = Array.isArray(candidate) ? candidate : Array.isArray(record4(candidate)?.deployments) ? record4(candidate)?.deployments : null;
32218
+ const values = Array.isArray(candidate) ? candidate : Array.isArray(record5(candidate)?.deployments) ? record5(candidate)?.deployments : null;
32046
32219
  if (!values)
32047
32220
  throw new Error("Invalid frontend deployment list response");
32048
32221
  return values.map(frontendDeployment);
@@ -32202,9 +32375,9 @@ async function collectArchiveFiles(root) {
32202
32375
  throw new Error(`Frontend output exceeds ${MAX_SOURCE_BYTES} bytes`);
32203
32376
  const archivePath = relative10(root, path).split(sep5).join("/");
32204
32377
  files.set(archivePath, data);
32205
- digests.push({ path: archivePath, size: data.byteLength, sha256: createHash10("sha256").update(data).digest("hex") });
32378
+ digests.push({ path: archivePath, size: data.byteLength, sha256: createHash11("sha256").update(data).digest("hex") });
32206
32379
  }
32207
- const tree = createHash10("sha256");
32380
+ const tree = createHash11("sha256");
32208
32381
  for (const file of digests.sort((left, right) => left.path.localeCompare(right.path))) {
32209
32382
  const pathBytes = Buffer.from(file.path, "utf8");
32210
32383
  const frame = Buffer.alloc(12);
@@ -32220,7 +32393,7 @@ async function createCollectedFrontendArchive(outputDirectory, collected) {
32220
32393
  try {
32221
32394
  const archiveBytes = await createFrontendTarZstd(collected.files);
32222
32395
  await writeFile6(archivePath, archiveBytes);
32223
- const sha256 = createHash10("sha256").update(archiveBytes).digest("hex");
32396
+ const sha256 = createHash11("sha256").update(archiveBytes).digest("hex");
32224
32397
  return {
32225
32398
  archivePath,
32226
32399
  sha256,
@@ -32342,7 +32515,7 @@ function registerDeployTools(server, http, options = {}) {
32342
32515
  import { existsSync as existsSync15 } from "node:fs";
32343
32516
  import { readFile as readFile7 } from "node:fs/promises";
32344
32517
  import { readdir as readdir7 } from "node:fs/promises";
32345
- import { createHash as createHash11 } from "node:crypto";
32518
+ import { createHash as createHash12 } from "node:crypto";
32346
32519
  import { join as join19, resolve as resolve25 } from "node:path";
32347
32520
  import { compileProject as compileProject2 } from "@supacloud/compiler";
32348
32521
  var SAFE_TOKEN = /^[A-Za-z0-9._:@/+,-]+$/;
@@ -32441,7 +32614,7 @@ async function sourceFingerprint(root) {
32441
32614
  }
32442
32615
  };
32443
32616
  await visit(root);
32444
- const hash = createHash11("sha256");
32617
+ const hash = createHash12("sha256");
32445
32618
  for (const file of files) {
32446
32619
  hash.update(file.slice(root.length).replace(/\\/g, "/"));
32447
32620
  hash.update(await readFile7(file));
@@ -32841,7 +33014,7 @@ function textResult3(text, isError = false) {
32841
33014
  function jsonResult(value, isError = false) {
32842
33015
  return textResult3(JSON.stringify(value, null, 2), isError);
32843
33016
  }
32844
- function record5(value) {
33017
+ function record6(value) {
32845
33018
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
32846
33019
  }
32847
33020
  function packageDependencies(manifest) {
@@ -32864,12 +33037,12 @@ async function readPackageManifest(root) {
32864
33037
  return {};
32865
33038
  try {
32866
33039
  const parsed = JSON.parse(await readFile8(path, "utf8"));
32867
- const value = record5(parsed);
33040
+ const value = record6(parsed);
32868
33041
  if (!value)
32869
33042
  throw new Error("package.json must contain an object");
32870
- const scripts = record5(value.scripts);
32871
- const dependencies = record5(value.dependencies);
32872
- const devDependencies = record5(value.devDependencies);
33043
+ const scripts = record6(value.scripts);
33044
+ const dependencies = record6(value.dependencies);
33045
+ const devDependencies = record6(value.devDependencies);
32873
33046
  return {
32874
33047
  scripts: scripts ?? undefined,
32875
33048
  dependencies: dependencies ?? undefined,
@@ -33027,9 +33200,9 @@ async function inspectProfile(profile) {
33027
33200
  return "{}";
33028
33201
  throw error;
33029
33202
  });
33030
- const config = record5(JSON.parse(configText));
33031
- const hasFrontend = record5(config?.frontend) !== null || Object.values(record5(config?.targets) ?? {}).some((target) => record5(target)?.type === "frontend");
33032
- const deploy = deployMissing(context, record5(config?.application), hasFrontend);
33203
+ const config = record6(JSON.parse(configText));
33204
+ const hasFrontend = record6(config?.frontend) !== null || Object.values(record6(config?.targets) ?? {}).some((target) => record6(target)?.type === "frontend");
33205
+ const deploy = deployMissing(context, record6(config?.application), hasFrontend);
33033
33206
  return {
33034
33207
  name: profile.name,
33035
33208
  path: profile.path,
@@ -33248,7 +33421,7 @@ async function runProjectDeployment(http, args) {
33248
33421
  // package.json
33249
33422
  var package_default = {
33250
33423
  name: "@supacloud/cli",
33251
- version: "0.110.1",
33424
+ version: "0.111.0",
33252
33425
  description: "Project-scoped CLI for SupaCloud users",
33253
33426
  type: "module",
33254
33427
  main: "./dist/index.js",
@@ -33301,13 +33474,13 @@ var package_default = {
33301
33474
  },
33302
33475
  dependencies: {
33303
33476
  "@supacloud/compiler": "^0.52.0",
33304
- "@supacloud/delivery": "0.22.0",
33477
+ "@supacloud/delivery": "0.23.0",
33305
33478
  "@supacloud/db": "0.20.1",
33306
33479
  typebox: "1.4.2"
33307
33480
  },
33308
33481
  overrides: {
33309
33482
  "@supacloud/contracts": "0.15.1",
33310
- "@supacloud/delivery": "0.22.0",
33483
+ "@supacloud/delivery": "0.23.0",
33311
33484
  "@graphql-tools/utils": "^12.0.4"
33312
33485
  },
33313
33486
  devDependencies: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.110.1",
3
+ "version": "0.111.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.22.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.22.0",
62
+ "@supacloud/delivery": "0.23.0",
63
63
  "@graphql-tools/utils": "^12.0.4"
64
64
  },
65
65
  "devDependencies": {