@terraforge/core 0.0.4 → 0.0.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.
- package/README.md +5 -3
- package/dist/index.d.ts +339 -267
- package/dist/index.js +614 -252
- package/package.json +10 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/node.ts
|
|
2
|
-
var nodeMetaSymbol = Symbol("metadata");
|
|
2
|
+
var nodeMetaSymbol = /* @__PURE__ */ Symbol("metadata");
|
|
3
3
|
var isNode = (obj) => {
|
|
4
4
|
const meta = obj[nodeMetaSymbol];
|
|
5
5
|
return meta && typeof meta === "object" && meta !== null && "tag" in meta && typeof meta.tag === "string";
|
|
@@ -15,17 +15,14 @@ var isDataSource = (obj) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/group.ts
|
|
18
|
-
|
|
19
|
-
parent;
|
|
20
|
-
type;
|
|
21
|
-
name;
|
|
22
|
-
children = [];
|
|
18
|
+
var Group = class _Group {
|
|
23
19
|
constructor(parent, type, name) {
|
|
24
20
|
this.parent = parent;
|
|
25
21
|
this.type = type;
|
|
26
22
|
this.name = name;
|
|
27
23
|
parent?.children.push(this);
|
|
28
24
|
}
|
|
25
|
+
children = [];
|
|
29
26
|
get urn() {
|
|
30
27
|
const urn = this.parent ? this.parent.urn : "urn";
|
|
31
28
|
return `${urn}:${this.type}:{${this.name}}`;
|
|
@@ -38,8 +35,8 @@ class Group {
|
|
|
38
35
|
throw new Error(`Duplicate node found: ${meta.type}:${meta.logicalId}`);
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
|
-
if (child instanceof
|
|
42
|
-
const duplicate = this.children.filter((c) => c instanceof
|
|
38
|
+
if (child instanceof _Group) {
|
|
39
|
+
const duplicate = this.children.filter((c) => c instanceof _Group).find((c) => c.type === child.type && c.name === child.name);
|
|
43
40
|
if (duplicate) {
|
|
44
41
|
throw new Error(`Duplicate group found: ${child.type}:${child.name}`);
|
|
45
42
|
}
|
|
@@ -53,7 +50,7 @@ class Group {
|
|
|
53
50
|
}
|
|
54
51
|
get nodes() {
|
|
55
52
|
return this.children.map((child) => {
|
|
56
|
-
if (child instanceof
|
|
53
|
+
if (child instanceof _Group) {
|
|
57
54
|
return child.nodes;
|
|
58
55
|
}
|
|
59
56
|
if (isNode(child)) {
|
|
@@ -68,16 +65,15 @@ class Group {
|
|
|
68
65
|
get dataSources() {
|
|
69
66
|
return this.nodes.filter((node) => isDataSource(node));
|
|
70
67
|
}
|
|
71
|
-
}
|
|
68
|
+
};
|
|
72
69
|
|
|
73
70
|
// src/stack.ts
|
|
74
|
-
|
|
75
|
-
app;
|
|
76
|
-
dependencies = new Set;
|
|
71
|
+
var Stack = class extends Group {
|
|
77
72
|
constructor(app, name) {
|
|
78
73
|
super(app, "stack", name);
|
|
79
74
|
this.app = app;
|
|
80
75
|
}
|
|
76
|
+
dependencies = /* @__PURE__ */ new Set();
|
|
81
77
|
dependsOn(...stacks) {
|
|
82
78
|
for (const stack of stacks) {
|
|
83
79
|
if (stack.app !== this.app) {
|
|
@@ -87,7 +83,7 @@ class Stack extends Group {
|
|
|
87
83
|
}
|
|
88
84
|
return this;
|
|
89
85
|
}
|
|
90
|
-
}
|
|
86
|
+
};
|
|
91
87
|
var findParentStack = (group) => {
|
|
92
88
|
if (group instanceof Stack) {
|
|
93
89
|
return group;
|
|
@@ -99,31 +95,29 @@ var findParentStack = (group) => {
|
|
|
99
95
|
};
|
|
100
96
|
|
|
101
97
|
// src/app.ts
|
|
102
|
-
|
|
103
|
-
name;
|
|
98
|
+
var App = class extends Group {
|
|
104
99
|
constructor(name) {
|
|
105
|
-
super(
|
|
100
|
+
super(void 0, "app", name);
|
|
106
101
|
this.name = name;
|
|
107
102
|
}
|
|
108
103
|
get stacks() {
|
|
109
104
|
return this.children.filter((child) => child instanceof Stack);
|
|
110
105
|
}
|
|
111
|
-
}
|
|
106
|
+
};
|
|
107
|
+
|
|
112
108
|
// src/future.ts
|
|
113
109
|
var IDLE = 0;
|
|
114
110
|
var PENDING = 1;
|
|
115
111
|
var RESOLVED = 2;
|
|
116
112
|
var REJECTED = 3;
|
|
117
|
-
|
|
118
|
-
class Future {
|
|
119
|
-
callback;
|
|
120
|
-
listeners = new Set;
|
|
121
|
-
status = IDLE;
|
|
122
|
-
data;
|
|
123
|
-
error;
|
|
113
|
+
var Future = class _Future {
|
|
124
114
|
constructor(callback) {
|
|
125
115
|
this.callback = callback;
|
|
126
116
|
}
|
|
117
|
+
listeners = /* @__PURE__ */ new Set();
|
|
118
|
+
status = IDLE;
|
|
119
|
+
data;
|
|
120
|
+
error;
|
|
127
121
|
get [Symbol.toStringTag]() {
|
|
128
122
|
switch (this.status) {
|
|
129
123
|
case IDLE:
|
|
@@ -137,42 +131,45 @@ class Future {
|
|
|
137
131
|
}
|
|
138
132
|
}
|
|
139
133
|
pipe(cb) {
|
|
140
|
-
return new
|
|
134
|
+
return new _Future((resolve2, reject) => {
|
|
141
135
|
this.then((value) => {
|
|
142
136
|
Promise.resolve(cb(value)).then((value2) => {
|
|
143
|
-
|
|
137
|
+
resolve2(value2);
|
|
144
138
|
}).catch(reject);
|
|
145
139
|
}, reject);
|
|
146
140
|
});
|
|
147
141
|
}
|
|
148
|
-
then(
|
|
142
|
+
then(resolve2, reject) {
|
|
149
143
|
if (this.status === RESOLVED) {
|
|
150
|
-
|
|
144
|
+
resolve2(this.data);
|
|
151
145
|
} else if (this.status === REJECTED) {
|
|
152
146
|
reject?.(this.error);
|
|
153
147
|
} else {
|
|
154
|
-
this.listeners.add({ resolve, reject });
|
|
148
|
+
this.listeners.add({ resolve: resolve2, reject });
|
|
155
149
|
if (this.status === IDLE) {
|
|
156
150
|
this.status = PENDING;
|
|
157
|
-
this.callback(
|
|
158
|
-
|
|
159
|
-
this.status
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this.
|
|
168
|
-
|
|
169
|
-
|
|
151
|
+
this.callback(
|
|
152
|
+
(data) => {
|
|
153
|
+
if (this.status === PENDING) {
|
|
154
|
+
this.status = RESOLVED;
|
|
155
|
+
this.data = data;
|
|
156
|
+
this.listeners.forEach(({ resolve: resolve3 }) => resolve3(data));
|
|
157
|
+
this.listeners.clear();
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
(error) => {
|
|
161
|
+
if (this.status === PENDING) {
|
|
162
|
+
this.status = REJECTED;
|
|
163
|
+
this.error = error;
|
|
164
|
+
this.listeners.forEach(({ reject: reject2 }) => reject2?.(error));
|
|
165
|
+
this.listeners.clear();
|
|
166
|
+
}
|
|
170
167
|
}
|
|
171
|
-
|
|
168
|
+
);
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
171
|
}
|
|
175
|
-
}
|
|
172
|
+
};
|
|
176
173
|
|
|
177
174
|
// src/input.ts
|
|
178
175
|
var findInputDeps = (props) => {
|
|
@@ -201,26 +198,32 @@ var resolveInputs = async (inputs) => {
|
|
|
201
198
|
}
|
|
202
199
|
};
|
|
203
200
|
find(inputs, {}, "root");
|
|
204
|
-
const responses = await Promise.all(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
201
|
+
const responses = await Promise.all(
|
|
202
|
+
unresolved.map(async ([obj, key]) => {
|
|
203
|
+
const promise = obj[key];
|
|
204
|
+
let timeout;
|
|
205
|
+
const response = await Promise.race([
|
|
206
|
+
promise,
|
|
207
|
+
new Promise((_, reject) => {
|
|
208
|
+
timeout = setTimeout(() => {
|
|
209
|
+
if (promise instanceof Output) {
|
|
210
|
+
reject(
|
|
211
|
+
new Error(
|
|
212
|
+
`Resolving Output<${[...promise.dependencies].map((d) => d.urn).join(", ")}> took too long.`
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
} else if (promise instanceof Future) {
|
|
216
|
+
reject(new Error("Resolving Future took too long."));
|
|
217
|
+
} else {
|
|
218
|
+
reject(new Error("Resolving Promise took too long."));
|
|
219
|
+
}
|
|
220
|
+
}, 3e3);
|
|
221
|
+
})
|
|
222
|
+
]);
|
|
223
|
+
clearTimeout(timeout);
|
|
224
|
+
return response;
|
|
225
|
+
})
|
|
226
|
+
);
|
|
224
227
|
unresolved.forEach(([props, key], i) => {
|
|
225
228
|
props[key] = responses[i];
|
|
226
229
|
});
|
|
@@ -228,33 +231,32 @@ var resolveInputs = async (inputs) => {
|
|
|
228
231
|
};
|
|
229
232
|
|
|
230
233
|
// src/output.ts
|
|
231
|
-
|
|
232
|
-
dependencies;
|
|
234
|
+
var Output = class _Output extends Future {
|
|
233
235
|
constructor(dependencies, callback) {
|
|
234
236
|
super(callback);
|
|
235
237
|
this.dependencies = dependencies;
|
|
236
238
|
}
|
|
237
239
|
pipe(cb) {
|
|
238
|
-
return new
|
|
240
|
+
return new _Output(this.dependencies, (resolve2, reject) => {
|
|
239
241
|
this.then((value) => {
|
|
240
242
|
Promise.resolve(cb(value)).then((value2) => {
|
|
241
|
-
|
|
243
|
+
resolve2(value2);
|
|
242
244
|
}).catch(reject);
|
|
243
245
|
}, reject);
|
|
244
246
|
});
|
|
245
247
|
}
|
|
246
|
-
}
|
|
248
|
+
};
|
|
247
249
|
var deferredOutput = (cb) => {
|
|
248
|
-
return new Output(new Set, cb);
|
|
250
|
+
return new Output(/* @__PURE__ */ new Set(), cb);
|
|
249
251
|
};
|
|
250
252
|
var output = (value) => {
|
|
251
|
-
return deferredOutput((
|
|
253
|
+
return deferredOutput((resolve2) => resolve2(value));
|
|
252
254
|
};
|
|
253
255
|
var combine = (...inputs) => {
|
|
254
256
|
const deps = new Set(findInputDeps(inputs));
|
|
255
|
-
return new Output(deps, (
|
|
257
|
+
return new Output(deps, (resolve2, reject) => {
|
|
256
258
|
Promise.all(inputs).then((result) => {
|
|
257
|
-
|
|
259
|
+
resolve2(result);
|
|
258
260
|
}, reject);
|
|
259
261
|
});
|
|
260
262
|
};
|
|
@@ -266,13 +268,14 @@ var resolve = (inputs, transformer) => {
|
|
|
266
268
|
var interpolate = (literals, ...placeholders) => {
|
|
267
269
|
return combine(...placeholders).pipe((unwrapped) => {
|
|
268
270
|
const result = [];
|
|
269
|
-
for (let i = 0;i < unwrapped.length; i++) {
|
|
271
|
+
for (let i = 0; i < unwrapped.length; i++) {
|
|
270
272
|
result.push(literals[i], unwrapped[i]);
|
|
271
273
|
}
|
|
272
274
|
result.push(literals.at(-1));
|
|
273
275
|
return result.join("");
|
|
274
276
|
});
|
|
275
277
|
};
|
|
278
|
+
|
|
276
279
|
// src/urn.ts
|
|
277
280
|
var createUrn = (tag, type, name, parentUrn) => {
|
|
278
281
|
return `${parentUrn ? parentUrn : "urn"}:${tag}:${type}:{${name}}`;
|
|
@@ -293,7 +296,7 @@ var createMeta = (tag, provider, parent, type, logicalId, input, config) => {
|
|
|
293
296
|
input,
|
|
294
297
|
config,
|
|
295
298
|
get dependencies() {
|
|
296
|
-
const dependencies = new Set;
|
|
299
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
297
300
|
const linkMetaDep = (dep) => {
|
|
298
301
|
if (dep.urn === urn) {
|
|
299
302
|
throw new Error("You can't depend on yourself");
|
|
@@ -308,11 +311,20 @@ var createMeta = (tag, provider, parent, type, logicalId, input, config) => {
|
|
|
308
311
|
}
|
|
309
312
|
return dependencies;
|
|
310
313
|
},
|
|
314
|
+
// attach(value) {
|
|
315
|
+
// resource = value
|
|
316
|
+
// },
|
|
317
|
+
// dependOn(...resources: Resource[]) {},
|
|
318
|
+
// attachDependencies(props) {
|
|
319
|
+
// for (const dep of findInputDeps(props)) {
|
|
320
|
+
// linkMetaDep(dep)
|
|
321
|
+
// }
|
|
322
|
+
// },
|
|
311
323
|
resolve(data) {
|
|
312
324
|
output2 = data;
|
|
313
325
|
},
|
|
314
326
|
output(cb) {
|
|
315
|
-
return new Output(new Set([this]), (resolve2) => {
|
|
327
|
+
return new Output(/* @__PURE__ */ new Set([this]), (resolve2) => {
|
|
316
328
|
if (!output2) {
|
|
317
329
|
throw new Error(`Unresolved output for ${tag}: ${urn}`);
|
|
318
330
|
}
|
|
@@ -321,6 +333,7 @@ var createMeta = (tag, provider, parent, type, logicalId, input, config) => {
|
|
|
321
333
|
}
|
|
322
334
|
};
|
|
323
335
|
};
|
|
336
|
+
|
|
324
337
|
// src/debug.ts
|
|
325
338
|
var enabled = false;
|
|
326
339
|
var enableDebug = () => {
|
|
@@ -336,9 +349,10 @@ var createDebugger = (group) => {
|
|
|
336
349
|
console.log();
|
|
337
350
|
};
|
|
338
351
|
};
|
|
352
|
+
|
|
339
353
|
// src/workspace/exit.ts
|
|
340
354
|
import asyncOnExit from "async-on-exit";
|
|
341
|
-
var listeners = new Set;
|
|
355
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
342
356
|
var listening = false;
|
|
343
357
|
var onExit = (cb) => {
|
|
344
358
|
listeners.add(cb);
|
|
@@ -399,9 +413,9 @@ var entries = (object) => {
|
|
|
399
413
|
};
|
|
400
414
|
|
|
401
415
|
// src/workspace/dependency.ts
|
|
402
|
-
|
|
403
|
-
graph = new DirectedGraph;
|
|
404
|
-
callbacks = new Map;
|
|
416
|
+
var DependencyGraph = class {
|
|
417
|
+
graph = new DirectedGraph();
|
|
418
|
+
callbacks = /* @__PURE__ */ new Map();
|
|
405
419
|
add(urn, deps, callback) {
|
|
406
420
|
this.callbacks.set(urn, callback);
|
|
407
421
|
this.graph.mergeNode(urn);
|
|
@@ -428,13 +442,15 @@ class DependencyGraph {
|
|
|
428
442
|
const graph = topologicalGenerations(this.graph);
|
|
429
443
|
const errors = [];
|
|
430
444
|
for (const list of graph) {
|
|
431
|
-
const result = await Promise.allSettled(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
445
|
+
const result = await Promise.allSettled(
|
|
446
|
+
list.map((urn) => {
|
|
447
|
+
const callback = this.callbacks.get(urn);
|
|
448
|
+
if (!callback) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
return callback();
|
|
452
|
+
})
|
|
453
|
+
);
|
|
438
454
|
for (const entry of result) {
|
|
439
455
|
if (entry.status === "rejected") {
|
|
440
456
|
if (entry.reason instanceof Error) {
|
|
@@ -450,7 +466,7 @@ class DependencyGraph {
|
|
|
450
466
|
}
|
|
451
467
|
return errors;
|
|
452
468
|
}
|
|
453
|
-
}
|
|
469
|
+
};
|
|
454
470
|
var dependentsOn = (resources, dependency) => {
|
|
455
471
|
const dependents = [];
|
|
456
472
|
for (const [urn, resource] of entries(resources)) {
|
|
@@ -462,39 +478,31 @@ var dependentsOn = (resources, dependency) => {
|
|
|
462
478
|
};
|
|
463
479
|
|
|
464
480
|
// src/workspace/error.ts
|
|
465
|
-
|
|
466
|
-
urn;
|
|
467
|
-
type;
|
|
468
|
-
operation;
|
|
469
|
-
static wrap(urn, type, operation, error) {
|
|
470
|
-
if (error instanceof Error) {
|
|
471
|
-
return new ResourceError(urn, type, operation, error.message);
|
|
472
|
-
}
|
|
473
|
-
return new ResourceError(urn, type, operation, "Unknown Error");
|
|
474
|
-
}
|
|
481
|
+
var ResourceError = class _ResourceError extends Error {
|
|
475
482
|
constructor(urn, type, operation, message) {
|
|
476
483
|
super(message);
|
|
477
484
|
this.urn = urn;
|
|
478
485
|
this.type = type;
|
|
479
486
|
this.operation = operation;
|
|
480
487
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
488
|
+
static wrap(urn, type, operation, error) {
|
|
489
|
+
if (error instanceof Error) {
|
|
490
|
+
return new _ResourceError(urn, type, operation, error.message);
|
|
491
|
+
}
|
|
492
|
+
return new _ResourceError(urn, type, operation, "Unknown Error");
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
var AppError = class extends Error {
|
|
486
496
|
constructor(app, issues, message) {
|
|
487
497
|
super(message);
|
|
488
498
|
this.app = app;
|
|
489
499
|
this.issues = issues;
|
|
490
500
|
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
class ResourceAlreadyExists extends Error {
|
|
497
|
-
}
|
|
501
|
+
};
|
|
502
|
+
var ResourceNotFound = class extends Error {
|
|
503
|
+
};
|
|
504
|
+
var ResourceAlreadyExists = class extends Error {
|
|
505
|
+
};
|
|
498
506
|
|
|
499
507
|
// src/workspace/state.ts
|
|
500
508
|
var compareState = (left, right) => {
|
|
@@ -602,11 +610,23 @@ var deleteResource = async (appToken, urn, state, opt) => {
|
|
|
602
610
|
const idempotantToken = createIdempotantToken(appToken, urn, "delete");
|
|
603
611
|
const provider = findProvider(opt.providers, state.provider);
|
|
604
612
|
try {
|
|
613
|
+
await opt.hooks?.beforeResourceDelete?.({
|
|
614
|
+
urn,
|
|
615
|
+
type: state.type,
|
|
616
|
+
oldInput: state.input,
|
|
617
|
+
oldOutput: state.output
|
|
618
|
+
});
|
|
605
619
|
await provider.deleteResource({
|
|
606
620
|
type: state.type,
|
|
607
621
|
state: state.output,
|
|
608
622
|
idempotantToken
|
|
609
623
|
});
|
|
624
|
+
await opt.hooks?.afterResourceDelete?.({
|
|
625
|
+
urn,
|
|
626
|
+
type: state.type,
|
|
627
|
+
oldInput: state.input,
|
|
628
|
+
oldOutput: state.output
|
|
629
|
+
});
|
|
610
630
|
} catch (error) {
|
|
611
631
|
if (error instanceof ResourceNotFound) {
|
|
612
632
|
debug(state.type, "already deleted");
|
|
@@ -632,7 +652,7 @@ var deleteApp = async (app, opt) => {
|
|
|
632
652
|
stackStates = stackStates.filter((stackState) => opt.filters.includes(stackState.name));
|
|
633
653
|
}
|
|
634
654
|
const queue = createConcurrencyQueue(opt.concurrency ?? 10);
|
|
635
|
-
const graph = new DependencyGraph;
|
|
655
|
+
const graph = new DependencyGraph();
|
|
636
656
|
const allNodes = {};
|
|
637
657
|
for (const stackState of Object.values(appState.stacks)) {
|
|
638
658
|
for (const [urn, nodeState] of entries(stackState.nodes)) {
|
|
@@ -668,7 +688,7 @@ var requiresReplacement = (priorState, proposedState, replaceOnChanges) => {
|
|
|
668
688
|
const priorValue = get(priorState, path);
|
|
669
689
|
const proposedValue = get(proposedState, path);
|
|
670
690
|
if (path.includes("*") && Array.isArray(priorValue)) {
|
|
671
|
-
for (let i = 0;i < priorValue.length; i++) {
|
|
691
|
+
for (let i = 0; i < priorValue.length; i++) {
|
|
672
692
|
if (!compareState(priorValue[i], proposedValue[i])) {
|
|
673
693
|
return true;
|
|
674
694
|
}
|
|
@@ -691,11 +711,24 @@ var createResource = async (resource, appToken, input, opt) => {
|
|
|
691
711
|
debug2(input);
|
|
692
712
|
let result;
|
|
693
713
|
try {
|
|
714
|
+
await opt.hooks?.beforeResourceCreate?.({
|
|
715
|
+
urn: resource.urn,
|
|
716
|
+
type: meta.type,
|
|
717
|
+
resource,
|
|
718
|
+
newInput: input
|
|
719
|
+
});
|
|
694
720
|
result = await provider.createResource({
|
|
695
721
|
type: meta.type,
|
|
696
722
|
state: input,
|
|
697
723
|
idempotantToken
|
|
698
724
|
});
|
|
725
|
+
await opt.hooks?.afterResourceCreate?.({
|
|
726
|
+
urn: resource.urn,
|
|
727
|
+
type: meta.type,
|
|
728
|
+
resource,
|
|
729
|
+
newInput: input,
|
|
730
|
+
newOutput: result.state
|
|
731
|
+
});
|
|
699
732
|
} catch (error) {
|
|
700
733
|
throw ResourceError.wrap(meta.urn, meta.type, "create", error);
|
|
701
734
|
}
|
|
@@ -766,7 +799,7 @@ var importResource = async (resource, input, opt) => {
|
|
|
766
799
|
|
|
767
800
|
// src/workspace/procedure/replace-resource.ts
|
|
768
801
|
var debug5 = createDebugger("Replace");
|
|
769
|
-
var replaceResource = async (resource, appToken,
|
|
802
|
+
var replaceResource = async (resource, appToken, priorInputState, priorOutputState, proposedState, opt) => {
|
|
770
803
|
const meta = getMeta(resource);
|
|
771
804
|
const urn = meta.urn;
|
|
772
805
|
const type = meta.type;
|
|
@@ -778,11 +811,23 @@ var replaceResource = async (resource, appToken, priorState, proposedState, opt)
|
|
|
778
811
|
debug5("retain", type);
|
|
779
812
|
} else {
|
|
780
813
|
try {
|
|
814
|
+
await opt.hooks?.beforeResourceDelete?.({
|
|
815
|
+
urn,
|
|
816
|
+
type,
|
|
817
|
+
oldInput: priorInputState,
|
|
818
|
+
oldOutput: priorOutputState
|
|
819
|
+
});
|
|
781
820
|
await provider.deleteResource({
|
|
782
821
|
type,
|
|
783
|
-
state:
|
|
822
|
+
state: priorOutputState,
|
|
784
823
|
idempotantToken
|
|
785
824
|
});
|
|
825
|
+
await opt.hooks?.afterResourceDelete?.({
|
|
826
|
+
urn,
|
|
827
|
+
type,
|
|
828
|
+
oldInput: priorInputState,
|
|
829
|
+
oldOutput: priorOutputState
|
|
830
|
+
});
|
|
786
831
|
} catch (error) {
|
|
787
832
|
if (error instanceof ResourceNotFound) {
|
|
788
833
|
debug5(type, "already deleted");
|
|
@@ -793,11 +838,24 @@ var replaceResource = async (resource, appToken, priorState, proposedState, opt)
|
|
|
793
838
|
}
|
|
794
839
|
let result;
|
|
795
840
|
try {
|
|
841
|
+
await opt.hooks?.beforeResourceCreate?.({
|
|
842
|
+
urn,
|
|
843
|
+
type,
|
|
844
|
+
resource,
|
|
845
|
+
newInput: proposedState
|
|
846
|
+
});
|
|
796
847
|
result = await provider.createResource({
|
|
797
848
|
type,
|
|
798
849
|
state: proposedState,
|
|
799
850
|
idempotantToken
|
|
800
851
|
});
|
|
852
|
+
await opt.hooks?.afterResourceCreate?.({
|
|
853
|
+
urn,
|
|
854
|
+
type,
|
|
855
|
+
resource,
|
|
856
|
+
newInput: proposedState,
|
|
857
|
+
newOutput: result.state
|
|
858
|
+
});
|
|
801
859
|
} catch (error) {
|
|
802
860
|
throw ResourceError.wrap(urn, type, "replace", error);
|
|
803
861
|
}
|
|
@@ -809,7 +867,7 @@ var replaceResource = async (resource, appToken, priorState, proposedState, opt)
|
|
|
809
867
|
|
|
810
868
|
// src/workspace/procedure/update-resource.ts
|
|
811
869
|
var debug6 = createDebugger("Update");
|
|
812
|
-
var updateResource = async (resource, appToken,
|
|
870
|
+
var updateResource = async (resource, appToken, priorInputState, priorOutputState, proposedState, opt) => {
|
|
813
871
|
const meta = getMeta(resource);
|
|
814
872
|
const provider = findProvider(opt.providers, meta.provider);
|
|
815
873
|
const idempotantToken = createIdempotantToken(appToken, meta.urn, "update");
|
|
@@ -817,12 +875,29 @@ var updateResource = async (resource, appToken, priorState, proposedState, opt)
|
|
|
817
875
|
debug6(meta.type);
|
|
818
876
|
debug6(proposedState);
|
|
819
877
|
try {
|
|
878
|
+
await opt.hooks?.beforeResourceUpdate?.({
|
|
879
|
+
urn: resource.urn,
|
|
880
|
+
type: meta.type,
|
|
881
|
+
resource,
|
|
882
|
+
newInput: proposedState,
|
|
883
|
+
oldInput: priorInputState,
|
|
884
|
+
oldOutput: priorOutputState
|
|
885
|
+
});
|
|
820
886
|
result = await provider.updateResource({
|
|
821
887
|
type: meta.type,
|
|
822
|
-
priorState,
|
|
888
|
+
priorState: priorOutputState,
|
|
823
889
|
proposedState,
|
|
824
890
|
idempotantToken
|
|
825
891
|
});
|
|
892
|
+
await opt.hooks?.afterResourceUpdate?.({
|
|
893
|
+
urn: resource.urn,
|
|
894
|
+
type: meta.type,
|
|
895
|
+
resource,
|
|
896
|
+
newInput: proposedState,
|
|
897
|
+
oldInput: priorInputState,
|
|
898
|
+
newOutput: result.state,
|
|
899
|
+
oldOutput: priorOutputState
|
|
900
|
+
});
|
|
826
901
|
} catch (error) {
|
|
827
902
|
throw ResourceError.wrap(meta.urn, meta.type, "update", error);
|
|
828
903
|
}
|
|
@@ -834,13 +909,98 @@ var updateResource = async (resource, appToken, priorState, proposedState, opt)
|
|
|
834
909
|
|
|
835
910
|
// src/workspace/procedure/deploy-app.ts
|
|
836
911
|
var debug7 = createDebugger("Deploy App");
|
|
912
|
+
var findDependencyPaths = (value, dependencyUrn, path = []) => {
|
|
913
|
+
const paths = [];
|
|
914
|
+
const visit = (current, currentPath) => {
|
|
915
|
+
if (current instanceof Output) {
|
|
916
|
+
for (const dep of current.dependencies) {
|
|
917
|
+
if (dep.urn === dependencyUrn) {
|
|
918
|
+
paths.push(currentPath);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (Array.isArray(current)) {
|
|
925
|
+
current.forEach((item, index) => {
|
|
926
|
+
visit(item, [...currentPath, index]);
|
|
927
|
+
});
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (current && typeof current === "object") {
|
|
931
|
+
for (const [key, item] of Object.entries(current)) {
|
|
932
|
+
visit(item, [...currentPath, key]);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
visit(value, path);
|
|
937
|
+
return paths;
|
|
938
|
+
};
|
|
939
|
+
var cloneState = (value) => JSON.parse(JSON.stringify(value));
|
|
940
|
+
var removeAtPath = (target, path) => {
|
|
941
|
+
if (path.length === 0) return;
|
|
942
|
+
let parent = target;
|
|
943
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
944
|
+
if (parent == null) return;
|
|
945
|
+
parent = parent[path[i]];
|
|
946
|
+
}
|
|
947
|
+
const last = path[path.length - 1];
|
|
948
|
+
if (Array.isArray(parent) && typeof last === "number") {
|
|
949
|
+
if (last >= 0 && last < parent.length) {
|
|
950
|
+
parent.splice(last, 1);
|
|
951
|
+
}
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (parent && typeof parent === "object") {
|
|
955
|
+
delete parent[last];
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
var stripDependencyInputs = (input, metaInput, dependencyUrn) => {
|
|
959
|
+
const paths = findDependencyPaths(metaInput, dependencyUrn);
|
|
960
|
+
if (paths.length === 0) {
|
|
961
|
+
return input;
|
|
962
|
+
}
|
|
963
|
+
const detached = cloneState(input);
|
|
964
|
+
const sortedPaths = [...paths].sort((a, b) => {
|
|
965
|
+
if (a.length !== b.length) return b.length - a.length;
|
|
966
|
+
const aLast = a[a.length - 1];
|
|
967
|
+
const bLast = b[b.length - 1];
|
|
968
|
+
if (typeof aLast === "number" && typeof bLast === "number") {
|
|
969
|
+
return bLast - aLast;
|
|
970
|
+
}
|
|
971
|
+
return 0;
|
|
972
|
+
});
|
|
973
|
+
for (const path of sortedPaths) {
|
|
974
|
+
removeAtPath(detached, path);
|
|
975
|
+
}
|
|
976
|
+
return detached;
|
|
977
|
+
};
|
|
978
|
+
var allowsDependentReplace = (replaceOnChanges, dependencyPaths) => {
|
|
979
|
+
if (!replaceOnChanges || replaceOnChanges.length === 0) {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
for (const path of dependencyPaths) {
|
|
983
|
+
const base = typeof path[0] === "string" ? path[0] : void 0;
|
|
984
|
+
if (!base) {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
for (const replacePath of replaceOnChanges) {
|
|
988
|
+
if (replacePath === base || replacePath.startsWith(`${base}.`) || replacePath.startsWith(`${base}[`) || replacePath.startsWith(`${base}.*`)) {
|
|
989
|
+
return true;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return false;
|
|
994
|
+
};
|
|
837
995
|
var deployApp = async (app, opt) => {
|
|
838
996
|
debug7(app.name, "start");
|
|
839
997
|
const latestState = await opt.backend.state.get(app.urn);
|
|
840
|
-
const appState = migrateAppState(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
998
|
+
const appState = migrateAppState(
|
|
999
|
+
latestState ?? {
|
|
1000
|
+
name: app.name,
|
|
1001
|
+
stacks: {}
|
|
1002
|
+
}
|
|
1003
|
+
);
|
|
844
1004
|
const releaseOnExit = onExit(async () => {
|
|
845
1005
|
await opt.backend.state.update(app.urn, appState);
|
|
846
1006
|
});
|
|
@@ -854,8 +1014,22 @@ var deployApp = async (app, opt) => {
|
|
|
854
1014
|
stacks = app.stacks.filter((stack) => opt.filters.includes(stack.name));
|
|
855
1015
|
filteredOutStacks = app.stacks.filter((stack) => !opt.filters.includes(stack.name));
|
|
856
1016
|
}
|
|
1017
|
+
const nodeByUrn = /* @__PURE__ */ new Map();
|
|
1018
|
+
const stackStates = /* @__PURE__ */ new Map();
|
|
1019
|
+
const plannedDependents = /* @__PURE__ */ new Set();
|
|
1020
|
+
const forcedUpdateDependents = /* @__PURE__ */ new Set();
|
|
1021
|
+
for (const stack of stacks) {
|
|
1022
|
+
const stackState = appState.stacks[stack.urn] = appState.stacks[stack.urn] ?? {
|
|
1023
|
+
name: stack.name,
|
|
1024
|
+
nodes: {}
|
|
1025
|
+
};
|
|
1026
|
+
stackStates.set(stack.urn, stackState);
|
|
1027
|
+
for (const node of stack.nodes) {
|
|
1028
|
+
nodeByUrn.set(getMeta(node).urn, node);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
857
1031
|
const queue = createConcurrencyQueue(opt.concurrency ?? 10);
|
|
858
|
-
const graph = new DependencyGraph;
|
|
1032
|
+
const graph = new DependencyGraph();
|
|
859
1033
|
const allNodes = {};
|
|
860
1034
|
for (const stackState of Object.values(appState.stacks)) {
|
|
861
1035
|
for (const [urn, nodeState] of entries(stackState.nodes)) {
|
|
@@ -886,7 +1060,15 @@ var deployApp = async (app, opt) => {
|
|
|
886
1060
|
for (const [urn2, nodeState] of entries(stackState.nodes)) {
|
|
887
1061
|
graph.add(urn2, dependentsOn(allNodes, urn2), async () => {
|
|
888
1062
|
if (nodeState.tag === "resource") {
|
|
889
|
-
await queue(
|
|
1063
|
+
await queue(
|
|
1064
|
+
() => deleteResource(
|
|
1065
|
+
//
|
|
1066
|
+
appState.idempotentToken,
|
|
1067
|
+
urn2,
|
|
1068
|
+
nodeState,
|
|
1069
|
+
opt
|
|
1070
|
+
)
|
|
1071
|
+
);
|
|
890
1072
|
}
|
|
891
1073
|
delete stackState.nodes[urn2];
|
|
892
1074
|
});
|
|
@@ -894,16 +1076,21 @@ var deployApp = async (app, opt) => {
|
|
|
894
1076
|
}
|
|
895
1077
|
}
|
|
896
1078
|
for (const stack of stacks) {
|
|
897
|
-
const stackState =
|
|
898
|
-
name: stack.name,
|
|
899
|
-
nodes: {}
|
|
900
|
-
};
|
|
1079
|
+
const stackState = stackStates.get(stack.urn);
|
|
901
1080
|
for (const [urn, nodeState] of entries(stackState.nodes)) {
|
|
902
1081
|
const resource = stack.nodes.find((r) => getMeta(r).urn === urn);
|
|
903
1082
|
if (!resource) {
|
|
904
1083
|
graph.add(urn, dependentsOn(allNodes, urn), async () => {
|
|
905
1084
|
if (nodeState.tag === "resource") {
|
|
906
|
-
await queue(
|
|
1085
|
+
await queue(
|
|
1086
|
+
() => deleteResource(
|
|
1087
|
+
//
|
|
1088
|
+
appState.idempotentToken,
|
|
1089
|
+
urn,
|
|
1090
|
+
nodeState,
|
|
1091
|
+
opt
|
|
1092
|
+
)
|
|
1093
|
+
);
|
|
907
1094
|
}
|
|
908
1095
|
delete stackState.nodes[urn];
|
|
909
1096
|
});
|
|
@@ -915,8 +1102,9 @@ var deployApp = async (app, opt) => {
|
|
|
915
1102
|
const partialNewResourceState = {
|
|
916
1103
|
dependencies,
|
|
917
1104
|
lifecycle: isResource(node) ? {
|
|
1105
|
+
// deleteAfterCreate: meta.config?.deleteAfterCreate,
|
|
918
1106
|
retainOnDelete: getMeta(node).config?.retainOnDelete
|
|
919
|
-
} :
|
|
1107
|
+
} : void 0
|
|
920
1108
|
};
|
|
921
1109
|
graph.add(meta.urn, dependencies, () => {
|
|
922
1110
|
return queue(async () => {
|
|
@@ -925,7 +1113,13 @@ var deployApp = async (app, opt) => {
|
|
|
925
1113
|
try {
|
|
926
1114
|
input = await resolveInputs(meta.input);
|
|
927
1115
|
} catch (error) {
|
|
928
|
-
throw ResourceError.wrap(
|
|
1116
|
+
throw ResourceError.wrap(
|
|
1117
|
+
//
|
|
1118
|
+
meta.urn,
|
|
1119
|
+
meta.type,
|
|
1120
|
+
"resolve",
|
|
1121
|
+
error
|
|
1122
|
+
);
|
|
929
1123
|
}
|
|
930
1124
|
if (isDataSource(node)) {
|
|
931
1125
|
const meta2 = getMeta(node);
|
|
@@ -950,25 +1144,144 @@ var deployApp = async (app, opt) => {
|
|
|
950
1144
|
if (!nodeState) {
|
|
951
1145
|
if (meta2.config?.import) {
|
|
952
1146
|
const importedState = await importResource(node, input, opt);
|
|
953
|
-
const newResourceState = await updateResource(
|
|
1147
|
+
const newResourceState = await updateResource(
|
|
1148
|
+
node,
|
|
1149
|
+
appState.idempotentToken,
|
|
1150
|
+
importedState.input,
|
|
1151
|
+
importedState.output,
|
|
1152
|
+
input,
|
|
1153
|
+
opt
|
|
1154
|
+
);
|
|
954
1155
|
nodeState = stackState.nodes[meta2.urn] = {
|
|
955
1156
|
...importedState,
|
|
956
1157
|
...newResourceState,
|
|
957
1158
|
...partialNewResourceState
|
|
958
1159
|
};
|
|
959
1160
|
} else {
|
|
960
|
-
const newResourceState = await createResource(
|
|
1161
|
+
const newResourceState = await createResource(
|
|
1162
|
+
node,
|
|
1163
|
+
appState.idempotentToken,
|
|
1164
|
+
input,
|
|
1165
|
+
opt
|
|
1166
|
+
);
|
|
961
1167
|
nodeState = stackState.nodes[meta2.urn] = {
|
|
962
1168
|
...newResourceState,
|
|
963
1169
|
...partialNewResourceState
|
|
964
1170
|
};
|
|
965
1171
|
}
|
|
966
|
-
} else if (
|
|
1172
|
+
} else if (
|
|
1173
|
+
// --------------------------------------------------
|
|
1174
|
+
// Check if any state has changed
|
|
1175
|
+
!compareState(nodeState.input, input)
|
|
1176
|
+
) {
|
|
967
1177
|
let newResourceState;
|
|
968
|
-
|
|
969
|
-
|
|
1178
|
+
const ignoreReplace = forcedUpdateDependents.has(meta2.urn);
|
|
1179
|
+
if (!ignoreReplace && requiresReplacement(nodeState.input, input, meta2.config?.replaceOnChanges ?? [])) {
|
|
1180
|
+
for (const [dependentUrn, dependentNode] of nodeByUrn.entries()) {
|
|
1181
|
+
if (!isResource(dependentNode)) {
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
const dependentMeta = getMeta(dependentNode);
|
|
1185
|
+
if (!dependentMeta.dependencies.has(meta2.urn)) {
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
if (plannedDependents.has(dependentUrn)) {
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
const dependentStackState = stackStates.get(dependentMeta.stack.urn);
|
|
1192
|
+
const dependentState = dependentStackState?.nodes[dependentUrn];
|
|
1193
|
+
if (!dependentStackState || !dependentState) {
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
const dependencyPaths = findDependencyPaths(dependentMeta.input, meta2.urn);
|
|
1197
|
+
if (dependencyPaths.length === 0) {
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
const detachedInput = stripDependencyInputs(
|
|
1201
|
+
dependentState.input,
|
|
1202
|
+
dependentMeta.input,
|
|
1203
|
+
meta2.urn
|
|
1204
|
+
);
|
|
1205
|
+
if (compareState(dependentState.input, detachedInput)) {
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
plannedDependents.add(dependentUrn);
|
|
1209
|
+
let dependentRequiresReplacement = false;
|
|
1210
|
+
const dependentProvider = findProvider(opt.providers, dependentMeta.provider);
|
|
1211
|
+
if (dependentProvider.planResourceChange) {
|
|
1212
|
+
try {
|
|
1213
|
+
const dependentPlan = await dependentProvider.planResourceChange({
|
|
1214
|
+
type: dependentMeta.type,
|
|
1215
|
+
priorState: dependentState.output,
|
|
1216
|
+
proposedState: detachedInput
|
|
1217
|
+
});
|
|
1218
|
+
dependentRequiresReplacement = dependentPlan.requiresReplacement;
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
throw ResourceError.wrap(
|
|
1221
|
+
dependentMeta.urn,
|
|
1222
|
+
dependentMeta.type,
|
|
1223
|
+
"update",
|
|
1224
|
+
error
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
if (dependentRequiresReplacement) {
|
|
1229
|
+
if (!allowsDependentReplace(
|
|
1230
|
+
dependentMeta.config?.replaceOnChanges,
|
|
1231
|
+
dependencyPaths
|
|
1232
|
+
)) {
|
|
1233
|
+
throw ResourceError.wrap(
|
|
1234
|
+
dependentMeta.urn,
|
|
1235
|
+
dependentMeta.type,
|
|
1236
|
+
"update",
|
|
1237
|
+
new Error(
|
|
1238
|
+
`Replacing ${meta2.urn} requires ${dependentMeta.urn} to set replaceOnChanges for its dependency fields.`
|
|
1239
|
+
)
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1242
|
+
await deleteResource(
|
|
1243
|
+
appState.idempotentToken,
|
|
1244
|
+
dependentUrn,
|
|
1245
|
+
dependentState,
|
|
1246
|
+
opt
|
|
1247
|
+
);
|
|
1248
|
+
delete dependentStackState.nodes[dependentUrn];
|
|
1249
|
+
} else {
|
|
1250
|
+
const updated = await updateResource(
|
|
1251
|
+
dependentNode,
|
|
1252
|
+
appState.idempotentToken,
|
|
1253
|
+
dependentState.input,
|
|
1254
|
+
dependentState.output,
|
|
1255
|
+
detachedInput,
|
|
1256
|
+
opt
|
|
1257
|
+
);
|
|
1258
|
+
Object.assign(dependentState, {
|
|
1259
|
+
input: detachedInput,
|
|
1260
|
+
...updated
|
|
1261
|
+
});
|
|
1262
|
+
forcedUpdateDependents.add(dependentUrn);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
newResourceState = await replaceResource(
|
|
1266
|
+
node,
|
|
1267
|
+
appState.idempotentToken,
|
|
1268
|
+
nodeState.input,
|
|
1269
|
+
nodeState.output,
|
|
1270
|
+
input,
|
|
1271
|
+
opt
|
|
1272
|
+
);
|
|
970
1273
|
} else {
|
|
971
|
-
newResourceState = await updateResource(
|
|
1274
|
+
newResourceState = await updateResource(
|
|
1275
|
+
node,
|
|
1276
|
+
appState.idempotentToken,
|
|
1277
|
+
nodeState.input,
|
|
1278
|
+
nodeState.output,
|
|
1279
|
+
input,
|
|
1280
|
+
opt
|
|
1281
|
+
);
|
|
1282
|
+
if (ignoreReplace) {
|
|
1283
|
+
forcedUpdateDependents.delete(meta2.urn);
|
|
1284
|
+
}
|
|
972
1285
|
}
|
|
973
1286
|
Object.assign(nodeState, {
|
|
974
1287
|
input,
|
|
@@ -1025,42 +1338,48 @@ var refresh = async (app, opt) => {
|
|
|
1025
1338
|
const appState = await opt.backend.state.get(app.urn);
|
|
1026
1339
|
const queue = createConcurrencyQueue(opt.concurrency ?? 10);
|
|
1027
1340
|
if (appState) {
|
|
1028
|
-
await Promise.all(
|
|
1029
|
-
|
|
1030
|
-
return
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1341
|
+
await Promise.all(
|
|
1342
|
+
Object.values(appState.stacks).map((stackState) => {
|
|
1343
|
+
return Promise.all(
|
|
1344
|
+
Object.values(stackState.nodes).map((nodeState) => {
|
|
1345
|
+
return queue(async () => {
|
|
1346
|
+
const provider = findProvider(opt.providers, nodeState.provider);
|
|
1347
|
+
if (nodeState.tag === "data") {
|
|
1348
|
+
const result = await provider.getData?.({
|
|
1349
|
+
type: nodeState.type,
|
|
1350
|
+
state: nodeState.output
|
|
1351
|
+
});
|
|
1352
|
+
if (result && !compareState(result.state, nodeState.output)) {
|
|
1353
|
+
nodeState.output = result.state;
|
|
1354
|
+
nodeState.input = result.state;
|
|
1355
|
+
}
|
|
1356
|
+
} else if (nodeState.tag === "resource") {
|
|
1357
|
+
const result = await provider.getResource({
|
|
1358
|
+
type: nodeState.type,
|
|
1359
|
+
state: nodeState.output
|
|
1360
|
+
});
|
|
1361
|
+
if (result && !compareState(result.state, nodeState.output)) {
|
|
1362
|
+
nodeState.output = result.state;
|
|
1363
|
+
nodeState.input = result.state;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1045
1366
|
});
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
}));
|
|
1053
|
-
}));
|
|
1367
|
+
})
|
|
1368
|
+
);
|
|
1369
|
+
})
|
|
1370
|
+
);
|
|
1054
1371
|
await opt.backend.state.update(app.urn, appState);
|
|
1055
1372
|
}
|
|
1056
1373
|
};
|
|
1057
1374
|
|
|
1058
1375
|
// src/workspace/workspace.ts
|
|
1059
|
-
|
|
1060
|
-
props;
|
|
1376
|
+
var WorkSpace = class {
|
|
1061
1377
|
constructor(props) {
|
|
1062
1378
|
this.props = props;
|
|
1063
1379
|
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Deploy the entire app or use the filter option to deploy specific stacks inside your app.
|
|
1382
|
+
*/
|
|
1064
1383
|
deploy(app, options = {}) {
|
|
1065
1384
|
return lockApp(this.props.backend.lock, app, async () => {
|
|
1066
1385
|
try {
|
|
@@ -1070,6 +1389,9 @@ class WorkSpace {
|
|
|
1070
1389
|
}
|
|
1071
1390
|
});
|
|
1072
1391
|
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Delete the entire app or use the filter option to delete specific stacks inside your app.
|
|
1394
|
+
*/
|
|
1073
1395
|
delete(app, options = {}) {
|
|
1074
1396
|
return lockApp(this.props.backend.lock, app, async () => {
|
|
1075
1397
|
try {
|
|
@@ -1079,9 +1401,15 @@ class WorkSpace {
|
|
|
1079
1401
|
}
|
|
1080
1402
|
});
|
|
1081
1403
|
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Hydrate the outputs of the resources & data-sources inside your app.
|
|
1406
|
+
*/
|
|
1082
1407
|
hydrate(app) {
|
|
1083
1408
|
return hydrate(app, this.props);
|
|
1084
1409
|
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Refresh the state of the resources & data-sources inside your app.
|
|
1412
|
+
*/
|
|
1085
1413
|
refresh(app) {
|
|
1086
1414
|
return lockApp(this.props.backend.lock, app, async () => {
|
|
1087
1415
|
try {
|
|
@@ -1092,14 +1420,17 @@ class WorkSpace {
|
|
|
1092
1420
|
});
|
|
1093
1421
|
}
|
|
1094
1422
|
async destroyProviders() {
|
|
1095
|
-
await Promise.all(
|
|
1096
|
-
|
|
1097
|
-
|
|
1423
|
+
await Promise.all(
|
|
1424
|
+
this.props.providers.map((p) => {
|
|
1425
|
+
return p.destroy?.();
|
|
1426
|
+
})
|
|
1427
|
+
);
|
|
1098
1428
|
}
|
|
1099
|
-
}
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1100
1431
|
// src/backend/memory/state.ts
|
|
1101
|
-
|
|
1102
|
-
states = new Map;
|
|
1432
|
+
var MemoryStateBackend = class {
|
|
1433
|
+
states = /* @__PURE__ */ new Map();
|
|
1103
1434
|
async get(urn) {
|
|
1104
1435
|
return this.states.get(urn);
|
|
1105
1436
|
}
|
|
@@ -1112,10 +1443,11 @@ class MemoryStateBackend {
|
|
|
1112
1443
|
clear() {
|
|
1113
1444
|
this.states.clear();
|
|
1114
1445
|
}
|
|
1115
|
-
}
|
|
1446
|
+
};
|
|
1447
|
+
|
|
1116
1448
|
// src/backend/memory/lock.ts
|
|
1117
|
-
|
|
1118
|
-
locks = new Map;
|
|
1449
|
+
var MemoryLockBackend = class {
|
|
1450
|
+
locks = /* @__PURE__ */ new Map();
|
|
1119
1451
|
async insecureReleaseLock(urn) {
|
|
1120
1452
|
this.locks.delete(urn);
|
|
1121
1453
|
}
|
|
@@ -1137,14 +1469,13 @@ class MemoryLockBackend {
|
|
|
1137
1469
|
clear() {
|
|
1138
1470
|
this.locks.clear();
|
|
1139
1471
|
}
|
|
1140
|
-
}
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1141
1474
|
// src/backend/file/state.ts
|
|
1142
|
-
import { mkdir, readFile, rm, writeFile } from "
|
|
1143
|
-
import { join } from "
|
|
1475
|
+
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
1476
|
+
import { join } from "path";
|
|
1144
1477
|
var debug8 = createDebugger("State");
|
|
1145
|
-
|
|
1146
|
-
class FileStateBackend {
|
|
1147
|
-
props;
|
|
1478
|
+
var FileStateBackend = class {
|
|
1148
1479
|
constructor(props) {
|
|
1149
1480
|
this.props = props;
|
|
1150
1481
|
}
|
|
@@ -1169,21 +1500,20 @@ class FileStateBackend {
|
|
|
1169
1500
|
async update(urn, state) {
|
|
1170
1501
|
debug8("update");
|
|
1171
1502
|
await this.mkdir();
|
|
1172
|
-
await writeFile(this.stateFile(urn), JSON.stringify(state,
|
|
1503
|
+
await writeFile(this.stateFile(urn), JSON.stringify(state, void 0, 2));
|
|
1173
1504
|
}
|
|
1174
1505
|
async delete(urn) {
|
|
1175
1506
|
debug8("delete");
|
|
1176
1507
|
await this.mkdir();
|
|
1177
1508
|
await rm(this.stateFile(urn));
|
|
1178
1509
|
}
|
|
1179
|
-
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1180
1512
|
// src/backend/file/lock.ts
|
|
1181
|
-
import { mkdir as mkdir2, rm as rm2, stat } from "
|
|
1182
|
-
import { join as join2 } from "
|
|
1513
|
+
import { mkdir as mkdir2, rm as rm2, stat } from "fs/promises";
|
|
1514
|
+
import { join as join2 } from "path";
|
|
1183
1515
|
import { lock } from "proper-lockfile";
|
|
1184
|
-
|
|
1185
|
-
class FileLockBackend {
|
|
1186
|
-
props;
|
|
1516
|
+
var FileLockBackend = class {
|
|
1187
1517
|
constructor(props) {
|
|
1188
1518
|
this.props = props;
|
|
1189
1519
|
}
|
|
@@ -1210,7 +1540,8 @@ class FileLockBackend {
|
|
|
1210
1540
|
realpath: false
|
|
1211
1541
|
});
|
|
1212
1542
|
}
|
|
1213
|
-
}
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1214
1545
|
// src/backend/aws/s3-state.ts
|
|
1215
1546
|
import {
|
|
1216
1547
|
DeleteObjectCommand,
|
|
@@ -1219,21 +1550,21 @@ import {
|
|
|
1219
1550
|
S3Client,
|
|
1220
1551
|
S3ServiceException
|
|
1221
1552
|
} from "@aws-sdk/client-s3";
|
|
1222
|
-
|
|
1223
|
-
class S3StateBackend {
|
|
1224
|
-
props;
|
|
1225
|
-
client;
|
|
1553
|
+
var S3StateBackend = class {
|
|
1226
1554
|
constructor(props) {
|
|
1227
1555
|
this.props = props;
|
|
1228
1556
|
this.client = new S3Client(props);
|
|
1229
1557
|
}
|
|
1558
|
+
client;
|
|
1230
1559
|
async get(urn) {
|
|
1231
1560
|
let result;
|
|
1232
1561
|
try {
|
|
1233
|
-
result = await this.client.send(
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1562
|
+
result = await this.client.send(
|
|
1563
|
+
new GetObjectCommand({
|
|
1564
|
+
Bucket: this.props.bucket,
|
|
1565
|
+
Key: `${urn}.state`
|
|
1566
|
+
})
|
|
1567
|
+
);
|
|
1237
1568
|
} catch (error) {
|
|
1238
1569
|
if (error instanceof S3ServiceException && error.name === "NoSuchKey") {
|
|
1239
1570
|
return;
|
|
@@ -1248,30 +1579,33 @@ class S3StateBackend {
|
|
|
1248
1579
|
return state;
|
|
1249
1580
|
}
|
|
1250
1581
|
async update(urn, state) {
|
|
1251
|
-
await this.client.send(
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1582
|
+
await this.client.send(
|
|
1583
|
+
new PutObjectCommand({
|
|
1584
|
+
Bucket: this.props.bucket,
|
|
1585
|
+
Key: `${urn}.state`,
|
|
1586
|
+
Body: JSON.stringify(state)
|
|
1587
|
+
})
|
|
1588
|
+
);
|
|
1256
1589
|
}
|
|
1257
1590
|
async delete(urn) {
|
|
1258
|
-
await this.client.send(
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1591
|
+
await this.client.send(
|
|
1592
|
+
new DeleteObjectCommand({
|
|
1593
|
+
Bucket: this.props.bucket,
|
|
1594
|
+
Key: `${urn}.state`
|
|
1595
|
+
})
|
|
1596
|
+
);
|
|
1262
1597
|
}
|
|
1263
|
-
}
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1264
1600
|
// src/backend/aws/dynamodb-lock.ts
|
|
1265
1601
|
import { DynamoDB } from "@aws-sdk/client-dynamodb";
|
|
1266
1602
|
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
|
|
1267
|
-
|
|
1268
|
-
class DynamoLockBackend {
|
|
1269
|
-
props;
|
|
1270
|
-
client;
|
|
1603
|
+
var DynamoLockBackend = class {
|
|
1271
1604
|
constructor(props) {
|
|
1272
1605
|
this.props = props;
|
|
1273
1606
|
this.client = new DynamoDB(props);
|
|
1274
1607
|
}
|
|
1608
|
+
client;
|
|
1275
1609
|
async insecureReleaseLock(urn) {
|
|
1276
1610
|
await this.client.updateItem({
|
|
1277
1611
|
TableName: this.props.tableName,
|
|
@@ -1312,10 +1646,11 @@ class DynamoLockBackend {
|
|
|
1312
1646
|
});
|
|
1313
1647
|
};
|
|
1314
1648
|
}
|
|
1315
|
-
}
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1316
1651
|
// src/helpers.ts
|
|
1317
|
-
import { createHash } from "
|
|
1318
|
-
import { readFile as readFile2 } from "
|
|
1652
|
+
import { createHash } from "crypto";
|
|
1653
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
1319
1654
|
var file = (path, encoding = "utf8") => {
|
|
1320
1655
|
return new Future(async (resolve2, reject) => {
|
|
1321
1656
|
try {
|
|
@@ -1345,25 +1680,37 @@ var createCustomResourceClass = (providerId, resourceType) => {
|
|
|
1345
1680
|
}, {
|
|
1346
1681
|
construct(_, [parent, id, input, config]) {
|
|
1347
1682
|
const meta = createMeta("resource", `custom:${providerId}`, parent, resourceType, id, input, config);
|
|
1348
|
-
const node = new Proxy(
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1683
|
+
const node = new Proxy(
|
|
1684
|
+
{},
|
|
1685
|
+
{
|
|
1686
|
+
get(_2, key) {
|
|
1687
|
+
if (key === nodeMetaSymbol) {
|
|
1688
|
+
return meta;
|
|
1689
|
+
}
|
|
1690
|
+
if (key === "urn") {
|
|
1691
|
+
return meta.urn;
|
|
1692
|
+
}
|
|
1693
|
+
if (typeof key === "symbol") {
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
return meta.output((data) => data[key]);
|
|
1358
1697
|
}
|
|
1359
|
-
return meta.output((data) => data[key]);
|
|
1360
1698
|
}
|
|
1361
|
-
|
|
1699
|
+
);
|
|
1362
1700
|
parent.add(node);
|
|
1363
1701
|
return node;
|
|
1364
1702
|
}
|
|
1703
|
+
// get(_, key: string) {
|
|
1704
|
+
// if (key === 'get') {
|
|
1705
|
+
// return (...args: any[]) => {
|
|
1706
|
+
// return get(...args)
|
|
1707
|
+
// }
|
|
1708
|
+
// }
|
|
1709
|
+
// return
|
|
1710
|
+
// },
|
|
1365
1711
|
});
|
|
1366
1712
|
};
|
|
1713
|
+
|
|
1367
1714
|
// src/custom/provider.ts
|
|
1368
1715
|
var createCustomProvider = (providerId, resourceProviders) => {
|
|
1369
1716
|
const version = 1;
|
|
@@ -1420,6 +1767,21 @@ var createCustomProvider = (providerId, resourceProviders) => {
|
|
|
1420
1767
|
async deleteResource({ type, ...props }) {
|
|
1421
1768
|
await getProvider(type).deleteResource?.(props);
|
|
1422
1769
|
},
|
|
1770
|
+
async planResourceChange({ type, ...props }) {
|
|
1771
|
+
const provider = getProvider(type);
|
|
1772
|
+
if (!provider.planResourceChange) {
|
|
1773
|
+
return {
|
|
1774
|
+
version,
|
|
1775
|
+
state: props.proposedState,
|
|
1776
|
+
requiresReplacement: false
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
const result = await provider.planResourceChange(props);
|
|
1780
|
+
return {
|
|
1781
|
+
version,
|
|
1782
|
+
...result
|
|
1783
|
+
};
|
|
1784
|
+
},
|
|
1423
1785
|
async getData({ type, ...props }) {
|
|
1424
1786
|
return {
|
|
1425
1787
|
version,
|
|
@@ -1429,34 +1791,34 @@ var createCustomProvider = (providerId, resourceProviders) => {
|
|
|
1429
1791
|
};
|
|
1430
1792
|
};
|
|
1431
1793
|
export {
|
|
1432
|
-
|
|
1433
|
-
output,
|
|
1434
|
-
nodeMetaSymbol,
|
|
1435
|
-
isResource,
|
|
1436
|
-
isNode,
|
|
1437
|
-
isDataSource,
|
|
1438
|
-
getMeta,
|
|
1439
|
-
findInputDeps,
|
|
1440
|
-
enableDebug,
|
|
1441
|
-
deferredOutput,
|
|
1442
|
-
createMeta,
|
|
1443
|
-
createDebugger,
|
|
1444
|
-
createCustomResourceClass,
|
|
1445
|
-
createCustomProvider,
|
|
1446
|
-
WorkSpace,
|
|
1447
|
-
Stack,
|
|
1448
|
-
S3StateBackend,
|
|
1449
|
-
ResourceNotFound,
|
|
1450
|
-
ResourceError,
|
|
1451
|
-
ResourceAlreadyExists,
|
|
1452
|
-
Output,
|
|
1453
|
-
MemoryStateBackend,
|
|
1454
|
-
MemoryLockBackend,
|
|
1455
|
-
Group,
|
|
1456
|
-
Future,
|
|
1457
|
-
FileStateBackend,
|
|
1458
|
-
FileLockBackend,
|
|
1459
|
-
DynamoLockBackend,
|
|
1794
|
+
App,
|
|
1460
1795
|
AppError,
|
|
1461
|
-
|
|
1796
|
+
DynamoLockBackend,
|
|
1797
|
+
FileLockBackend,
|
|
1798
|
+
FileStateBackend,
|
|
1799
|
+
Future,
|
|
1800
|
+
Group,
|
|
1801
|
+
MemoryLockBackend,
|
|
1802
|
+
MemoryStateBackend,
|
|
1803
|
+
Output,
|
|
1804
|
+
ResourceAlreadyExists,
|
|
1805
|
+
ResourceError,
|
|
1806
|
+
ResourceNotFound,
|
|
1807
|
+
S3StateBackend,
|
|
1808
|
+
Stack,
|
|
1809
|
+
WorkSpace,
|
|
1810
|
+
createCustomProvider,
|
|
1811
|
+
createCustomResourceClass,
|
|
1812
|
+
createDebugger,
|
|
1813
|
+
createMeta,
|
|
1814
|
+
deferredOutput,
|
|
1815
|
+
enableDebug,
|
|
1816
|
+
findInputDeps,
|
|
1817
|
+
getMeta,
|
|
1818
|
+
isDataSource,
|
|
1819
|
+
isNode,
|
|
1820
|
+
isResource,
|
|
1821
|
+
nodeMetaSymbol,
|
|
1822
|
+
output,
|
|
1823
|
+
resolveInputs
|
|
1462
1824
|
};
|