@treeseed/sdk 0.12.55 → 0.12.58

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.
@@ -12,7 +12,11 @@ import {
12
12
  normalizeRailwayEnvironmentName,
13
13
  resolveRailwayWorkspaceContext
14
14
  } from "./railway-api.js";
15
- import { configuredRailwayServices, findStaleTreeseedOperationsRunnerResources } from "./railway-deploy.js";
15
+ import {
16
+ configuredRailwayServices,
17
+ findStaleTreeseedOperationsRunnerResources,
18
+ isTreeseedOperationsRunnerResourceName
19
+ } from "./railway-deploy.js";
16
20
  import { discoverTreeseedApplications } from "../../hosting/apps.js";
17
21
  import {
18
22
  collectTreeseedHostedServiceChecks
@@ -235,6 +239,8 @@ async function collectRailwayObservations(options) {
235
239
  const workspace = await resolveRailwayWorkspaceContext({ env: options.env, fetchImpl: options.fetchImpl });
236
240
  const projects = await listRailwayProjects({ workspaceId: workspace.id, env: options.env, fetchImpl: options.fetchImpl });
237
241
  const configuredServices = configuredRailwayServices(options.tenantRoot, options.target, options.env).filter((entry) => serviceMatchesAppSelection(entry, options.tenantRoot, options.appId, applications)).filter((entry) => serviceIsSelected(selectedServiceKeys, entry.key));
242
+ const siblingTarget = options.target === "staging" ? "prod" : options.target === "prod" ? "staging" : null;
243
+ const configuredSiblingServices = siblingTarget ? configuredRailwayServices(options.tenantRoot, siblingTarget, options.env, { identityOnly: true }).filter((entry) => entry.enabled !== false).filter((entry) => entry.key === "operationsRunner") : [];
238
244
  if (selectedServiceKeys.size === 0 || selectedServiceKeys.has("api") || selectedServiceKeys.has("operationsRunner")) {
239
245
  for (const descriptor of treeseedDatabaseDescriptors(options.tenantRoot, options)) {
240
246
  await verifyRailwayPostgresTopology({ descriptor, configuredServices, projects, options, issues });
@@ -278,7 +284,10 @@ async function collectRailwayObservations(options) {
278
284
  const runnerScope = `${project.id}:${environment.id}`;
279
285
  if (service.key === "operationsRunner" && !inspectedRunnerScopes.has(runnerScope)) {
280
286
  inspectedRunnerScopes.add(runnerScope);
281
- const desiredRunnerNames = new Set(configuredServices.filter((entry) => entry.key === "operationsRunner").filter((entry) => entry.projectId ? entry.projectId === project.id : entry.projectName === project.name).filter((entry) => normalizeRailwayEnvironmentName(entry.railwayEnvironment) === normalizeRailwayEnvironmentName(environment.name)).map((entry) => entry.serviceName).filter(Boolean));
287
+ const desiredRunnerNames = /* @__PURE__ */ new Set([
288
+ ...configuredServices.filter((entry) => entry.key === "operationsRunner").filter((entry) => entry.projectId ? entry.projectId === project.id : entry.projectName === project.name).filter((entry) => normalizeRailwayEnvironmentName(entry.railwayEnvironment) === normalizeRailwayEnvironmentName(environment.name)).map((entry) => entry.serviceName).filter(Boolean),
289
+ ...configuredSiblingServices.filter((entry) => entry.projectId ? entry.projectId === project.id : entry.projectName === project.name).map((entry) => entry.serviceName).filter((name) => Boolean(name) && isTreeseedOperationsRunnerResourceName(name))
290
+ ]);
282
291
  const desiredRunnerServiceIds = new Set(services.filter((entry) => desiredRunnerNames.has(entry.name)).map((entry) => entry.id));
283
292
  for (const staleService of findStaleTreeseedOperationsRunnerResources(services, desiredRunnerNames)) {
284
293
  issues.push(`${staleService.name}: stale operations runner Railway service remains in project ${project.name}.`);
@@ -40,11 +40,11 @@ export declare function rewriteInternalDependenciesToStableVersions(root: any, v
40
40
  repoName: string;
41
41
  packageJsonPath: string;
42
42
  })[];
43
- export declare function rewriteProjectInternalDependenciesToStableVersions(root: any, versions: Map<string, string>): (RewrittenDevReference & {
43
+ export declare function rewriteProjectInternalDependenciesToStableVersions(root: any, versions: Map<string, string>, targetPackageNames?: ReadonlySet<string>): (RewrittenDevReference & {
44
44
  repoName: string;
45
45
  packageJsonPath: string;
46
46
  })[];
47
- export declare function collectInternalDevReferenceIssues(root?: any, packageNames?: Set<any>): {
47
+ export declare function collectInternalDevReferenceIssues(root?: any, packageNames?: Set<any>, targetPackageNames?: ReadonlySet<string>): {
48
48
  repoName: string;
49
49
  filePath: string;
50
50
  field?: string;
@@ -182,12 +182,12 @@ function rewriteInternalDependenciesToStableVersions(root = workspaceRoot(), ver
182
182
  }
183
183
  return rewrites;
184
184
  }
185
- function rewriteProjectInternalDependenciesToStableVersions(root = workspaceRoot(), versions) {
185
+ function rewriteProjectInternalDependenciesToStableVersions(root = workspaceRoot(), versions, targetPackageNames) {
186
186
  const rewrites = [];
187
187
  const installableVersions = installableInternalDependencyVersions(root, versions);
188
188
  const targets = [
189
189
  { name: "@treeseed/market", dir: root },
190
- ...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))
190
+ ...workspacePackages(root).filter((pkg) => !targetPackageNames || targetPackageNames.has(pkg.name)).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))
191
191
  ];
192
192
  for (const target of targets) {
193
193
  const packageJsonPath = resolve(target.dir, "package.json");
@@ -216,11 +216,12 @@ function rewriteProjectInternalDependenciesToStableVersions(root = workspaceRoot
216
216
  }
217
217
  return rewrites;
218
218
  }
219
- function collectInternalDevReferenceIssues(root = workspaceRoot(), packageNames = new Set(workspacePackages(root).map((pkg) => pkg.name))) {
219
+ function collectInternalDevReferenceIssues(root = workspaceRoot(), packageNames = new Set(workspacePackages(root).map((pkg) => pkg.name)), targetPackageNames) {
220
220
  const issues = [];
221
+ const workspaceTargets = workspacePackages(root).filter((pkg) => !targetPackageNames || targetPackageNames.has(pkg.name)).map((pkg) => ({ name: pkg.name, dir: pkg.dir }));
221
222
  const manifestRoots = [
222
223
  { name: "@treeseed/market", dir: root },
223
- ...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))
224
+ ...workspaceTargets
224
225
  ];
225
226
  for (const pkg of manifestRoots) {
226
227
  const packageJsonPath = resolve(pkg.dir, "package.json");
@@ -239,7 +240,7 @@ function collectInternalDevReferenceIssues(root = workspaceRoot(), packageNames
239
240
  }
240
241
  }
241
242
  }
242
- const lockRoots = [{ name: "@treeseed/market", dir: root }, ...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))];
243
+ const lockRoots = [{ name: "@treeseed/market", dir: root }, ...workspaceTargets];
243
244
  for (const lockRoot of lockRoots) {
244
245
  for (const lockName of ["package-lock.json", "npm-shrinkwrap.json"]) {
245
246
  const lockPath = resolve(lockRoot.dir, lockName);
@@ -42,6 +42,7 @@ import {
42
42
  import {
43
43
  configuredRailwayServices,
44
44
  findStaleTreeseedOperationsRunnerResources,
45
+ isTreeseedOperationsRunnerResourceName,
45
46
  validateRailwayDeployPrerequisites
46
47
  } from "../operations/services/railway-deploy.js";
47
48
  import { shouldExposeManagedHostRuntimeSecret } from "../operations/services/managed-host-security.js";
@@ -54,6 +55,7 @@ import {
54
55
  ensureRailwayServiceVolume,
55
56
  ensureRailwayPostgresService,
56
57
  deleteRailwayService,
58
+ deleteRailwayCustomDomain,
57
59
  deleteRailwayVolume,
58
60
  deployRailwayServiceInstance,
59
61
  inspectRailwayServiceDeploymentHealth,
@@ -2372,6 +2374,48 @@ async function ensureRailwayCustomDomain(input, service, domain, env, identifier
2372
2374
  if (matched) {
2373
2375
  return matched;
2374
2376
  }
2377
+ const environmentServices = await listRailwayEnvironmentServices({
2378
+ environmentId: identifiers.environmentId,
2379
+ env
2380
+ });
2381
+ for (const candidate of environmentServices) {
2382
+ const candidateServiceId = typeof candidate.id === "string" ? candidate.id.trim() : "";
2383
+ if (!candidateServiceId || candidateServiceId === identifiers.serviceId) {
2384
+ continue;
2385
+ }
2386
+ const candidateDomains = await listRailwayCustomDomains({
2387
+ projectId: identifiers.projectId,
2388
+ environmentId: identifiers.environmentId,
2389
+ serviceId: candidateServiceId,
2390
+ env
2391
+ });
2392
+ const staleAttachment = candidateDomains.find((entry) => entry.domain === domain) ?? null;
2393
+ if (!staleAttachment) {
2394
+ continue;
2395
+ }
2396
+ if (!staleAttachment.id) {
2397
+ throw new Error(`Railway custom domain ${domain} is attached to service ${candidateServiceId}, but Railway did not return its domain id for exact-state reattachment.`);
2398
+ }
2399
+ await deleteRailwayCustomDomain({ domainId: staleAttachment.id, env });
2400
+ let detached = false;
2401
+ for (let attempt = 0; attempt < 12; attempt += 1) {
2402
+ const remaining = await listRailwayCustomDomains({
2403
+ projectId: identifiers.projectId,
2404
+ environmentId: identifiers.environmentId,
2405
+ serviceId: candidateServiceId,
2406
+ env
2407
+ });
2408
+ if (!remaining.some((entry) => entry.domain === domain)) {
2409
+ detached = true;
2410
+ break;
2411
+ }
2412
+ await new Promise((resolve2) => setTimeout(resolve2, 1e3));
2413
+ }
2414
+ if (!detached) {
2415
+ throw new Error(`Railway custom domain ${domain} remained attached to service ${candidateServiceId} after deletion.`);
2416
+ }
2417
+ break;
2418
+ }
2375
2419
  const ensured = await ensureRailwayCustomDomainViaApi({
2376
2420
  projectId: identifiers.projectId,
2377
2421
  environmentId: identifiers.environmentId,
@@ -3784,13 +3828,16 @@ async function reconcileStaleOperationsRunnerResourcesForScope(input, topology)
3784
3828
  );
3785
3829
  const projects = new Map(projectEntries.map((entry) => [entry.project.id, entry]));
3786
3830
  for (const { project, environment } of projects.values()) {
3831
+ const siblingResourceNames = configuredRailwaySiblingResourceNames(input, scope, project.name);
3832
+ const retainedSiblingServiceNames = siblingResourceNames.filter((name) => !name.endsWith("-volume")).filter(isTreeseedOperationsRunnerResourceName);
3833
+ const retainedSiblingVolumeNames = siblingResourceNames.filter((name) => name.endsWith("-volume")).filter(isTreeseedOperationsRunnerResourceName);
3787
3834
  await reconcileStaleOperationsRunnerResourcesForProject(input, {
3788
3835
  scope,
3789
3836
  env: topology.env,
3790
3837
  project,
3791
3838
  environment,
3792
- desiredServiceNames,
3793
- desiredVolumeNames
3839
+ desiredServiceNames: /* @__PURE__ */ new Set([...desiredServiceNames, ...retainedSiblingServiceNames]),
3840
+ desiredVolumeNames: /* @__PURE__ */ new Set([...desiredVolumeNames, ...retainedSiblingVolumeNames])
3794
3841
  });
3795
3842
  }
3796
3843
  }
@@ -1194,8 +1194,12 @@ function updatePackageLockRootVersion(root, version) {
1194
1194
  }
1195
1195
  return { status: changed ? "updated" : "unchanged", path: "package-lock.json" };
1196
1196
  }
1197
- function applyStableWorkspaceVersionChanges(root, versions) {
1198
- for (const target of [{ name: "@treeseed/market", dir: root }, ...workspacePackages(root).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))]) {
1197
+ function applyStableWorkspaceVersionChanges(root, versions, targetPackageNames) {
1198
+ const targets = [
1199
+ { name: "@treeseed/market", dir: root },
1200
+ ...workspacePackages(root).filter((pkg) => targetPackageNames.has(pkg.name)).map((pkg) => ({ name: pkg.name, dir: pkg.dir }))
1201
+ ];
1202
+ for (const target of targets) {
1199
1203
  const packageJsonPath = resolve(target.dir, "package.json");
1200
1204
  if (!existsSync(packageJsonPath)) continue;
1201
1205
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
@@ -1220,7 +1224,7 @@ function applyStableWorkspaceVersionChanges(root, versions) {
1220
1224
  writeJsonFile(packageJsonPath, packageJson);
1221
1225
  }
1222
1226
  }
1223
- rewriteProjectInternalDependenciesToStableVersions(root, versions);
1227
+ rewriteProjectInternalDependenciesToStableVersions(root, versions, targetPackageNames);
1224
1228
  }
1225
1229
  function gitObjectCommit(repoDir, ref) {
1226
1230
  try {
@@ -6038,14 +6042,14 @@ ${blockers.join("\n")}`, {
6038
6042
  });
6039
6043
  const workspaceUnlink = await executeJournalStep(root, workflowRun.runId, "workspace-unlink", () => unlinkWorkflowWorkspaceLinks(root, helpers, effectiveInput.workspaceLinks ?? "auto"));
6040
6044
  const releaseMetadata = await executeJournalStep(root, workflowRun.runId, "prepare-release-metadata", () => {
6041
- applyStableWorkspaceVersionChanges(root, allVersions);
6045
+ applyStableWorkspaceVersionChanges(root, allVersions, selectedPackageSet);
6042
6046
  const adapterMetadata = checkedOutWorkspacePackageRepos(root).filter((pkg) => selectedPackageSet.has(pkg.name)).map((pkg) => ({
6043
6047
  name: pkg.name,
6044
6048
  version: selectedVersions.get(pkg.name) ?? null,
6045
6049
  result: selectedVersions.has(pkg.name) ? { status: "pending-package-release-step" } : { status: "skipped", reason: "no planned version" }
6046
6050
  }));
6047
6051
  const rootPackageLock = updatePackageLockRootVersion(root, plannedRelease.rootVersion);
6048
- const remainingDevReferences = collectInternalDevReferenceIssues(root, selectedPackageSet).filter((issue) => !issue.reason.startsWith("lockfile-"));
6052
+ const remainingDevReferences = collectInternalDevReferenceIssues(root, selectedPackageSet, selectedPackageSet).filter((issue) => !issue.reason.startsWith("lockfile-"));
6049
6053
  if (remainingDevReferences.length > 0) {
6050
6054
  const rendered = remainingDevReferences.map((issue) => `${issue.repoName}: ${issue.filePath} ${issue.dependencyName ?? ""} ${issue.reason} ${issue.spec}`).join("\n");
6051
6055
  throw new Error(`Stable release metadata still contains development references.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.12.55",
3
+ "version": "0.12.58",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {