@treeseed/sdk 0.12.20 → 0.12.21

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.
@@ -294,7 +294,8 @@ export declare function queueName(entry: any): any;
294
294
  export declare function queueId(entry: any): any;
295
295
  export declare function hasProvisionedCloudflareResources(state: any): boolean;
296
296
  export declare function purgeSourcePageCaches(tenantRoot: any, options?: {}): {
297
- skipped: any;
297
+ skipped: boolean;
298
+ reason: string;
298
299
  urls: (string | null)[];
299
300
  results: never[];
300
301
  } | {
@@ -305,9 +306,11 @@ export declare function purgeSourcePageCaches(tenantRoot: any, options?: {}): {
305
306
  success: boolean;
306
307
  }[];
307
308
  skipped?: undefined;
309
+ reason?: undefined;
308
310
  };
309
311
  export declare function purgePublishedContentCaches(tenantRoot: any, urls: any, options?: {}): {
310
- skipped: any;
312
+ skipped: boolean;
313
+ reason: string;
311
314
  urls: any;
312
315
  results: never[];
313
316
  } | {
@@ -318,6 +321,7 @@ export declare function purgePublishedContentCaches(tenantRoot: any, urls: any,
318
321
  success: boolean;
319
322
  }[];
320
323
  skipped?: undefined;
324
+ reason?: undefined;
321
325
  };
322
326
  export declare function resolveConfiguredCloudflareAccountId(deployConfig: any): any;
323
327
  export declare function collectMissingDeployInputs(tenantRoot: any): {
@@ -1683,19 +1683,30 @@ function recordCachePurgeResult(targetState, results, error = null) {
1683
1683
  targetState.purgeCount = Array.isArray(results) ? results.reduce((sum, result) => sum + (result?.count ?? 0), 0) : 0;
1684
1684
  targetState.lastError = null;
1685
1685
  }
1686
+ function resolveCloudflareCachePurgeEnv(options = {}) {
1687
+ const env = options.env ?? {};
1688
+ const token = env.TREESEED_CLOUDFLARE_API_TOKEN ?? env.CLOUDFLARE_API_TOKEN ?? process.env.TREESEED_CLOUDFLARE_API_TOKEN ?? process.env.CLOUDFLARE_API_TOKEN;
1689
+ return token ? { ...env, TREESEED_CLOUDFLARE_API_TOKEN: token, CLOUDFLARE_API_TOKEN: token } : null;
1690
+ }
1686
1691
  function purgeSourcePageCaches(tenantRoot, options = {}) {
1687
1692
  const target = normalizeTarget(options.scope ?? options.target ?? "prod");
1688
1693
  const deployConfig = loadTenantDeployConfig(tenantRoot);
1689
1694
  const state = loadDeployState(tenantRoot, deployConfig, { target });
1690
1695
  const urls = resolveSourcePagePurgeUrls(deployConfig);
1691
- if ((options.dryRun ?? false) || urls.length === 0 || !process.env.TREESEED_CLOUDFLARE_API_TOKEN) {
1696
+ const env = resolveCloudflareCachePurgeEnv(options);
1697
+ if ((options.dryRun ?? false) || urls.length === 0 || !env) {
1692
1698
  recordCachePurgeResult(state.webCache.deployPurge, urls.map((url) => ({ count: url ? 1 : 0 })));
1693
1699
  writeDeployState(tenantRoot, state, { target });
1694
- return { skipped: options.dryRun ?? false, urls, results: [] };
1700
+ return {
1701
+ skipped: true,
1702
+ reason: options.dryRun ? "dry_run" : urls.length === 0 ? "no_urls" : "missing_cloudflare_token",
1703
+ urls,
1704
+ results: []
1705
+ };
1695
1706
  }
1696
1707
  try {
1697
1708
  const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
1698
- env: { CLOUDFLARE_API_TOKEN: process.env.TREESEED_CLOUDFLARE_API_TOKEN }
1709
+ env
1699
1710
  });
1700
1711
  recordCachePurgeResult(state.webCache.deployPurge, results);
1701
1712
  writeDeployState(tenantRoot, state, { target });
@@ -1710,14 +1721,20 @@ function purgePublishedContentCaches(tenantRoot, urls, options = {}) {
1710
1721
  const target = normalizeTarget(options.scope ?? options.target ?? "prod");
1711
1722
  const deployConfig = loadTenantDeployConfig(tenantRoot);
1712
1723
  const state = loadDeployState(tenantRoot, deployConfig, { target });
1713
- if ((options.dryRun ?? false) || !urls?.length || !process.env.TREESEED_CLOUDFLARE_API_TOKEN) {
1724
+ const env = resolveCloudflareCachePurgeEnv(options);
1725
+ if ((options.dryRun ?? false) || !urls?.length || !env) {
1714
1726
  recordCachePurgeResult(state.webCache.contentPurge, (urls ?? []).map((url) => ({ count: url ? 1 : 0 })));
1715
1727
  writeDeployState(tenantRoot, state, { target });
1716
- return { skipped: options.dryRun ?? false, urls: urls ?? [], results: [] };
1728
+ return {
1729
+ skipped: true,
1730
+ reason: options.dryRun ? "dry_run" : !urls?.length ? "no_urls" : "missing_cloudflare_token",
1731
+ urls: urls ?? [],
1732
+ results: []
1733
+ };
1717
1734
  }
1718
1735
  try {
1719
1736
  const results = purgeCloudflareCacheByUrls(urls, deployConfig, {
1720
- env: { CLOUDFLARE_API_TOKEN: process.env.TREESEED_CLOUDFLARE_API_TOKEN }
1737
+ env
1721
1738
  });
1722
1739
  recordCachePurgeResult(state.webCache.contentPurge, results);
1723
1740
  writeDeployState(tenantRoot, state, { target });
@@ -3657,8 +3674,14 @@ function finalizeDeploymentState(tenantRoot, options = {}) {
3657
3674
  writeDeployState(tenantRoot, state, { target });
3658
3675
  if (target.kind === "persistent") {
3659
3676
  try {
3660
- purgeSourcePageCaches(tenantRoot, { target });
3661
- } catch {
3677
+ const purgeResult = purgeSourcePageCaches(tenantRoot, { target, env: options.env });
3678
+ if (target.scope === "prod" && purgeResult?.skipped) {
3679
+ throw new Error(`Production source-page cache purge was skipped: ${purgeResult.reason ?? "unknown"}.`);
3680
+ }
3681
+ } catch (error) {
3682
+ if (target.scope === "prod") {
3683
+ throw error;
3684
+ }
3662
3685
  }
3663
3686
  return loadDeployState(tenantRoot, deployConfig, { target });
3664
3687
  }
@@ -160,11 +160,12 @@ function collectTreeseedHostedServiceChecks(options) {
160
160
  const selectedAppId = options.appId?.trim() || null;
161
161
  const applications = discoverTreeseedApplications(tenantRoot);
162
162
  const selectedApplication = selectedAppId ? applications.find((application) => application.id === selectedAppId || application.relativeRoot === selectedAppId) : null;
163
+ const selectedService = (serviceKey) => selectedServiceKeys.size === 0 || selectedServiceKeys.has(serviceKey);
163
164
  const workspaceHasApiApplication = applications.some(
164
165
  (application) => application.roles.includes("api") || application.config.surfaces?.api?.enabled === true || application.config.services?.api?.enabled !== false && Boolean(application.config.services?.api)
165
166
  );
166
- const includeWeb = !selectedAppId || selectedAppId === "web" || selectedApplication?.roles.includes("web") === true;
167
- const includeApi = !selectedAppId || selectedAppId === "api" || selectedApplication?.roles.includes("api") === true;
167
+ const includeWeb = selectedService("web") && (!selectedAppId || selectedAppId === "web" || selectedApplication?.roles.includes("web") === true);
168
+ const includeApi = selectedService("api") && (!selectedAppId || selectedAppId === "api" || selectedApplication?.roles.includes("api") === true);
168
169
  const selectedAppHasApi = Boolean(
169
170
  selectedApplication?.roles.includes("api") || selectedApplication?.config.surfaces?.api?.enabled === true || selectedApplication?.config.services?.api?.enabled !== false && selectedApplication?.config.services?.api || selectedAppId === "web" && workspaceHasApiApplication || selectedAppId === "web" && (deployConfig.surfaces?.api?.enabled === true || deployConfig.services?.api?.enabled !== false && deployConfig.services?.api)
170
171
  );
@@ -190,12 +191,13 @@ function collectTreeseedHostedServiceChecks(options) {
190
191
  if (domain) {
191
192
  const url = String(domain).startsWith("http") ? String(domain) : `https://${domain}`;
192
193
  checks.push({ ...httpStatus(url, options), id: `http:${selectedWeb.appId}`, serviceKey: selectedWeb.appId, serviceType: "web", description: "Web public URL responds." });
193
- if (!selectedAppId || selectedAppHasApi) {
194
+ if (selectedServiceKeys.size === 0 && (!selectedAppId || selectedAppHasApi)) {
194
195
  checks.push({ ...httpStatus(`${url.replace(/\/+$/u, "")}/v1/healthz`, options), id: `http:${selectedWeb.appId}:v1-healthz`, serviceKey: selectedWeb.appId, serviceType: "web", description: "Web proxy reaches API health." });
195
196
  }
196
197
  }
197
198
  }
198
199
  for (const [surfaceKey, surface] of Object.entries(deployConfig.surfaces ?? {})) {
200
+ if (!selectedService(surfaceKey)) continue;
199
201
  if (selectedAppId === "api" && surfaceKey === "web") continue;
200
202
  if (surface && typeof surface === "object" && surface.enabled !== false && surface.provider && !["cloudflare", "railway"].includes(surface.provider)) {
201
203
  checks.push(check({
@@ -423,13 +423,14 @@ async function collectHttpObservations(options) {
423
423
  const fallbacks = /* @__PURE__ */ new Map();
424
424
  const selectedServiceKeys = selectedServiceKeySet(options);
425
425
  const selectedApplication = options.appId ? discoverTreeseedApplications(options.tenantRoot).find((application) => application.id === options.appId || application.relativeRoot === options.appId) : null;
426
- if (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("web")) {
426
+ const webHttpSelected = selectedServiceKeys.size === 0 || selectedServiceKeys.has("web");
427
+ if (webHttpSelected && (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("web"))) {
427
428
  const webConfig = selectedWebConfig(deployConfig, selectedApplication);
428
429
  const webDomain = webConfig.surfaces?.web?.environments?.[options.target]?.domain ?? webConfig.surfaces?.web?.publicBaseUrl ?? webConfig.siteUrl;
429
430
  const webUrl = urlForDomain(webDomain);
430
431
  if (webUrl) {
431
432
  urls.add(webUrl);
432
- if (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("api")) {
433
+ if (selectedServiceKeys.size === 0 && (!options.appId || options.appId === "web" || selectedApplication?.roles.includes("api"))) {
433
434
  urls.add(`${webUrl}/v1/healthz`);
434
435
  }
435
436
  const pagesProjectName = webConfig.cloudflare?.pages?.projectName;
@@ -196,7 +196,7 @@ function isTransientWranglerOutput(output) {
196
196
  return /fetch failed|timed out|etimedout|econnreset|enetunreach|temporarily unavailable|connectivity issue|internal error|aborted/i.test(output);
197
197
  }
198
198
  async function runPrefixedWranglerWithRetry(tenantRoot, args, {
199
- env = {},
199
+ env: env2 = {},
200
200
  write,
201
201
  prefix
202
202
  }) {
@@ -208,7 +208,7 @@ async function runPrefixedWranglerWithRetry(tenantRoot, args, {
208
208
  }
209
209
  const result = await runPrefixedCommand(wrangler.command, [...wrangler.argsPrefix, ...args], {
210
210
  cwd: tenantRoot,
211
- env,
211
+ env: env2,
212
212
  write,
213
213
  prefix
214
214
  });
@@ -235,7 +235,7 @@ function prepareTenantCloudflareDeploy({
235
235
  target: explicitTarget,
236
236
  dryRun,
237
237
  write,
238
- env = process.env
238
+ env: env2 = process.env
239
239
  }) {
240
240
  const target = explicitTarget ?? createPersistentDeployTarget(scope === "local" ? "staging" : scope);
241
241
  if (scope !== "local") {
@@ -260,7 +260,7 @@ function prepareTenantCloudflareDeploy({
260
260
  pagesBranchName,
261
261
  env: {
262
262
  ...process.env,
263
- ...env,
263
+ ...env2,
264
264
  CLOUDFLARE_ACCOUNT_ID: resolveConfiguredCloudflareAccountId(deployConfig),
265
265
  ...target.kind === "persistent" && target.scope !== "local" ? { TREESEED_CONTENT_SERVING_MODE: "published_runtime" } : {}
266
266
  },
@@ -478,12 +478,12 @@ async function repairHostingAfterSuccessfulDeploy(options, systems) {
478
478
  };
479
479
  }
480
480
  const environment = options.scope === "prod" ? "prod" : "staging";
481
- const env = { ...process.env, ...options.env ?? {} };
481
+ const env2 = { ...process.env, ...options.env ?? {} };
482
482
  const audit = await runTreeseedHostingAudit({
483
483
  tenantRoot: options.tenantRoot,
484
484
  environment,
485
485
  repair: false,
486
- env,
486
+ env: env2,
487
487
  hostKinds: [...hostKinds]
488
488
  });
489
489
  if (audit.ok) {
@@ -494,7 +494,7 @@ async function repairHostingAfterSuccessfulDeploy(options, systems) {
494
494
  tenantRoot: options.tenantRoot,
495
495
  environment,
496
496
  repair: true,
497
- env,
497
+ env: env2,
498
498
  hostKinds: [...hostKinds],
499
499
  write: (line) => options.write?.(`[${environment}][hosting][repair] ${line}`)
500
500
  });
@@ -955,7 +955,7 @@ async function publishContent(options, reporter, publishOptions = {}) {
955
955
  }, manifestFile, uploadOptions);
956
956
  if (contentPurgeUrls.size > 0) {
957
957
  try {
958
- purgePublishedContentCaches(options.tenantRoot, [...contentPurgeUrls].filter(Boolean), { target });
958
+ purgePublishedContentCaches(options.tenantRoot, [...contentPurgeUrls].filter(Boolean), { target, env });
959
959
  } catch {
960
960
  }
961
961
  }
@@ -1020,12 +1020,12 @@ async function provisionProjectPlatform(options) {
1020
1020
  writeWorkflowStatus("provision:resolve-bootstrap-systems");
1021
1021
  const bootstrapSystems = resolveProjectPlatformBootstrapSystems(options, siteConfig);
1022
1022
  const selectedSystems = new Set(bootstrapSystems);
1023
- const env = { ...process.env, ...options.env ?? {} };
1023
+ const env2 = { ...process.env, ...options.env ?? {} };
1024
1024
  writeWorkflowStatus(`provision:reconcile:start systems=${bootstrapSystems.join(",") || "(none)"}`);
1025
1025
  const summary = await timedPhase(timings, "provision:reconcile", () => reconcileTreeseedTarget({
1026
1026
  tenantRoot: options.tenantRoot,
1027
1027
  target,
1028
- env,
1028
+ env: env2,
1029
1029
  systems: bootstrapSystems,
1030
1030
  write: options.write,
1031
1031
  dryRun: options.dryRun
@@ -1036,7 +1036,7 @@ async function provisionProjectPlatform(options) {
1036
1036
  const verification = await timedPhase(timings, "provision:collect-reconcile-status", () => collectTreeseedReconcileStatus({
1037
1037
  tenantRoot: options.tenantRoot,
1038
1038
  target,
1039
- env,
1039
+ env: env2,
1040
1040
  systems: bootstrapSystems
1041
1041
  }));
1042
1042
  writeWorkflowStatus("provision:collect-reconcile-status:done");
@@ -1050,7 +1050,7 @@ async function provisionProjectPlatform(options) {
1050
1050
  writeWorkflowStatus("provision:ensure-wrangler-config:skipped");
1051
1051
  }
1052
1052
  const shouldValidateRailway = selectedSystems.has("api") || selectedSystems.has("agents");
1053
- const railwayValidation = shouldValidateRailway ? options.scope === "local" ? validateRailwayServiceConfiguration(options.tenantRoot, options.scope) : validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env }) : { services: [] };
1053
+ const railwayValidation = shouldValidateRailway ? options.scope === "local" ? validateRailwayServiceConfiguration(options.tenantRoot, options.scope) : validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env: env2 }) : { services: [] };
1054
1054
  const railwaySchedules = [];
1055
1055
  const railwayScheduleVerification = {
1056
1056
  ok: true,
@@ -1210,7 +1210,7 @@ async function deployProjectPlatform(options) {
1210
1210
  const selectedSystems = new Set(bootstrapSystems);
1211
1211
  const execution = options.bootstrapExecution ?? "parallel";
1212
1212
  const write = options.write;
1213
- const env = { ...process.env, ...options.env ?? {} };
1213
+ const env2 = { ...process.env, ...options.env ?? {} };
1214
1214
  writeWorkflowStatus(`deploy:bootstrap-systems ${bootstrapSystems.join(",") || "(none)"}`);
1215
1215
  writeWorkflowStatus("deploy:report-running");
1216
1216
  await reportDeployment(reporter, {
@@ -1238,7 +1238,7 @@ async function deployProjectPlatform(options) {
1238
1238
  tenantRoot: options.tenantRoot,
1239
1239
  scope: "local",
1240
1240
  dryRun: options.dryRun,
1241
- env,
1241
+ env: env2,
1242
1242
  write
1243
1243
  })
1244
1244
  });
@@ -1248,7 +1248,7 @@ async function deployProjectPlatform(options) {
1248
1248
  scope: options.scope,
1249
1249
  dryRun: options.dryRun,
1250
1250
  write,
1251
- env
1251
+ env: env2
1252
1252
  });
1253
1253
  }
1254
1254
  if (cloudflareContext && selectedSystems.has("data")) {
@@ -1283,11 +1283,11 @@ async function deployProjectPlatform(options) {
1283
1283
  const serviceResultsByKey = /* @__PURE__ */ new Map();
1284
1284
  let selectedRailwayServiceKeys = [];
1285
1285
  if (options.scope !== "local" && (selectedSystems.has("api") || selectedSystems.has("agents"))) {
1286
- const validation = validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env });
1286
+ const validation = validateRailwayDeployPrerequisites(options.tenantRoot, options.scope, { env: env2 });
1287
1287
  const selectedServices = validation.services.filter(
1288
1288
  (service) => service.key === "api" ? selectedSystems.has("api") : selectedSystems.has("agents")
1289
1289
  );
1290
- const sequentialRailwayDeploys = String(env.TREESEED_RAILWAY_DEPLOY_SEQUENTIAL ?? "").trim() === "1";
1290
+ const sequentialRailwayDeploys = String(env2.TREESEED_RAILWAY_DEPLOY_SEQUENTIAL ?? "").trim() === "1";
1291
1291
  let previousRailwayDeployNodeId = null;
1292
1292
  for (const service of selectedServices) {
1293
1293
  const system = service.key === "api" ? "api" : "agents";
@@ -1304,7 +1304,7 @@ async function deployProjectPlatform(options) {
1304
1304
  const result = await deployRailwayService(options.tenantRoot, service, {
1305
1305
  dryRun: options.dryRun,
1306
1306
  write,
1307
- env,
1307
+ env: env2,
1308
1308
  prefix: {
1309
1309
  scope: options.scope,
1310
1310
  system,
@@ -1336,7 +1336,7 @@ async function deployProjectPlatform(options) {
1336
1336
  task: "schedules",
1337
1337
  stage: "deploy"
1338
1338
  }, "Reconciling Railway schedules...");
1339
- railwaySchedules = await ensureRailwayScheduledJobs(options.tenantRoot, options.scope, { dryRun: options.dryRun, env });
1339
+ railwaySchedules = await ensureRailwayScheduledJobs(options.tenantRoot, options.scope, { dryRun: options.dryRun, env: env2 });
1340
1340
  railwayScheduleVerification = !options.dryRun ? await verifyRailwayScheduledJobs(options.tenantRoot, options.scope) : { ok: true, checks: railwaySchedules, skipped: true, reason: "dry_run" };
1341
1341
  return {
1342
1342
  service: "railway-schedules",
@@ -1358,7 +1358,8 @@ async function deployProjectPlatform(options) {
1358
1358
  if (options.scope !== "local" && !options.dryRun && (selectedSystems.has("web") || serviceResults.length > 0)) {
1359
1359
  finalizeDeploymentState(options.tenantRoot, {
1360
1360
  target: createPersistentDeployTarget(options.scope),
1361
- serviceResults
1361
+ serviceResults,
1362
+ env: env2
1362
1363
  });
1363
1364
  }
1364
1365
  if (!managesRailwaySchedules || !selectedSystems.has("agents")) {
@@ -1423,7 +1424,7 @@ async function publishProjectContent(options) {
1423
1424
  async function monitorProjectPlatform(options) {
1424
1425
  const timings = [];
1425
1426
  const reporter = resolveReporter(options.tenantRoot, options.reporter);
1426
- const env = { ...process.env, ...options.env ?? {} };
1427
+ const env2 = { ...process.env, ...options.env ?? {} };
1427
1428
  const target = createPersistentDeployTarget(options.scope === "local" ? "staging" : options.scope);
1428
1429
  const siteConfig = loadCliDeployConfig(options.tenantRoot);
1429
1430
  const selectedSystems = new Set(resolveProjectPlatformBootstrapSystems(options, siteConfig));
@@ -1434,7 +1435,7 @@ async function monitorProjectPlatform(options) {
1434
1435
  const apiBaseUrl = resolveImmediateApiProbeUrl(siteConfig, state, target);
1435
1436
  const apiMonitorEndpoints = resolveApiMonitorEndpoints(siteConfig, apiBaseUrl);
1436
1437
  const railwayResourcesPromise = options.scope === "local" || !apiSelected && !agentsSelected ? { ok: true, skipped: true, reason: options.scope === "local" ? "local_scope" : "railway_not_selected" } : timedPhase(timings, "monitor:railway-resources", () => verifyRailwayManagedResources(options.tenantRoot, options.scope, {
1437
- env,
1438
+ env: env2,
1438
1439
  settleDeployments: true,
1439
1440
  onProgress: options.write
1440
1441
  }));
@@ -1167,6 +1167,7 @@ export declare function workflowRelease(helpers: WorkflowOperationHelpers, input
1167
1167
  releaseBlockingFailures: number;
1168
1168
  };
1169
1169
  };
1170
+ productionWebVerification: Record<string, unknown> | null;
1170
1171
  backMerge: {
1171
1172
  packages: {
1172
1173
  status: string;
@@ -617,6 +617,33 @@ ${liveFailures.join("\n")}`, {
617
617
  liveVerification: live
618
618
  };
619
619
  }
620
+ async function runReleaseWebLiveVerification(root, environment, helpers, operation) {
621
+ const env = {
622
+ ...helpers.context.env,
623
+ ...collectTreeseedConfigSeedValues(root, environment, helpers.context.env)
624
+ };
625
+ const live = await collectTreeseedLiveHostedServiceChecks({
626
+ tenantRoot: root,
627
+ target: environment,
628
+ appId: "web",
629
+ serviceKeys: ["web"],
630
+ strict: true,
631
+ requireLiveRailway: false,
632
+ requireLiveHttp: true,
633
+ env
634
+ });
635
+ const liveFailures = [
636
+ ...live.checks.filter((check) => check.status === "failed").map((check) => `${check.id}: ${check.issues.join("; ") || "failed"}`),
637
+ ...live.liveObservation.issues
638
+ ];
639
+ if (liveFailures.length > 0) {
640
+ workflowError(operation, "hosted_live_verification_failed", `Production web live verification failed after root deployment:
641
+ ${liveFailures.join("\n")}`, {
642
+ details: { environment, live }
643
+ });
644
+ }
645
+ return live;
646
+ }
620
647
  function productionReleaseImageRefEnv(selectedVersions) {
621
648
  const refs = {};
622
649
  const apiVersion = selectedVersions.get("@treeseed/api");
@@ -5664,6 +5691,7 @@ ${rendered}`);
5664
5691
  runId: workflowRun.runId,
5665
5692
  onProgress: (line, stream) => helpers.write(line, stream)
5666
5693
  }).then((workflowGates) => ({ workflowGates })));
5694
+ const productionWebVerification = await executeJournalStep(root, workflowRun.runId, "production-web-live-verification", () => runReleaseWebLiveVerification(root, "prod", helpers, "release"));
5667
5695
  const backMerge = await executeJournalStep(root, workflowRun.runId, "release-back-merge", () => {
5668
5696
  const packageBackMerges = checkedOutWorkspacePackageRepos(root).filter((pkg) => selectedPackageSet.has(pkg.name)).map((pkg) => backMergeProductionIntoStaging(pkg.dir, pkg.name, releaseAdminMessage({
5669
5697
  subject: `release: back-merge ${PRODUCTION_BRANCH} into ${STAGING_BRANCH}`,
@@ -5692,6 +5720,7 @@ ${rendered}`);
5692
5720
  publishedArtifacts,
5693
5721
  productionHosting,
5694
5722
  productionApiGuarantees,
5723
+ productionWebVerification,
5695
5724
  backMerge,
5696
5725
  workspaceLinks,
5697
5726
  releasedCommit: String(rootRelease.commit.commitSha ?? ""),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.12.20",
3
+ "version": "0.12.21",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {