@treeseed/sdk 0.5.2 → 0.6.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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +46 -0
- package/dist/operations/providers/default.js +1 -1
- package/dist/operations/services/config-runtime.d.ts +49 -42
- package/dist/operations/services/config-runtime.js +465 -142
- package/dist/operations/services/deploy.d.ts +298 -0
- package/dist/operations/services/deploy.js +381 -137
- package/dist/operations/services/git-workflow.d.ts +9 -0
- package/dist/operations/services/git-workflow.js +32 -0
- package/dist/operations/services/github-api.d.ts +115 -0
- package/dist/operations/services/github-api.js +455 -0
- package/dist/operations/services/github-automation.d.ts +19 -33
- package/dist/operations/services/github-automation.js +44 -131
- package/dist/operations/services/key-agent.d.ts +20 -1
- package/dist/operations/services/key-agent.js +267 -102
- package/dist/operations/services/knowledge-coop-launch.d.ts +2 -3
- package/dist/operations/services/knowledge-coop-launch.js +26 -12
- package/dist/operations/services/project-platform.d.ts +157 -150
- package/dist/operations/services/project-platform.js +129 -26
- package/dist/operations/services/railway-api.d.ts +244 -0
- package/dist/operations/services/railway-api.js +882 -0
- package/dist/operations/services/railway-deploy.d.ts +171 -27
- package/dist/operations/services/railway-deploy.js +672 -172
- package/dist/operations/services/runtime-tools.d.ts +18 -0
- package/dist/operations/services/runtime-tools.js +19 -6
- package/dist/operations/services/workspace-preflight.js +2 -2
- package/dist/platform/contracts.d.ts +7 -0
- package/dist/platform/deploy-config.js +23 -0
- package/dist/platform/deploy-runtime.d.ts +1 -0
- package/dist/platform/deploy-runtime.js +7 -9
- package/dist/platform/env.yaml +10 -9
- package/dist/platform/environment.js +4 -0
- package/dist/platform/plugin.d.ts +6 -0
- package/dist/platform/plugins/constants.d.ts +1 -0
- package/dist/platform/plugins/constants.js +1 -0
- package/dist/platform/plugins/runtime.d.ts +4 -0
- package/dist/platform/plugins/runtime.js +8 -1
- package/dist/platform/published-content.js +27 -4
- package/dist/platform/tenant/runtime-config.js +33 -24
- package/dist/plugin-default.d.ts +1 -0
- package/dist/plugin-default.js +1 -0
- package/dist/reconcile/builtin-adapters.d.ts +3 -0
- package/dist/reconcile/builtin-adapters.js +2093 -0
- package/dist/reconcile/contracts.d.ts +155 -0
- package/dist/reconcile/contracts.js +0 -0
- package/dist/reconcile/desired-state.d.ts +179 -0
- package/dist/reconcile/desired-state.js +319 -0
- package/dist/reconcile/engine.d.ts +405 -0
- package/dist/reconcile/engine.js +356 -0
- package/dist/reconcile/errors.d.ts +5 -0
- package/dist/reconcile/errors.js +13 -0
- package/dist/reconcile/index.d.ts +7 -0
- package/dist/reconcile/index.js +7 -0
- package/dist/reconcile/registry.d.ts +7 -0
- package/dist/reconcile/registry.js +64 -0
- package/dist/reconcile/state.d.ts +7 -0
- package/dist/reconcile/state.js +303 -0
- package/dist/reconcile/units.d.ts +6 -0
- package/dist/reconcile/units.js +68 -0
- package/dist/scripts/config-treeseed.js +27 -19
- package/dist/scripts/tenant-deploy.js +35 -14
- package/dist/workflow/operations.js +127 -22
- package/dist/workflow-support.d.ts +3 -1
- package/dist/workflow-support.js +50 -0
- package/dist/workflow.d.ts +2 -0
- package/package.json +7 -1
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { createTreeseedReconcileRegistry } from "./registry.js";
|
|
2
|
+
import { deriveTreeseedDesiredUnits } from "./desired-state.js";
|
|
3
|
+
import { ensureTreeseedPersistedUnitState, desiredUnitSpecHash, loadTreeseedReconcileState, updateTreeseedPersistedUnitState, writeTreeseedReconcileState } from "./state.js";
|
|
4
|
+
import { reverseTopologicallySortedUnits, topologicallySortDesiredUnits } from "./units.js";
|
|
5
|
+
function nowIso() {
|
|
6
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7
|
+
}
|
|
8
|
+
function toErrorMessage(error) {
|
|
9
|
+
return error instanceof Error ? error.message : String(error);
|
|
10
|
+
}
|
|
11
|
+
function wrapAdapterFailure(stage, provider, unitType, unitId, error) {
|
|
12
|
+
const message = `Treeseed reconcile adapter failed during ${stage} for ${provider}:${unitType} (${unitId}): ${toErrorMessage(error)}`;
|
|
13
|
+
const wrapped = new Error(message);
|
|
14
|
+
if (error instanceof Error && "stack" in error && typeof error.stack === "string") {
|
|
15
|
+
wrapped.stack = error.stack;
|
|
16
|
+
}
|
|
17
|
+
throw wrapped;
|
|
18
|
+
}
|
|
19
|
+
function formatVerificationFailure(verification) {
|
|
20
|
+
const details = [
|
|
21
|
+
verification.missing.length > 0 ? `missing: ${verification.missing.join("; ")}` : null,
|
|
22
|
+
verification.drifted.length > 0 ? `drifted: ${verification.drifted.join("; ")}` : null,
|
|
23
|
+
verification.warnings.length > 0 ? `warnings: ${verification.warnings.join("; ")}` : null
|
|
24
|
+
].filter(Boolean);
|
|
25
|
+
return details.length > 0 ? `Verification failed (${details.join(" | ")})` : "Verification failed.";
|
|
26
|
+
}
|
|
27
|
+
function createRunContext(tenantRoot, target, launchEnv, write) {
|
|
28
|
+
const { deployConfig } = deriveTreeseedDesiredUnits({ tenantRoot, target });
|
|
29
|
+
return {
|
|
30
|
+
tenantRoot,
|
|
31
|
+
target,
|
|
32
|
+
deployConfig,
|
|
33
|
+
launchEnv,
|
|
34
|
+
write,
|
|
35
|
+
session: /* @__PURE__ */ new Map()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function persistResult(reconcileState, previous, result) {
|
|
39
|
+
updateTreeseedPersistedUnitState(reconcileState, {
|
|
40
|
+
...previous,
|
|
41
|
+
desiredSpecHash: desiredUnitSpecHash(result.unit),
|
|
42
|
+
lastObservedAt: nowIso(),
|
|
43
|
+
lastReconciledAt: nowIso(),
|
|
44
|
+
lastVerifiedAt: result.verification?.verified ? nowIso() : previous.lastVerifiedAt,
|
|
45
|
+
lastStatus: result.verification?.verified ? "ready" : result.observed.status,
|
|
46
|
+
lastObservedState: result.observed.live,
|
|
47
|
+
lastReconciledState: result.state,
|
|
48
|
+
lastDiff: result.diff,
|
|
49
|
+
lastVerification: result.verification,
|
|
50
|
+
lastAction: result.action,
|
|
51
|
+
resourceLocators: result.resourceLocators,
|
|
52
|
+
warnings: [...result.warnings, ...result.verification?.warnings ?? []],
|
|
53
|
+
error: result.verification?.verified === false ? formatVerificationFailure(result.verification) : null
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function verifyPlanUnits({
|
|
57
|
+
plans,
|
|
58
|
+
registry,
|
|
59
|
+
context,
|
|
60
|
+
state,
|
|
61
|
+
write
|
|
62
|
+
}) {
|
|
63
|
+
const verificationMap = /* @__PURE__ */ new Map();
|
|
64
|
+
context.session.set("treeseed:verification-results", verificationMap);
|
|
65
|
+
const verificationResults = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const plan of plans) {
|
|
67
|
+
write?.(`Verifying ${plan.unit.provider}:${plan.unit.unitType}...`);
|
|
68
|
+
const adapter = registry.get(plan.unit.unitType, plan.unit.provider);
|
|
69
|
+
const persisted = ensureTreeseedPersistedUnitState(state, plan.unit);
|
|
70
|
+
const postconditions = await Promise.resolve(adapter.requiredPostconditions?.({
|
|
71
|
+
context,
|
|
72
|
+
unit: plan.unit,
|
|
73
|
+
persistedState: persisted
|
|
74
|
+
}) ?? []);
|
|
75
|
+
let verification;
|
|
76
|
+
try {
|
|
77
|
+
verification = await Promise.resolve(adapter.verify({
|
|
78
|
+
context,
|
|
79
|
+
unit: plan.unit,
|
|
80
|
+
persistedState: persisted,
|
|
81
|
+
observed: plan.observed,
|
|
82
|
+
diff: plan.diff,
|
|
83
|
+
result: null,
|
|
84
|
+
postconditions
|
|
85
|
+
}));
|
|
86
|
+
} catch (error) {
|
|
87
|
+
wrapAdapterFailure("verify", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
|
|
88
|
+
}
|
|
89
|
+
verificationMap.set(plan.unit.unitId, verification);
|
|
90
|
+
verificationResults.set(plan.unit.unitId, {
|
|
91
|
+
postconditions,
|
|
92
|
+
verification
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return verificationResults;
|
|
96
|
+
}
|
|
97
|
+
async function observeTreeseedUnits({
|
|
98
|
+
tenantRoot,
|
|
99
|
+
target,
|
|
100
|
+
env = process.env,
|
|
101
|
+
write
|
|
102
|
+
}) {
|
|
103
|
+
const { units, deployConfig } = deriveTreeseedDesiredUnits({ tenantRoot, target });
|
|
104
|
+
const registry = createTreeseedReconcileRegistry(deployConfig);
|
|
105
|
+
const reconcileState = loadTreeseedReconcileState(tenantRoot, target);
|
|
106
|
+
const context = createRunContext(tenantRoot, target, env, write);
|
|
107
|
+
const observations = /* @__PURE__ */ new Map();
|
|
108
|
+
for (const unit of topologicallySortDesiredUnits(units)) {
|
|
109
|
+
write?.(`Observing ${unit.provider}:${unit.unitType}...`);
|
|
110
|
+
const adapter = registry.get(unit.unitType, unit.provider);
|
|
111
|
+
const persisted = ensureTreeseedPersistedUnitState(reconcileState, unit);
|
|
112
|
+
let observed;
|
|
113
|
+
try {
|
|
114
|
+
observed = await Promise.resolve(adapter.observe({
|
|
115
|
+
context,
|
|
116
|
+
unit,
|
|
117
|
+
persistedState: persisted
|
|
118
|
+
}));
|
|
119
|
+
} catch (error) {
|
|
120
|
+
wrapAdapterFailure("observe", unit.provider, unit.unitType, unit.unitId, error);
|
|
121
|
+
}
|
|
122
|
+
observations.set(unit.unitId, observed);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
units,
|
|
126
|
+
observations,
|
|
127
|
+
state: reconcileState,
|
|
128
|
+
deployConfig
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function planTreeseedReconciliation({
|
|
132
|
+
tenantRoot,
|
|
133
|
+
target,
|
|
134
|
+
env = process.env,
|
|
135
|
+
write
|
|
136
|
+
}) {
|
|
137
|
+
const observed = await observeTreeseedUnits({ tenantRoot, target, env, write });
|
|
138
|
+
const registry = createTreeseedReconcileRegistry(observed.deployConfig);
|
|
139
|
+
const context = createRunContext(tenantRoot, target, env, write);
|
|
140
|
+
const plans = [];
|
|
141
|
+
for (const unit of topologicallySortDesiredUnits(observed.units)) {
|
|
142
|
+
const adapter = registry.get(unit.unitType, unit.provider);
|
|
143
|
+
const persisted = ensureTreeseedPersistedUnitState(observed.state, unit);
|
|
144
|
+
const observation = observed.observations.get(unit.unitId);
|
|
145
|
+
let diff;
|
|
146
|
+
try {
|
|
147
|
+
diff = await Promise.resolve(adapter.plan({
|
|
148
|
+
context,
|
|
149
|
+
unit,
|
|
150
|
+
persistedState: persisted,
|
|
151
|
+
observed: observation
|
|
152
|
+
}));
|
|
153
|
+
} catch (error) {
|
|
154
|
+
wrapAdapterFailure("plan", unit.provider, unit.unitType, unit.unitId, error);
|
|
155
|
+
}
|
|
156
|
+
plans.push({
|
|
157
|
+
unit,
|
|
158
|
+
observed: observation,
|
|
159
|
+
diff,
|
|
160
|
+
persisted
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
...observed,
|
|
165
|
+
plans
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
async function reconcileTreeseedTarget({
|
|
169
|
+
tenantRoot,
|
|
170
|
+
target,
|
|
171
|
+
env = process.env,
|
|
172
|
+
write
|
|
173
|
+
}) {
|
|
174
|
+
const planned = await planTreeseedReconciliation({ tenantRoot, target, env, write });
|
|
175
|
+
const registry = createTreeseedReconcileRegistry(planned.deployConfig);
|
|
176
|
+
const context = createRunContext(tenantRoot, target, env, write);
|
|
177
|
+
const results = [];
|
|
178
|
+
const verificationMap = /* @__PURE__ */ new Map();
|
|
179
|
+
context.session.set("treeseed:verification-results", verificationMap);
|
|
180
|
+
for (const plan of planned.plans) {
|
|
181
|
+
write?.(`Reconciling ${plan.unit.provider}:${plan.unit.unitType}...`);
|
|
182
|
+
const adapter = registry.get(plan.unit.unitType, plan.unit.provider);
|
|
183
|
+
const persisted = ensureTreeseedPersistedUnitState(planned.state, plan.unit);
|
|
184
|
+
try {
|
|
185
|
+
await Promise.resolve(adapter.validate?.({
|
|
186
|
+
context,
|
|
187
|
+
unit: plan.unit,
|
|
188
|
+
persistedState: persisted
|
|
189
|
+
}));
|
|
190
|
+
} catch (error) {
|
|
191
|
+
wrapAdapterFailure("validate", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
|
|
192
|
+
}
|
|
193
|
+
let result;
|
|
194
|
+
try {
|
|
195
|
+
result = await Promise.resolve(adapter.reconcile({
|
|
196
|
+
context,
|
|
197
|
+
unit: plan.unit,
|
|
198
|
+
persistedState: persisted,
|
|
199
|
+
observed: plan.observed,
|
|
200
|
+
diff: plan.diff
|
|
201
|
+
}));
|
|
202
|
+
} catch (error) {
|
|
203
|
+
wrapAdapterFailure("reconcile", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
|
|
204
|
+
}
|
|
205
|
+
write?.(`Verifying ${plan.unit.provider}:${plan.unit.unitType}...`);
|
|
206
|
+
const postconditions = await Promise.resolve(adapter.requiredPostconditions?.({
|
|
207
|
+
context,
|
|
208
|
+
unit: plan.unit,
|
|
209
|
+
persistedState: persisted
|
|
210
|
+
}) ?? []);
|
|
211
|
+
let verification;
|
|
212
|
+
try {
|
|
213
|
+
verification = await Promise.resolve(adapter.verify({
|
|
214
|
+
context,
|
|
215
|
+
unit: plan.unit,
|
|
216
|
+
persistedState: persisted,
|
|
217
|
+
observed: result.observed,
|
|
218
|
+
diff: plan.diff,
|
|
219
|
+
result,
|
|
220
|
+
postconditions
|
|
221
|
+
}));
|
|
222
|
+
} catch (error) {
|
|
223
|
+
wrapAdapterFailure("verify", plan.unit.provider, plan.unit.unitType, plan.unit.unitId, error);
|
|
224
|
+
}
|
|
225
|
+
const verifiedResult = {
|
|
226
|
+
...result,
|
|
227
|
+
verification,
|
|
228
|
+
warnings: [
|
|
229
|
+
...result.warnings,
|
|
230
|
+
...verification.warnings ?? []
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
verificationMap.set(plan.unit.unitId, verification);
|
|
234
|
+
if (!verification.verified) {
|
|
235
|
+
persistResult(planned.state, persisted, verifiedResult);
|
|
236
|
+
writeTreeseedReconcileState(tenantRoot, planned.state);
|
|
237
|
+
throw new Error(`Treeseed reconcile verification failed for ${plan.unit.provider}:${plan.unit.unitType} (${plan.unit.unitId}): ${formatVerificationFailure(verification)}`);
|
|
238
|
+
}
|
|
239
|
+
persistResult(planned.state, persisted, verifiedResult);
|
|
240
|
+
results.push(verifiedResult);
|
|
241
|
+
}
|
|
242
|
+
writeTreeseedReconcileState(tenantRoot, planned.state);
|
|
243
|
+
return {
|
|
244
|
+
target,
|
|
245
|
+
units: planned.units,
|
|
246
|
+
plans: planned.plans,
|
|
247
|
+
results,
|
|
248
|
+
state: planned.state
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
async function destroyTreeseedTargetUnits({
|
|
252
|
+
tenantRoot,
|
|
253
|
+
target,
|
|
254
|
+
env = process.env,
|
|
255
|
+
write
|
|
256
|
+
}) {
|
|
257
|
+
const { units, deployConfig } = deriveTreeseedDesiredUnits({ tenantRoot, target });
|
|
258
|
+
const registry = createTreeseedReconcileRegistry(deployConfig);
|
|
259
|
+
const reconcileState = loadTreeseedReconcileState(tenantRoot, target);
|
|
260
|
+
const context = createRunContext(tenantRoot, target, env, write);
|
|
261
|
+
const results = [];
|
|
262
|
+
for (const unit of reverseTopologicallySortedUnits(units)) {
|
|
263
|
+
const adapter = registry.get(unit.unitType, unit.provider);
|
|
264
|
+
if (!adapter.destroy) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
write?.(`Destroying ${unit.provider}:${unit.unitType}...`);
|
|
268
|
+
const persisted = ensureTreeseedPersistedUnitState(reconcileState, unit);
|
|
269
|
+
let observed;
|
|
270
|
+
try {
|
|
271
|
+
observed = await Promise.resolve(adapter.observe({
|
|
272
|
+
context,
|
|
273
|
+
unit,
|
|
274
|
+
persistedState: persisted
|
|
275
|
+
}));
|
|
276
|
+
} catch (error) {
|
|
277
|
+
wrapAdapterFailure("observe", unit.provider, unit.unitType, unit.unitId, error);
|
|
278
|
+
}
|
|
279
|
+
let result;
|
|
280
|
+
try {
|
|
281
|
+
result = await Promise.resolve(adapter.destroy({
|
|
282
|
+
context,
|
|
283
|
+
unit,
|
|
284
|
+
persistedState: persisted,
|
|
285
|
+
observed
|
|
286
|
+
}));
|
|
287
|
+
} catch (error) {
|
|
288
|
+
wrapAdapterFailure("destroy", unit.provider, unit.unitType, unit.unitId, error);
|
|
289
|
+
}
|
|
290
|
+
results.push(result);
|
|
291
|
+
}
|
|
292
|
+
return { target, results };
|
|
293
|
+
}
|
|
294
|
+
async function collectTreeseedReconcileStatus({
|
|
295
|
+
tenantRoot,
|
|
296
|
+
target,
|
|
297
|
+
env = process.env
|
|
298
|
+
}) {
|
|
299
|
+
const observed = await observeTreeseedUnits({ tenantRoot, target, env });
|
|
300
|
+
const registry = createTreeseedReconcileRegistry(observed.deployConfig);
|
|
301
|
+
const context = createRunContext(tenantRoot, target, env);
|
|
302
|
+
const plans = await Promise.all(observed.units.map(async (unit) => {
|
|
303
|
+
const adapter = registry.get(unit.unitType, unit.provider);
|
|
304
|
+
const persisted = ensureTreeseedPersistedUnitState(observed.state, unit);
|
|
305
|
+
const observation = observed.observations.get(unit.unitId);
|
|
306
|
+
const diff = await Promise.resolve(adapter.plan({
|
|
307
|
+
context,
|
|
308
|
+
unit,
|
|
309
|
+
persistedState: persisted,
|
|
310
|
+
observed: observation
|
|
311
|
+
}));
|
|
312
|
+
return {
|
|
313
|
+
unit,
|
|
314
|
+
observed: observation,
|
|
315
|
+
diff,
|
|
316
|
+
persisted
|
|
317
|
+
};
|
|
318
|
+
}));
|
|
319
|
+
const verificationResults = await verifyPlanUnits({
|
|
320
|
+
plans,
|
|
321
|
+
registry,
|
|
322
|
+
context,
|
|
323
|
+
state: observed.state
|
|
324
|
+
});
|
|
325
|
+
const units = observed.units.map((unit) => {
|
|
326
|
+
const observation = observed.observations.get(unit.unitId);
|
|
327
|
+
const verification = verificationResults.get(unit.unitId)?.verification ?? null;
|
|
328
|
+
return {
|
|
329
|
+
unitId: unit.unitId,
|
|
330
|
+
unitType: unit.unitType,
|
|
331
|
+
provider: unit.provider,
|
|
332
|
+
status: observation.status,
|
|
333
|
+
exists: observation.exists,
|
|
334
|
+
locators: observation.locators,
|
|
335
|
+
warnings: [...observation.warnings, ...verification?.warnings ?? []],
|
|
336
|
+
verification
|
|
337
|
+
};
|
|
338
|
+
});
|
|
339
|
+
const ready = units.every((unit) => unit.verification?.verified === true);
|
|
340
|
+
const blockers = units.filter((unit) => unit.verification?.verified !== true).map((unit) => `Reconcile unit ${unit.provider}:${unit.unitType} is unverified: ${formatVerificationFailure(unit.verification)}.`);
|
|
341
|
+
const warnings = units.flatMap((unit) => unit.warnings);
|
|
342
|
+
return {
|
|
343
|
+
target,
|
|
344
|
+
ready,
|
|
345
|
+
blockers,
|
|
346
|
+
warnings,
|
|
347
|
+
units
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
export {
|
|
351
|
+
collectTreeseedReconcileStatus,
|
|
352
|
+
destroyTreeseedTargetUnits,
|
|
353
|
+
observeTreeseedUnits,
|
|
354
|
+
planTreeseedReconciliation,
|
|
355
|
+
reconcileTreeseedTarget
|
|
356
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class TreeseedReconcileError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
unitId;
|
|
4
|
+
constructor(code, message, unitId = null) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "TreeseedReconcileError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.unitId = unitId;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
TreeseedReconcileError
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TreeseedDeployConfig } from '../platform/contracts.ts';
|
|
2
|
+
import type { TreeseedReconcileAdapter, TreeseedReconcileProviderId, TreeseedReconcileUnitType } from './contracts.ts';
|
|
3
|
+
export type TreeseedReconcileRegistry = {
|
|
4
|
+
adapters: TreeseedReconcileAdapter[];
|
|
5
|
+
get(unitType: TreeseedReconcileUnitType, providerId: TreeseedReconcileProviderId): TreeseedReconcileAdapter;
|
|
6
|
+
};
|
|
7
|
+
export declare function createTreeseedReconcileRegistry(config: TreeseedDeployConfig): TreeseedReconcileRegistry;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { loadTreeseedPlugins } from "../platform/plugins/runtime.js";
|
|
2
|
+
import { createCloudflareReconcileAdapters, createRailwayReconcileAdapters } from "./builtin-adapters.js";
|
|
3
|
+
function normalizeAdapterContribution(contribution, context) {
|
|
4
|
+
if (typeof contribution === "function") {
|
|
5
|
+
return contribution(context);
|
|
6
|
+
}
|
|
7
|
+
return contribution;
|
|
8
|
+
}
|
|
9
|
+
function loadPluginReconcileAdapters(config) {
|
|
10
|
+
const tenantRoot = config.__tenantRoot ?? process.cwd();
|
|
11
|
+
const plugins = loadTreeseedPlugins(config);
|
|
12
|
+
const adapters = [];
|
|
13
|
+
for (const entry of plugins) {
|
|
14
|
+
const plugin = entry.plugin;
|
|
15
|
+
const contributions = plugin.reconcileAdapters;
|
|
16
|
+
if (!contributions || typeof contributions !== "object") {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
for (const contribution of Object.values(contributions)) {
|
|
20
|
+
const resolved = normalizeAdapterContribution(contribution, {
|
|
21
|
+
projectRoot: tenantRoot,
|
|
22
|
+
deployConfig: config,
|
|
23
|
+
tenantConfig: void 0,
|
|
24
|
+
pluginConfig: entry.config ?? {}
|
|
25
|
+
});
|
|
26
|
+
if (!resolved) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
adapters.push(resolved);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return adapters;
|
|
33
|
+
}
|
|
34
|
+
function createTreeseedReconcileRegistry(config) {
|
|
35
|
+
const adapters = [
|
|
36
|
+
...createCloudflareReconcileAdapters(),
|
|
37
|
+
...createRailwayReconcileAdapters(),
|
|
38
|
+
...loadPluginReconcileAdapters(config)
|
|
39
|
+
];
|
|
40
|
+
const seenBindings = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const adapter of adapters) {
|
|
42
|
+
for (const unitType of adapter.unitTypes) {
|
|
43
|
+
const bindingKey = `${adapter.providerId}:${unitType}`;
|
|
44
|
+
const existing = seenBindings.get(bindingKey);
|
|
45
|
+
if (existing) {
|
|
46
|
+
throw new Error(`Duplicate Treeseed reconcile adapter binding for ${bindingKey} (${existing}, ${adapter.providerId}).`);
|
|
47
|
+
}
|
|
48
|
+
seenBindings.set(bindingKey, adapter.providerId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
adapters,
|
|
53
|
+
get(unitType, providerId) {
|
|
54
|
+
const adapter = adapters.find((candidate) => candidate.supports(unitType, providerId));
|
|
55
|
+
if (!adapter) {
|
|
56
|
+
throw new Error(`Treeseed reconcile adapter missing for ${providerId}:${unitType}.`);
|
|
57
|
+
}
|
|
58
|
+
return adapter;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
createTreeseedReconcileRegistry
|
|
64
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TreeseedDesiredUnit, TreeseedReconcileStateRecord, TreeseedReconcileTarget, TreeseedUnitPersistedState } from './contracts.ts';
|
|
2
|
+
export declare function migrateLegacyDeployStateUnits(legacyState: Record<string, any>, target: TreeseedReconcileTarget): Record<string, TreeseedUnitPersistedState>;
|
|
3
|
+
export declare function loadTreeseedReconcileState(tenantRoot: string, target: TreeseedReconcileTarget): TreeseedReconcileStateRecord;
|
|
4
|
+
export declare function writeTreeseedReconcileState(tenantRoot: string, reconcileState: TreeseedReconcileStateRecord): void;
|
|
5
|
+
export declare function ensureTreeseedPersistedUnitState(reconcileState: TreeseedReconcileStateRecord, unit: TreeseedDesiredUnit): TreeseedUnitPersistedState;
|
|
6
|
+
export declare function updateTreeseedPersistedUnitState(reconcileState: TreeseedReconcileStateRecord, state: TreeseedUnitPersistedState): void;
|
|
7
|
+
export declare function desiredUnitSpecHash(unit: TreeseedDesiredUnit): string;
|