@treeseed/sdk 0.12.59 → 0.12.61

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 (44) hide show
  1. package/dist/guarantees/index.js +59 -56
  2. package/dist/hosting/contracts.d.ts +0 -19
  3. package/dist/hosting/graph.d.ts +1 -86
  4. package/dist/hosting/graph.js +96 -247
  5. package/dist/local-dev/managed-dev.js +30 -8
  6. package/dist/managed-dependencies.d.ts +3 -0
  7. package/dist/managed-dependencies.js +294 -20
  8. package/dist/operations/services/deploy.js +6 -6
  9. package/dist/operations/services/deployment-readiness.js +5 -4
  10. package/dist/operations/services/git-runner.d.ts +2 -0
  11. package/dist/operations/services/git-runner.js +23 -2
  12. package/dist/operations/services/hosted-service-checks.js +28 -0
  13. package/dist/operations/services/live-hosted-service-checks.js +56 -14
  14. package/dist/operations/services/local-cleanup.d.ts +1 -0
  15. package/dist/operations/services/local-cleanup.js +28 -9
  16. package/dist/operations/services/package-adapters.js +3 -3
  17. package/dist/operations/services/railway-api.d.ts +72 -28
  18. package/dist/operations/services/railway-api.js +321 -876
  19. package/dist/operations/services/railway-cli.d.ts +47 -0
  20. package/dist/operations/services/railway-cli.js +142 -0
  21. package/dist/operations/services/railway-deploy.d.ts +5 -0
  22. package/dist/operations/services/railway-deploy.js +47 -75
  23. package/dist/operations/services/railway-source-policy.d.ts +6 -0
  24. package/dist/operations/services/railway-source-policy.js +52 -9
  25. package/dist/operations/services/repository-save-orchestrator.d.ts +2 -0
  26. package/dist/operations/services/repository-save-orchestrator.js +45 -14
  27. package/dist/operations-types.d.ts +3 -1
  28. package/dist/platform/contracts.d.ts +1 -0
  29. package/dist/platform/deploy-config.js +2 -1
  30. package/dist/reconcile/builtin-adapters.js +524 -680
  31. package/dist/reconcile/desired-state.js +5 -3
  32. package/dist/reconcile/engine.js +34 -28
  33. package/dist/reconcile/live-acceptance.js +2 -11
  34. package/dist/reconcile/providers/railway-iac.d.ts +148 -0
  35. package/dist/reconcile/providers/railway-iac.js +294 -18
  36. package/dist/scenes/runner.js +11 -11
  37. package/dist/scripts/build-dist.js +22 -0
  38. package/dist/workflow/operations.d.ts +12 -0
  39. package/dist/workflow/operations.js +265 -90
  40. package/dist/workflow/runs.d.ts +4 -0
  41. package/dist/workflow/runs.js +25 -0
  42. package/dist/workflow-support.d.ts +1 -1
  43. package/dist/workflow-support.js +3 -1
  44. package/package.json +1 -2
@@ -238,6 +238,7 @@ function deriveTreeseedDesiredUnits({
238
238
  secrets: {},
239
239
  metadata: {
240
240
  serviceKey,
241
+ serviceInstanceKey: configuredService.instanceKey ?? desiredServiceName,
241
242
  scheduleManaged: Array.isArray(configuredService.schedule) && configuredService.schedule.length > 0,
242
243
  scheduleBootstrap: false,
243
244
  scheduleDeployScopes: ["staging", "prod"],
@@ -293,13 +294,14 @@ function deriveTreeseedDesiredUnits({
293
294
  return isPublicTreeDxNodeServiceKey(serviceKey) ? "api-runtime" : "api-runtime";
294
295
  }
295
296
  })();
297
+ const runtimeInstanceKey = serviceKey === "operationsRunner" ? configuredService.instanceKey ?? desiredServiceName : serviceKey;
296
298
  add({
297
- unitId: createTreeseedReconcileUnitId(runtimeUnitType, serviceKey),
299
+ unitId: createTreeseedReconcileUnitId(runtimeUnitType, runtimeInstanceKey),
298
300
  unitType: runtimeUnitType,
299
301
  provider: "treeseed",
300
302
  identity,
301
303
  target,
302
- logicalName: serviceKey,
304
+ logicalName: runtimeInstanceKey,
303
305
  dependencies: [
304
306
  concreteId,
305
307
  ...apiCustomDomainId ? [apiCustomDomainId] : [],
@@ -310,7 +312,7 @@ function deriveTreeseedDesiredUnits({
310
312
  publicBaseUrl: serviceState?.publicBaseUrl ?? null
311
313
  },
312
314
  secrets: {},
313
- metadata: { bootstrapSystem: serviceBootstrapSystem }
315
+ metadata: { serviceKey, bootstrapSystem: serviceBootstrapSystem }
314
316
  });
315
317
  }
316
318
  return { deployConfig, legacyState, units };
@@ -309,7 +309,7 @@ async function reconcileTreeseedTarget({
309
309
  };
310
310
  const unitStartMs = performance.now();
311
311
  timingEntries.push(unitTiming);
312
- write?.(`Applying ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName})...`);
312
+ write?.(`${planOnly ? "Planning" : "Applying"} ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName})...`);
313
313
  const adapter = registry.get(plan.unit.unitType, plan.unit.provider);
314
314
  const persisted = ensureTreeseedPersistedUnitState(planned.state, plan.unit);
315
315
  try {
@@ -372,34 +372,40 @@ async function reconcileTreeseedTarget({
372
372
  unitTiming.status = "failed";
373
373
  wrapAdapterFailure("apply", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
374
374
  }
375
+ if (planOnly) {
376
+ const plannedResult = { ...result, verification: null };
377
+ unitTiming.durationMs = elapsedMs(unitStartMs);
378
+ unitTiming.status = "success";
379
+ write?.(`Finished plan for ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName}) in ${formatDurationMs(unitTiming.durationMs)}.`);
380
+ results.push(plannedResult);
381
+ return;
382
+ }
375
383
  let refreshedObserved = result.observed;
376
- if (!planOnly) {
377
- try {
378
- const stageStartMs = performance.now();
379
- refreshedObserved = await Promise.resolve(adapter.refresh({
380
- context,
381
- unit: plan.unit,
382
- persistedState: persisted
383
- }));
384
- result = {
385
- ...result,
386
- observed: refreshedObserved
387
- };
388
- unitTiming.children?.push({
389
- name: `${unitTiming.name}:refresh-after-apply`,
390
- durationMs: elapsedMs(stageStartMs),
391
- status: "success"
392
- });
393
- } catch (error) {
394
- unitTiming.children?.push({
395
- name: `${unitTiming.name}:refresh-after-apply`,
396
- durationMs: elapsedMs(unitStartMs),
397
- status: "failed"
398
- });
399
- unitTiming.durationMs = elapsedMs(unitStartMs);
400
- unitTiming.status = "failed";
401
- wrapAdapterFailure("refresh", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
402
- }
384
+ try {
385
+ const stageStartMs = performance.now();
386
+ refreshedObserved = await Promise.resolve(adapter.refresh({
387
+ context,
388
+ unit: plan.unit,
389
+ persistedState: persisted
390
+ }));
391
+ result = {
392
+ ...result,
393
+ observed: refreshedObserved
394
+ };
395
+ unitTiming.children?.push({
396
+ name: `${unitTiming.name}:refresh-after-apply`,
397
+ durationMs: elapsedMs(stageStartMs),
398
+ status: "success"
399
+ });
400
+ } catch (error) {
401
+ unitTiming.children?.push({
402
+ name: `${unitTiming.name}:refresh-after-apply`,
403
+ durationMs: elapsedMs(unitStartMs),
404
+ status: "failed"
405
+ });
406
+ unitTiming.durationMs = elapsedMs(unitStartMs);
407
+ unitTiming.status = "failed";
408
+ wrapAdapterFailure("refresh", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
403
409
  }
404
410
  write?.(`Verifying ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.logicalName})...`);
405
411
  const postconditions = await Promise.resolve(adapter.requiredPostconditions?.({
@@ -11,6 +11,7 @@ import { runTreeseedGit } from "../operations/services/git-runner.js";
11
11
  import {
12
12
  deleteRailwayProject,
13
13
  ensureRailwayEnvironment,
14
+ ensureRailwayCustomDomain,
14
15
  ensureRailwayGeneratedServiceDomain,
15
16
  ensureRailwayPostgresService,
16
17
  ensureRailwayProject,
@@ -21,7 +22,6 @@ import {
21
22
  listRailwayServices,
22
23
  listRailwayVariables,
23
24
  listRailwayVolumes,
24
- railwayGraphqlRequest,
25
25
  resolveRailwayWorkspaceContext,
26
26
  upsertRailwayVariables
27
27
  } from "../operations/services/railway-api.js";
@@ -1217,16 +1217,7 @@ async function runRailwayAcceptance(cwd, environment, runId, prefix, env, fetchI
1217
1217
  const generatedDomain = await ensureRailwayGeneratedServiceDomain({ projectId, environmentId, serviceId, targetPort: 80, env, fetchImpl });
1218
1218
  let customDomainCreated = false;
1219
1219
  try {
1220
- await railwayGraphqlRequest({
1221
- query: `
1222
- mutation TreeseedLiveRailwayCustomDomainCreate($input: CustomDomainCreateInput!) {
1223
- customDomainCreate(input: $input) { id domain serviceId environmentId }
1224
- }
1225
- `.trim(),
1226
- variables: { input: { projectId, environmentId, serviceId, domain: customDomain } },
1227
- env,
1228
- fetchImpl
1229
- });
1220
+ await ensureRailwayCustomDomain({ projectId, environmentId, serviceId, domain: customDomain, env, fetchImpl });
1230
1221
  customDomainCreated = true;
1231
1222
  } catch {
1232
1223
  customDomainCreated = false;
@@ -14,10 +14,51 @@ export type TreeseedRailwayIacService = {
14
14
  healthcheckPath?: string | null;
15
15
  healthcheckTimeoutSeconds?: number | null;
16
16
  runtimeMode?: string | null;
17
+ numReplicas?: number | null;
17
18
  volumeMountPath?: string | null;
19
+ volumeName?: string | null;
20
+ volumeAddress?: string | null;
18
21
  variables?: Record<string, string>;
19
22
  secrets?: Record<string, string>;
20
23
  detachVolumeIds?: string[];
24
+ customDomains?: string[];
25
+ };
26
+ export type TreeseedRailwayObservedService = {
27
+ id: string;
28
+ name: string;
29
+ };
30
+ export type TreeseedRailwayObservedVolumeInstance = {
31
+ id?: string | null;
32
+ environmentId?: string | null;
33
+ serviceId?: string | null;
34
+ mountPath?: string | null;
35
+ state?: string | null;
36
+ isPendingDeletion?: boolean | null;
37
+ deletedAt?: string | null;
38
+ };
39
+ export type TreeseedRailwayObservedVolume = {
40
+ id: string;
41
+ name?: string | null;
42
+ instances: TreeseedRailwayObservedVolumeInstance[];
43
+ };
44
+ export type TreeseedRailwayVolumeBinding = {
45
+ serviceName: string;
46
+ volumeId: string;
47
+ volumeName: string;
48
+ canonicalVolumeName: string;
49
+ mode: 'canonical' | 'environment-owned' | 'shared-legacy';
50
+ reason: string;
51
+ };
52
+ export type TreeseedRailwayVolumeBindingResult = {
53
+ bindings: TreeseedRailwayVolumeBinding[];
54
+ blockedReasons: string[];
55
+ };
56
+ export type TreeseedRailwayPendingVolumeCollision = {
57
+ serviceName: string;
58
+ volumeId: string;
59
+ canonicalVolumeName: string;
60
+ mountPath: string;
61
+ serviceId: string | null;
21
62
  };
22
63
  export type TreeseedRailwayIacDatabase = {
23
64
  serviceName: string;
@@ -57,6 +98,111 @@ export type RailwayIacValidationResult = {
57
98
  blockedReasons: string[];
58
99
  allowedDrift: string[];
59
100
  };
101
+ export declare function waitForRailwayVolumeAdoptionResources<TService, TVolume>({ load, serviceName, volumeId, attempts, intervalMs, sleep, }: {
102
+ load: () => Promise<{
103
+ services: Array<TService & {
104
+ name?: string | null;
105
+ }>;
106
+ volumes: Array<TVolume & {
107
+ id?: string | null;
108
+ }>;
109
+ }>;
110
+ serviceName: string;
111
+ volumeId: string;
112
+ attempts?: number;
113
+ intervalMs?: number;
114
+ sleep?: (milliseconds: number) => Promise<unknown>;
115
+ }): Promise<{
116
+ service: TService & {
117
+ name?: string | null;
118
+ };
119
+ volume: TVolume & {
120
+ id?: string | null;
121
+ };
122
+ services: (TService & {
123
+ name?: string | null;
124
+ })[];
125
+ volumes: (TVolume & {
126
+ id?: string | null;
127
+ })[];
128
+ attempt: number;
129
+ } | null>;
130
+ export declare function waitForRailwayServices<TService extends {
131
+ name?: string | null;
132
+ }>({ load, serviceNames, attempts, intervalMs, sleep, }: {
133
+ load: () => Promise<TService[]>;
134
+ serviceNames: Iterable<string>;
135
+ attempts?: number;
136
+ intervalMs?: number;
137
+ sleep?: (milliseconds: number) => Promise<unknown>;
138
+ }): Promise<{
139
+ services: TService[];
140
+ attempt: number;
141
+ } | null>;
142
+ export declare function waitForRailwayVolumeName<TVolume extends {
143
+ id?: string | null;
144
+ name?: string | null;
145
+ }>({ load, volumeId, expectedName, attempts, intervalMs, sleep, }: {
146
+ load: () => Promise<TVolume[]>;
147
+ volumeId: string;
148
+ expectedName: string;
149
+ attempts?: number;
150
+ intervalMs?: number;
151
+ sleep?: (milliseconds: number) => Promise<unknown>;
152
+ }): Promise<{
153
+ volume: TVolume;
154
+ attempt: number;
155
+ } | null>;
156
+ export declare function waitForRailwayVolumeDetachment<TVolume extends {
157
+ id?: string | null;
158
+ instances?: Array<{
159
+ environmentId?: string | null;
160
+ serviceId?: string | null;
161
+ }>;
162
+ }>({ load, volumeId, environmentId, serviceId, attempts, intervalMs, sleep, }: {
163
+ load: () => Promise<TVolume[]>;
164
+ volumeId: string;
165
+ environmentId: string;
166
+ serviceId: string;
167
+ attempts?: number;
168
+ intervalMs?: number;
169
+ sleep?: (milliseconds: number) => Promise<unknown>;
170
+ }): Promise<{
171
+ volume: TVolume | null;
172
+ attempt: number;
173
+ } | null>;
174
+ export declare function waitForRailwayServiceAbsence<TService extends {
175
+ id?: string | null;
176
+ }>({ load, serviceId, attempts, intervalMs, sleep, }: {
177
+ load: () => Promise<TService[]>;
178
+ serviceId: string;
179
+ attempts?: number;
180
+ intervalMs?: number;
181
+ sleep?: (milliseconds: number) => Promise<unknown>;
182
+ }): Promise<{
183
+ attempt: number;
184
+ } | null>;
185
+ export declare function cleanupStaleRailwayIacRenders(tenantRoot: string, now?: number): string[];
186
+ export declare function railwayIacApplyFailure(response: RailwayIacApplyResponse): string | null;
187
+ export declare function runRailwayIacWithRateLimitRetry<T>(run: () => Promise<T>, { delaysMs, sleep, onRetry, onWait, }?: {
188
+ delaysMs?: number[];
189
+ sleep?: (milliseconds: number) => Promise<unknown>;
190
+ onRetry?: (attempt: number, delayMs: number, error: unknown) => void;
191
+ onWait?: (attempt: number, remainingMs: number) => void;
192
+ }): Promise<T>;
193
+ export declare function findRailwayPendingVolumeNameCollisions(input: {
194
+ services: TreeseedRailwayIacService[];
195
+ liveServices?: TreeseedRailwayObservedService[];
196
+ volumes: TreeseedRailwayObservedVolume[];
197
+ }): TreeseedRailwayPendingVolumeCollision[];
198
+ export declare function resolveRailwayIacVolumeBindings(input: {
199
+ environmentId: string;
200
+ services: TreeseedRailwayIacService[];
201
+ liveServices: TreeseedRailwayObservedService[];
202
+ volumes: TreeseedRailwayObservedVolume[];
203
+ }): TreeseedRailwayVolumeBindingResult;
204
+ export declare function detachRetainedRailwayVolumeBindings(resources: ResourceNode[], bindings: TreeseedRailwayVolumeBinding[]): ResourceNode[];
205
+ export declare function detachRetainedRailwayCustomDomains(resources: ResourceNode[], domains: string[]): ResourceNode[];
60
206
  export declare function renderRailwayIacProject(input: TreeseedRailwayIacProjectInput): TreeseedRailwayIacRenderResult;
61
207
  export declare function selectRailwayIacRetainedResources(plan: Pick<RailwayIacPlanResponse, 'changeSet' | 'currentGraph'>, allowedNames: Iterable<string>): ResourceNode[];
62
208
  export declare function validateRailwayIacChangeSet(changeSet: RailwayChangeSet | undefined, desiredNames: {
@@ -66,6 +212,8 @@ export declare function validateRailwayIacChangeSet(changeSet: RailwayChangeSet
66
212
  scope: string;
67
213
  serviceSourceModes?: Record<string, string | null | undefined>;
68
214
  serviceSourceRefs?: Record<string, string | null | undefined>;
215
+ allowedResourceDeletions?: string[];
216
+ protectedResourceNames?: string[];
69
217
  }): RailwayIacValidationResult;
70
218
  export declare function planRailwayIacProject(input: TreeseedRailwayIacProjectInput, rendered?: TreeseedRailwayIacRenderResult): Promise<RailwayIacPlanResponse>;
71
219
  export declare function applyRailwayIacProject(input: TreeseedRailwayIacProjectInput, rendered?: TreeseedRailwayIacRenderResult): Promise<RailwayIacApplyResponse>;