run402-mcp 3.5.4 → 3.5.6

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 (40) hide show
  1. package/README.md +1 -1
  2. package/core/dist/config.d.ts +19 -0
  3. package/core/dist/config.d.ts.map +1 -1
  4. package/core/dist/config.js +103 -4
  5. package/core/dist/config.js.map +1 -1
  6. package/dist/config.d.ts +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +1 -1
  9. package/dist/config.js.map +1 -1
  10. package/dist/sdk.d.ts.map +1 -1
  11. package/dist/sdk.js +2 -1
  12. package/dist/sdk.js.map +1 -1
  13. package/package.json +3 -3
  14. package/schemas/release-spec.v1.json +29 -2
  15. package/sdk/README.md +30 -1
  16. package/sdk/core-dist/config.d.ts +19 -0
  17. package/sdk/core-dist/config.js +103 -4
  18. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  19. package/sdk/dist/namespaces/deploy.js +281 -21
  20. package/sdk/dist/namespaces/deploy.js.map +1 -1
  21. package/sdk/dist/namespaces/deploy.types.d.ts +16 -1
  22. package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
  23. package/sdk/dist/namespaces/deploy.types.js.map +1 -1
  24. package/sdk/dist/namespaces/projects.d.ts.map +1 -1
  25. package/sdk/dist/namespaces/projects.js +1 -0
  26. package/sdk/dist/namespaces/projects.js.map +1 -1
  27. package/sdk/dist/namespaces/projects.types.d.ts +7 -0
  28. package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
  29. package/sdk/dist/node/deploy-manifest.d.ts +5 -3
  30. package/sdk/dist/node/deploy-manifest.d.ts.map +1 -1
  31. package/sdk/dist/node/deploy-manifest.js +16 -2
  32. package/sdk/dist/node/deploy-manifest.js.map +1 -1
  33. package/sdk/dist/node/index.d.ts +2 -0
  34. package/sdk/dist/node/index.d.ts.map +1 -1
  35. package/sdk/dist/node/index.js +1 -0
  36. package/sdk/dist/node/index.js.map +1 -1
  37. package/sdk/dist/node/target-profile.d.ts +40 -0
  38. package/sdk/dist/node/target-profile.d.ts.map +1 -0
  39. package/sdk/dist/node/target-profile.js +98 -0
  40. package/sdk/dist/node/target-profile.js.map +1 -0
@@ -166,7 +166,7 @@ export class Deploy {
166
166
  */
167
167
  async commit(planId, opts = {}) {
168
168
  const emit = makeEmitter(opts.onEvent);
169
- const commit = await commitInternal(this.client, planId, opts.idempotencyKey, opts.project);
169
+ const commit = requireCloudCommitResponse(await commitInternal(this.client, planId, opts.idempotencyKey, opts.project), "committing deploy");
170
170
  return await pollUntilReady(this.client, commit, {}, [], emit, opts.project);
171
171
  }
172
172
  /**
@@ -452,9 +452,10 @@ function deriveSliceKinds(spec) {
452
452
  }
453
453
  async function applyOnce(client, spec, opts, emit) {
454
454
  const allowWarningCodes = normalizeAllowWarningCodes(opts.allowWarningCodes);
455
+ const target = opts.target === "core" ? "core" : "cloud";
455
456
  const sliceKinds = deriveSliceKinds(spec);
456
457
  emit({ type: "plan.started" });
457
- const { plan, byteReaders } = await planInternal(client, spec, opts.idempotencyKey);
458
+ const { plan, byteReaders } = await planInternal(client, spec, opts.idempotencyKey, false, target);
458
459
  emit({ type: "plan.diff", diff: plan.diff });
459
460
  emitPlanWarnings(plan, emit);
460
461
  abortOnConfirmationWarnings(plan, opts, allowWarningCodes);
@@ -471,6 +472,21 @@ async function applyOnce(client, spec, opts, emit) {
471
472
  // sandbox provider without payment auto-handling can intercept the
472
473
  // event and resolve before we hit upload.
473
474
  }
475
+ if (target === "core") {
476
+ await uploadCoreContent(client, spec.project, byteReaders, emit);
477
+ emit({
478
+ type: "commit.phase",
479
+ phase: "validate",
480
+ status: "started",
481
+ ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
482
+ });
483
+ const planId = requirePlanId(plan, "applying deploy to Core");
484
+ const commit = await commitInternal(client, planId, opts.idempotencyKey, spec.project);
485
+ if (!isCoreCommitResponse(commit)) {
486
+ return await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
487
+ }
488
+ return await coreDeployResult(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
489
+ }
474
490
  await uploadMissing(client, spec.project, plan.missing_content, byteReaders, emit);
475
491
  emit({
476
492
  type: "commit.phase",
@@ -479,7 +495,7 @@ async function applyOnce(client, spec, opts, emit) {
479
495
  ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
480
496
  });
481
497
  const { planId } = requirePersistedPlan(plan, "applying deploy");
482
- const commit = await commitInternal(client, planId, opts.idempotencyKey, spec.project);
498
+ const commit = requireCloudCommitResponse(await commitInternal(client, planId, opts.idempotencyKey, spec.project), "applying deploy");
483
499
  const result = await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
484
500
  // v1.48 unified-apply: thread the plan response's `asset_entries[]` back
485
501
  // into DeployResult.assets so callers reading `result.assets.byKey[key]`
@@ -642,12 +658,39 @@ function contentRefToWire(ref) {
642
658
  ...(ref.integrity ? { integrity: ref.integrity } : {}),
643
659
  };
644
660
  }
661
+ function contentRefToCoreSpec(ref) {
662
+ const maybeWire = ref;
663
+ const contentType = ref.contentType ?? maybeWire.content_type;
664
+ return {
665
+ sha256: ref.sha256,
666
+ size: ref.size,
667
+ ...(contentType ? { contentType } : {}),
668
+ ...(ref.integrity ? { integrity: ref.integrity } : {}),
669
+ };
670
+ }
671
+ function requireContentRef(ref, resource) {
672
+ if (ref)
673
+ return ref;
674
+ throw new Run402DeployError(`Missing content ref for ${resource}`, {
675
+ code: "INTERNAL_ERROR",
676
+ phase: "validate",
677
+ resource,
678
+ retryable: false,
679
+ context: "serializing deploy spec",
680
+ });
681
+ }
645
682
  function fileSetToWire(map) {
646
683
  const out = {};
647
684
  for (const [path, ref] of Object.entries(map))
648
685
  out[path] = contentRefToWire(ref);
649
686
  return out;
650
687
  }
688
+ function fileSetToCoreSpec(map) {
689
+ const out = {};
690
+ for (const [path, ref] of Object.entries(map))
691
+ out[path] = contentRefToCoreSpec(ref);
692
+ return out;
693
+ }
651
694
  function requireRoleToWire(gate) {
652
695
  return {
653
696
  table: gate.table,
@@ -659,6 +702,21 @@ function requireRoleToWire(gate) {
659
702
  ...(gate.signInPath !== undefined ? { sign_in_path: gate.signInPath } : {}),
660
703
  };
661
704
  }
705
+ function functionToCoreSpec(fn) {
706
+ return {
707
+ ...(fn.runtime !== undefined ? { runtime: fn.runtime } : {}),
708
+ ...(fn.source !== undefined ? { source: contentRefToCoreSpec(fn.source) } : {}),
709
+ ...(fn.files !== undefined ? { files: fileSetToCoreSpec(fn.files) } : {}),
710
+ ...(fn.entrypoint !== undefined ? { entrypoint: fn.entrypoint } : {}),
711
+ ...(fn.config !== undefined ? { config: fn.config } : {}),
712
+ ...(fn.deps !== undefined ? { deps: fn.deps } : {}),
713
+ ...(fn.schedule !== undefined ? { schedule: fn.schedule } : {}),
714
+ ...(fn.requireAuth !== undefined ? { requireAuth: fn.requireAuth } : {}),
715
+ ...(fn.requireRole !== undefined ? { requireRole: fn.requireRole } : {}),
716
+ ...(fn.class !== undefined ? { class: fn.class } : {}),
717
+ ...(fn.capabilities !== undefined ? { capabilities: fn.capabilities } : {}),
718
+ };
719
+ }
662
720
  function functionToWire(fn) {
663
721
  return {
664
722
  ...(fn.runtime !== undefined ? { runtime: fn.runtime } : {}),
@@ -680,6 +738,7 @@ function functionToWire(fn) {
680
738
  ? { require_role: fn.requireRole === null ? null : requireRoleToWire(fn.requireRole) }
681
739
  : {}),
682
740
  ...(fn.class !== undefined ? { class: fn.class } : {}),
741
+ ...(fn.capabilities !== undefined ? { capabilities: fn.capabilities } : {}),
683
742
  };
684
743
  }
685
744
  function functionMapToWire(map) {
@@ -688,6 +747,25 @@ function functionMapToWire(map) {
688
747
  out[name] = functionToWire(fn);
689
748
  return out;
690
749
  }
750
+ function functionMapToCoreSpec(map) {
751
+ const out = {};
752
+ for (const [name, fn] of Object.entries(map))
753
+ out[name] = functionToCoreSpec(fn);
754
+ return out;
755
+ }
756
+ function functionsToCoreSpec(functions) {
757
+ return {
758
+ ...(functions.replace !== undefined ? { replace: functionMapToCoreSpec(functions.replace) } : {}),
759
+ ...(functions.patch !== undefined
760
+ ? {
761
+ patch: {
762
+ ...(functions.patch.set !== undefined ? { set: functionMapToCoreSpec(functions.patch.set) } : {}),
763
+ ...(functions.patch.delete !== undefined ? { delete: functions.patch.delete } : {}),
764
+ },
765
+ }
766
+ : {}),
767
+ };
768
+ }
691
769
  function databaseToWire(database) {
692
770
  return {
693
771
  ...(database.expose !== undefined ? { expose: database.expose } : {}),
@@ -697,7 +775,27 @@ function databaseToWire(database) {
697
775
  migrations: database.migrations.map((m) => ({
698
776
  id: m.id,
699
777
  checksum: m.checksum,
700
- sql_ref: contentRefToWire(m.sql_ref),
778
+ ...(m.sql !== undefined
779
+ ? { sql: m.sql }
780
+ : { sql_ref: contentRefToWire(requireContentRef(m.sql_ref, `database.migrations.${m.id}.sql_ref`)) }),
781
+ ...(m.transaction !== undefined ? { transaction: m.transaction } : {}),
782
+ })),
783
+ }
784
+ : {}),
785
+ };
786
+ }
787
+ function databaseToCoreSpec(database) {
788
+ return {
789
+ ...(database.expose !== undefined ? { expose: database.expose } : {}),
790
+ ...(database.zero_downtime !== undefined ? { zero_downtime: database.zero_downtime } : {}),
791
+ ...(database.migrations !== undefined
792
+ ? {
793
+ migrations: database.migrations.map((m) => ({
794
+ id: m.id,
795
+ checksum: m.checksum,
796
+ ...(m.sql !== undefined
797
+ ? { sql: m.sql }
798
+ : { sql_ref: contentRefToCoreSpec(requireContentRef(m.sql_ref, `database.migrations.${m.id}.sql_ref`)) }),
701
799
  ...(m.transaction !== undefined ? { transaction: m.transaction } : {}),
702
800
  })),
703
801
  }
@@ -717,6 +815,24 @@ function functionsToWire(functions) {
717
815
  : {}),
718
816
  };
719
817
  }
818
+ function siteToCoreSpec(site) {
819
+ if ("replace" in site && site.replace) {
820
+ return {
821
+ replace: fileSetToCoreSpec(site.replace),
822
+ ...(site.public_paths ? { public_paths: site.public_paths } : {}),
823
+ };
824
+ }
825
+ if ("patch" in site && site.patch) {
826
+ return {
827
+ patch: {
828
+ ...(site.patch.put ? { put: fileSetToCoreSpec(site.patch.put) } : {}),
829
+ ...(site.patch.delete ? { delete: site.patch.delete } : {}),
830
+ },
831
+ ...(site.public_paths ? { public_paths: site.public_paths } : {}),
832
+ };
833
+ }
834
+ return { public_paths: site.public_paths };
835
+ }
720
836
  function siteToWire(site) {
721
837
  if ("replace" in site && site.replace) {
722
838
  return {
@@ -743,6 +859,21 @@ function i18nToWire(i18n) {
743
859
  ...(i18n.unknownLocalePolicy !== undefined ? { unknown_locale_policy: i18n.unknownLocalePolicy } : {}),
744
860
  };
745
861
  }
862
+ function releaseSpecToCoreSpec(spec) {
863
+ return {
864
+ project: spec.project,
865
+ ...(spec.base !== undefined ? { base: spec.base } : {}),
866
+ ...(spec.database !== undefined ? { database: databaseToCoreSpec(spec.database) } : {}),
867
+ ...(spec.secrets !== undefined ? { secrets: spec.secrets } : {}),
868
+ ...(spec.functions !== undefined ? { functions: functionsToCoreSpec(spec.functions) } : {}),
869
+ ...(spec.site !== undefined ? { site: siteToCoreSpec(spec.site) } : {}),
870
+ ...(spec.subdomains !== undefined ? { subdomains: spec.subdomains } : {}),
871
+ ...(spec.routes !== undefined ? { routes: spec.routes } : {}),
872
+ ...(spec.checks !== undefined ? { checks: spec.checks } : {}),
873
+ ...(spec.assets !== undefined ? { assets: spec.assets } : {}),
874
+ ...(spec.i18n !== undefined ? { i18n: spec.i18n } : {}),
875
+ };
876
+ }
746
877
  function releaseSpecToWire(spec) {
747
878
  return {
748
879
  project_id: spec.project,
@@ -758,19 +889,24 @@ function releaseSpecToWire(spec) {
758
889
  ...(spec.i18n !== undefined ? { i18n: spec.i18n === null ? null : i18nToWire(spec.i18n) } : {}),
759
890
  };
760
891
  }
761
- async function planInternal(client, spec, idempotencyKey, dryRun = false) {
892
+ async function planInternal(client, spec, idempotencyKey, dryRun = false, target = "cloud") {
762
893
  const ciCredentials = isCiClient(client);
763
894
  validateSpec(spec);
764
895
  if (ciCredentials)
765
896
  assertCiDeployableSpec(spec);
766
- const { normalized, byteReaders } = await normalizeReleaseSpec(client, spec);
767
- await preflightTierFunctionLimits(client, normalized, ciCredentials);
768
- const wireSpec = releaseSpecToWire(normalized);
769
- // The gateway expects { spec, manifest_ref?, idempotency_key? } with
770
- // snake_case ReleaseSpec wire JSON. For oversized specs the SDK uploads
771
- // the manifest JSON to CAS first and references it; the gateway still
772
- // needs `spec` in the body (with at least the project), so we keep a
773
- // minimal stub there.
897
+ const isCore = target === "core";
898
+ const { normalized, byteReaders } = await normalizeReleaseSpec(client, spec, {
899
+ inlineMigrationSql: isCore,
900
+ });
901
+ if (!isCore)
902
+ await preflightTierFunctionLimits(client, normalized, ciCredentials);
903
+ const wireSpec = isCore ? releaseSpecToCoreSpec(normalized) : releaseSpecToWire(normalized);
904
+ // The gateway expects { spec, manifest_ref?, idempotency_key? }. Cloud
905
+ // receives the legacy snake_case apply wire shape; Core receives the
906
+ // open ReleaseSpec shape from @run402/release. For oversized Cloud specs
907
+ // the SDK uploads the manifest JSON to CAS first and references it; the
908
+ // gateway still needs `spec` in the body (with at least the project), so
909
+ // we keep a minimal stub there.
774
910
  const inlineBody = { spec: wireSpec };
775
911
  if (idempotencyKey && !dryRun)
776
912
  inlineBody.idempotency_key = idempotencyKey;
@@ -779,6 +915,15 @@ async function planInternal(client, spec, idempotencyKey, dryRun = false) {
779
915
  if (inlineBytes <= PLAN_BODY_LIMIT_BYTES) {
780
916
  body = inlineBody;
781
917
  }
918
+ else if (isCore) {
919
+ throw new Run402DeployError("Core deploy planning requires an inline spec under the gateway body cap; manifest_ref is not supported by Core Developer Preview yet.", {
920
+ code: "DRY_RUN_REQUIRES_INLINE_SPEC",
921
+ phase: "validate",
922
+ resource: "manifest_ref",
923
+ retryable: false,
924
+ context: "planning Core deploy",
925
+ });
926
+ }
782
927
  else {
783
928
  if (dryRun) {
784
929
  throw new Run402DeployError("Dry-run deploy planning requires an inline spec under the gateway body cap; the normalized deploy plan would require manifest_ref.", {
@@ -1250,6 +1395,33 @@ async function uploadMissing(client, projectId, presence, byteReaders, emit) {
1250
1395
  // above; this call is the plan-level idempotency anchor.
1251
1396
  await client.request(`/content/v1/plans/${encodeURIComponent(planRes.plan_id)}/commit`, { method: "POST", headers, body: {}, context: "committing content upload" });
1252
1397
  }
1398
+ async function uploadCoreContent(client, projectId, byteReaders, emit) {
1399
+ const entries = [...byteReaders.entries()].sort(([a], [b]) => a.localeCompare(b));
1400
+ const total = entries.length;
1401
+ let done = 0;
1402
+ for (const [sha256, reader] of entries) {
1403
+ const bytes = await reader();
1404
+ await client.request(`/projects/v1/${encodeURIComponent(projectId)}/content`, {
1405
+ method: "POST",
1406
+ body: {
1407
+ sha256,
1408
+ size: bytes.byteLength,
1409
+ content_type: reader.contentType ?? "application/octet-stream",
1410
+ bytes_base64: base64FromBytes(bytes),
1411
+ },
1412
+ context: "staging Core deploy content",
1413
+ });
1414
+ done += 1;
1415
+ emit({
1416
+ type: "content.upload.progress",
1417
+ label: reader.label ?? sha256,
1418
+ sha256,
1419
+ done,
1420
+ total,
1421
+ ...(reader.slice ? { slice_kind: reader.slice } : {}),
1422
+ });
1423
+ }
1424
+ }
1253
1425
  async function uploadOneWithRetry(fetchFn, session, bytes) {
1254
1426
  const MAX_ATTEMPTS = 3;
1255
1427
  for (let attempt = 1;; attempt++) {
@@ -1555,7 +1727,7 @@ async function startInternal(client, spec, opts) {
1555
1727
  ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
1556
1728
  });
1557
1729
  const { planId } = requirePersistedPlan(plan, "starting deploy");
1558
- const commit = await commitInternal(client, planId, opts.idempotencyKey, spec.project);
1730
+ const commit = requireCloudCommitResponse(await commitInternal(client, planId, opts.idempotencyKey, spec.project), "starting deploy");
1559
1731
  return await pollUntilReady(client, commit, plan.diff, plan.warnings, emit, spec.project, sliceKinds);
1560
1732
  })();
1561
1733
  // Avoid an unhandled-rejection at construction time. Consumers must call
@@ -1693,6 +1865,7 @@ const FUNCTION_SPEC_FIELDS = new Set([
1693
1865
  "requireAuth",
1694
1866
  "requireRole",
1695
1867
  "class",
1868
+ "capabilities",
1696
1869
  ]);
1697
1870
  const FUNCTION_CONFIG_FIELDS = new Set(["timeoutSeconds", "memoryMb"]);
1698
1871
  const SITE_SPEC_FIELDS = new Set(["replace", "patch", "public_paths"]);
@@ -2316,6 +2489,7 @@ function invalidRouteSpec(message, resource) {
2316
2489
  function normalizePlanResponse(plan) {
2317
2490
  const raw = plan;
2318
2491
  const warnings = Array.isArray(raw.warnings) ? raw.warnings : [];
2492
+ const missingContent = Array.isArray(raw.missing_content) ? raw.missing_content : [];
2319
2493
  if (raw.kind === "plan_response") {
2320
2494
  const diff = {
2321
2495
  is_noop: raw.is_noop,
@@ -2332,6 +2506,7 @@ function normalizePlanResponse(plan) {
2332
2506
  return {
2333
2507
  ...plan,
2334
2508
  warnings,
2509
+ missing_content: missingContent,
2335
2510
  diff,
2336
2511
  payment_required: raw.payment_required ?? null,
2337
2512
  };
@@ -2339,6 +2514,8 @@ function normalizePlanResponse(plan) {
2339
2514
  return {
2340
2515
  ...plan,
2341
2516
  warnings,
2517
+ missing_content: missingContent,
2518
+ diff: plan.diff ?? {},
2342
2519
  };
2343
2520
  }
2344
2521
  function requirePersistedPlan(plan, context) {
@@ -2353,6 +2530,77 @@ function requirePersistedPlan(plan, context) {
2353
2530
  context,
2354
2531
  });
2355
2532
  }
2533
+ function requirePlanId(plan, context) {
2534
+ if (plan.plan_id)
2535
+ return plan.plan_id;
2536
+ throw new Run402DeployError("Plan response cannot be committed because it does not include plan_id.", {
2537
+ code: "DRY_RUN_PLAN_NOT_COMMITTABLE",
2538
+ phase: "plan",
2539
+ resource: "plan_id",
2540
+ retryable: false,
2541
+ context,
2542
+ });
2543
+ }
2544
+ function isCoreCommitResponse(commit) {
2545
+ return (typeof commit.plan_id === "string" &&
2546
+ typeof commit.project_id === "string" &&
2547
+ typeof commit.release_digest === "string" &&
2548
+ (commit.status === "committed" ||
2549
+ commit.status === "noop" ||
2550
+ commit.status === "deferred"));
2551
+ }
2552
+ function requireCloudCommitResponse(commit, context) {
2553
+ if (!isCoreCommitResponse(commit))
2554
+ return commit;
2555
+ throw new Run402DeployError("Core commit response reached a Cloud operation-polling path.", {
2556
+ code: "INTERNAL_ERROR",
2557
+ phase: "commit",
2558
+ retryable: false,
2559
+ context,
2560
+ });
2561
+ }
2562
+ async function coreDeployResult(client, commit, diff, warnings, emit, projectId, sliceKinds = []) {
2563
+ if (commit.status === "deferred") {
2564
+ throw new Run402DeployError(commit.deferred_reason ?? "Core deploy commit deferred.", {
2565
+ code: "INTERNAL_ERROR",
2566
+ phase: commit.deferred_phase ?? "commit",
2567
+ retryable: true,
2568
+ context: "committing Core deploy",
2569
+ });
2570
+ }
2571
+ const urls = await coreProjectUrls(client, projectId);
2572
+ emit({
2573
+ type: "ready",
2574
+ releaseId: commit.release_id,
2575
+ urls,
2576
+ ...(sliceKinds.length > 0 ? { slice_kinds: sliceKinds } : {}),
2577
+ });
2578
+ return {
2579
+ release_id: commit.release_id,
2580
+ operation_id: `core:${commit.plan_id}`,
2581
+ urls,
2582
+ diff,
2583
+ warnings,
2584
+ };
2585
+ }
2586
+ async function coreProjectUrls(client, projectId) {
2587
+ try {
2588
+ const project = await client.request(`/projects/v1/${encodeURIComponent(projectId)}`, {
2589
+ method: "GET",
2590
+ context: "fetching Core project endpoints",
2591
+ });
2592
+ const staticUrl = project.endpoints?.static_base_url;
2593
+ return {
2594
+ ...(staticUrl ? { site: staticUrl, static: staticUrl } : {}),
2595
+ ...(project.endpoints?.rest_url ? { rest: project.endpoints.rest_url } : {}),
2596
+ ...(project.endpoints?.storage_base_url ? { storage: project.endpoints.storage_base_url } : {}),
2597
+ };
2598
+ }
2599
+ catch {
2600
+ const staticUrl = `${client.apiBase.replace(/\/+$/, "")}/projects/v1/${encodeURIComponent(projectId)}/static`;
2601
+ return { site: staticUrl, static: staticUrl };
2602
+ }
2603
+ }
2356
2604
  function emitPlanWarnings(plan, emit) {
2357
2605
  if (plan.warnings.length > 0) {
2358
2606
  emit({ type: "plan.warnings", warnings: plan.warnings });
@@ -2526,7 +2774,7 @@ function invalidSecretSpec(message, resource) {
2526
2774
  context: "validating spec",
2527
2775
  });
2528
2776
  }
2529
- async function normalizeReleaseSpec(client, spec) {
2777
+ async function normalizeReleaseSpec(client, spec, opts = {}) {
2530
2778
  const byteReaders = new Map();
2531
2779
  // Slice-tagged `remember`. Each slice category creates its own remember
2532
2780
  // closure so the registered reader carries `reader.slice = "release" |
@@ -2585,7 +2833,7 @@ async function normalizeReleaseSpec(client, spec) {
2585
2833
  db.zero_downtime = spec.database.zero_downtime;
2586
2834
  }
2587
2835
  if (spec.database.migrations && spec.database.migrations.length > 0) {
2588
- db.migrations = await Promise.all(spec.database.migrations.map(async (m) => normalizeMigration(client, spec.project, m, rememberRelease)));
2836
+ db.migrations = await Promise.all(spec.database.migrations.map(async (m) => normalizeMigration(client, spec.project, m, rememberRelease, opts)));
2589
2837
  }
2590
2838
  normalized.database = db;
2591
2839
  }
@@ -2667,6 +2915,8 @@ async function normalizeFunction(fn, remember) {
2667
2915
  out.requireRole = fn.requireRole;
2668
2916
  if (fn.class !== undefined)
2669
2917
  out.class = fn.class;
2918
+ if (fn.capabilities !== undefined)
2919
+ out.capabilities = [...fn.capabilities];
2670
2920
  if (fn.source !== undefined) {
2671
2921
  const resolved = await resolveContent(fn.source, "function source");
2672
2922
  out.source = remember(resolved);
@@ -2966,7 +3216,7 @@ async function normalizeAssetSlice(slice, remember) {
2966
3216
  }
2967
3217
  return out;
2968
3218
  }
2969
- async function normalizeMigration(client, projectId, m, remember) {
3219
+ async function normalizeMigration(client, projectId, m, remember, opts = {}) {
2970
3220
  if (!m.id) {
2971
3221
  throw new Run402DeployError("MigrationSpec.id is required", {
2972
3222
  code: "INVALID_SPEC",
@@ -2978,6 +3228,7 @@ async function normalizeMigration(client, projectId, m, remember) {
2978
3228
  });
2979
3229
  }
2980
3230
  let sql_ref;
3231
+ let sql;
2981
3232
  let checksum;
2982
3233
  if (m.sql_ref) {
2983
3234
  sql_ref = m.sql_ref;
@@ -2986,9 +3237,14 @@ async function normalizeMigration(client, projectId, m, remember) {
2986
3237
  else if (m.sql !== undefined) {
2987
3238
  const bytes = new TextEncoder().encode(m.sql);
2988
3239
  const sha256 = await sha256Hex(bytes);
2989
- const ref = { sha256, size: bytes.byteLength, contentType: "application/sql" };
2990
- remember({ ref, reader: makeBytesReader(bytes, `migration:${m.id}`) });
2991
- sql_ref = ref;
3240
+ if (opts.inlineMigrationSql) {
3241
+ sql = m.sql;
3242
+ }
3243
+ else {
3244
+ const ref = { sha256, size: bytes.byteLength, contentType: "application/sql" };
3245
+ remember({ ref, reader: makeBytesReader(bytes, `migration:${m.id}`) });
3246
+ sql_ref = ref;
3247
+ }
2992
3248
  checksum = m.checksum ?? sha256;
2993
3249
  }
2994
3250
  else {
@@ -3004,7 +3260,11 @@ async function normalizeMigration(client, projectId, m, remember) {
3004
3260
  context: "validating spec",
3005
3261
  });
3006
3262
  }
3007
- const out = { id: m.id, checksum, sql_ref };
3263
+ const out = { id: m.id, checksum };
3264
+ if (sql_ref)
3265
+ out.sql_ref = sql_ref;
3266
+ if (sql !== undefined)
3267
+ out.sql = sql;
3008
3268
  if (m.transaction)
3009
3269
  out.transaction = m.transaction;
3010
3270
  return out;