alchemy 0.1.12 → 0.1.13
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/lib/$.d.ts +12 -0
- package/lib/$.d.ts.map +1 -0
- package/lib/$.js +42 -0
- package/lib/$.js.map +1 -0
- package/lib/alchemize.d.ts.map +1 -1
- package/lib/alchemize.js +7 -0
- package/lib/alchemize.js.map +1 -1
- package/lib/apply.d.ts +2 -2
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +1 -1
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/api.d.ts +4 -0
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +6 -0
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +14 -4
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js +0 -5
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/durable-object-namespace.d.ts +23 -0
- package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -0
- package/lib/cloudflare/durable-object-namespace.js +26 -0
- package/lib/cloudflare/durable-object-namespace.js.map +1 -0
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.js +1 -1
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/static-site-router.d.ts +11 -2
- package/lib/cloudflare/static-site-router.d.ts.map +1 -1
- package/lib/cloudflare/static-site-router.js +98 -76
- package/lib/cloudflare/static-site-router.js.map +1 -1
- package/lib/cloudflare/static-site.d.ts +15 -15
- package/lib/cloudflare/static-site.d.ts.map +1 -1
- package/lib/cloudflare/static-site.js +41 -25
- package/lib/cloudflare/static-site.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +130 -0
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -0
- package/lib/cloudflare/worker-metadata.js +2 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -0
- package/lib/cloudflare/worker-migration.d.ts +110 -0
- package/lib/cloudflare/worker-migration.d.ts.map +1 -0
- package/lib/cloudflare/worker-migration.js +11 -0
- package/lib/cloudflare/worker-migration.js.map +1 -0
- package/lib/cloudflare/worker.d.ts +29 -11
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +316 -322
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/destroy.d.ts +1 -1
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +2 -1
- package/lib/destroy.js.map +1 -1
- package/lib/esbuild.d.ts +1 -1
- package/lib/esbuild.d.ts.map +1 -1
- package/lib/esbuild.js +3 -1
- package/lib/esbuild.js.map +1 -1
- package/lib/global.d.ts +0 -2
- package/lib/global.d.ts.map +1 -1
- package/lib/global.js +0 -2
- package/lib/global.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/output.d.ts +4 -3
- package/lib/output.d.ts.map +1 -1
- package/lib/output.js +10 -3
- package/lib/output.js.map +1 -1
- package/lib/print.d.ts +7 -0
- package/lib/print.d.ts.map +1 -0
- package/lib/print.js +10 -0
- package/lib/print.js.map +1 -0
- package/lib/resource.d.ts +8 -4
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js +125 -91
- package/lib/resource.js.map +1 -1
- package/lib/scope.d.ts +3 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +4 -12
- package/lib/scope.js.map +1 -1
- package/package.json +3 -2
- package/src/$.ts +69 -0
- package/src/alchemize.ts +12 -0
- package/src/apply.ts +4 -4
- package/src/cloudflare/api.ts +6 -0
- package/src/cloudflare/bindings.ts +37 -26
- package/src/cloudflare/durable-object-namespace.ts +43 -0
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/kv-namespace.ts +1 -1
- package/src/cloudflare/static-site-router.ts +124 -82
- package/src/cloudflare/static-site.ts +63 -47
- package/src/cloudflare/worker-metadata.ts +154 -0
- package/src/cloudflare/worker-migration.ts +145 -0
- package/src/cloudflare/worker.ts +526 -428
- package/src/destroy.ts +2 -2
- package/src/esbuild.ts +3 -0
- package/src/global.ts +0 -3
- package/src/index.ts +2 -1
- package/src/output.ts +24 -8
- package/src/print.ts +18 -0
- package/src/resource.ts +156 -115
- package/src/scope.ts +7 -12
package/src/destroy.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defaultStage, defaultStateStore
|
|
1
|
+
import { defaultStage, defaultStateStore } from "./global";
|
|
2
2
|
import type { Output } from "./output";
|
|
3
3
|
import { Provider, ResourceID, isResource } from "./resource";
|
|
4
|
-
import type
|
|
4
|
+
import { type Scope, rootScope } from "./scope";
|
|
5
5
|
import type { State, StateStore } from "./state";
|
|
6
6
|
|
|
7
7
|
export interface DestroyOptions {
|
package/src/esbuild.ts
CHANGED
|
@@ -60,6 +60,9 @@ export interface BundleOutput {
|
|
|
60
60
|
|
|
61
61
|
export class Bundle extends Resource(
|
|
62
62
|
"esbuild::Bundle",
|
|
63
|
+
{
|
|
64
|
+
alwaysUpdate: true,
|
|
65
|
+
},
|
|
63
66
|
async (ctx, props: BundleProps): Promise<BundleOutput> => {
|
|
64
67
|
// Determine output path
|
|
65
68
|
const outputPath = getOutputPath(props);
|
package/src/global.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type { Provider, ResourceType } from "./resource";
|
|
4
|
-
import { Scope } from "./scope";
|
|
5
4
|
import { FileSystemStateStore } from "./state";
|
|
6
5
|
|
|
7
6
|
export interface Config {
|
|
@@ -52,8 +51,6 @@ export const defaultStage = config.defaultStage ?? _stage;
|
|
|
52
51
|
|
|
53
52
|
export const defaultStateStore = config.stateStore ?? FileSystemStateStore;
|
|
54
53
|
|
|
55
|
-
export const rootScope = new Scope(null);
|
|
56
|
-
|
|
57
54
|
export const providers = new Map<
|
|
58
55
|
ResourceType,
|
|
59
56
|
Provider<ResourceType, any[], any>
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export * from "./$";
|
|
1
2
|
export * from "./alchemize";
|
|
2
3
|
export * from "./error";
|
|
3
4
|
export * from "./global";
|
|
4
5
|
export * from "./input";
|
|
5
6
|
export * from "./output";
|
|
7
|
+
export * from "./print";
|
|
6
8
|
export {
|
|
7
9
|
Resource,
|
|
8
10
|
type Context,
|
|
@@ -10,5 +12,4 @@ export {
|
|
|
10
12
|
type DeleteContext,
|
|
11
13
|
type UpdateContext,
|
|
12
14
|
} from "./resource";
|
|
13
|
-
|
|
14
15
|
export type { FileSystemStateStore as StateStore } from "./state";
|
package/src/output.ts
CHANGED
|
@@ -16,18 +16,34 @@ export interface Output<T> {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export class OutputChain<T, U> {
|
|
19
|
+
public readonly fn: (value: T) => U;
|
|
19
20
|
constructor(
|
|
20
21
|
public readonly parent: Output<T>,
|
|
21
|
-
|
|
22
|
-
) {
|
|
22
|
+
fn: (value: T) => U,
|
|
23
|
+
) {
|
|
24
|
+
let result:
|
|
25
|
+
| {
|
|
26
|
+
value: U;
|
|
27
|
+
}
|
|
28
|
+
| undefined;
|
|
29
|
+
this.fn = (value: T) => {
|
|
30
|
+
if (result === undefined) {
|
|
31
|
+
result = {
|
|
32
|
+
value: fn(value),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return result.value as U;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
23
38
|
|
|
24
|
-
public apply<V>(fn: (value: U) => V): Output<V
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
public apply<V>(fn: (value: U) => Output<V>): Output<V>;
|
|
40
|
+
public apply<V>(fn: (value: U) => V): Output<V>;
|
|
41
|
+
public apply<V>(fn: (value: U) => any): Output<V> {
|
|
42
|
+
return new OutputChain(this, fn);
|
|
27
43
|
}
|
|
28
44
|
}
|
|
29
45
|
|
|
30
|
-
export type
|
|
46
|
+
export type Resolved<O> = O extends Output<infer U>
|
|
31
47
|
? U
|
|
32
48
|
: O extends null
|
|
33
49
|
? O
|
|
@@ -35,12 +51,12 @@ export type Resolve<O> = O extends Output<infer U>
|
|
|
35
51
|
? ResolveN<O>
|
|
36
52
|
: O extends object
|
|
37
53
|
? {
|
|
38
|
-
[k in keyof O]:
|
|
54
|
+
[k in keyof O]: Resolved<O[k]>;
|
|
39
55
|
}
|
|
40
56
|
: O;
|
|
41
57
|
|
|
42
58
|
type ResolveN<O extends any[]> = O extends [infer First, ...infer Rest]
|
|
43
|
-
? [
|
|
59
|
+
? [Resolved<First>, ...ResolveN<Rest>]
|
|
44
60
|
: [];
|
|
45
61
|
|
|
46
62
|
// export data from a resource
|
package/src/print.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Resource } from "./resource";
|
|
2
|
+
|
|
3
|
+
export class Print extends Resource(
|
|
4
|
+
"Print",
|
|
5
|
+
{
|
|
6
|
+
alwaysUpdate: true,
|
|
7
|
+
},
|
|
8
|
+
async (
|
|
9
|
+
ctx,
|
|
10
|
+
values: {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
},
|
|
13
|
+
) => {
|
|
14
|
+
if (ctx.event !== "delete") {
|
|
15
|
+
console.log(JSON.stringify(values, null, 2));
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
) {}
|
package/src/resource.ts
CHANGED
|
@@ -3,14 +3,17 @@ import { type ApplyOptions, apply } from "./apply";
|
|
|
3
3
|
import type { DestroyOptions } from "./destroy";
|
|
4
4
|
import { defaultStateStore, deletions, providers } from "./global";
|
|
5
5
|
import type { Inputs, Input as input } from "./input";
|
|
6
|
-
import { type Export, type Output, OutputChain, type
|
|
7
|
-
import { Scope as IScope, getScope, pushScope } from "./scope";
|
|
6
|
+
import { type Export, type Output, OutputChain, type Resolved } from "./output";
|
|
7
|
+
import { type Scope as IScope, getScope, pushScope } from "./scope";
|
|
8
8
|
import type { State, StateStore } from "./state";
|
|
9
9
|
|
|
10
10
|
export type ResourceID = string;
|
|
11
|
+
export type ResourceFQN = string;
|
|
11
12
|
export type ResourceType = string;
|
|
12
13
|
|
|
13
14
|
export const ResourceID = Symbol.for("ResourceID");
|
|
15
|
+
export const ResourceFQN = Symbol.for("ResourceFQN");
|
|
16
|
+
export const ResourceHandle = Symbol.for("ResourceHandle");
|
|
14
17
|
export const Provider = Symbol.for("Provider");
|
|
15
18
|
export const Input = Symbol.for("Input");
|
|
16
19
|
export const Value = Symbol.for("Value");
|
|
@@ -22,6 +25,8 @@ export const Options = Symbol.for("Options");
|
|
|
22
25
|
// properties that pierce through the Proxy
|
|
23
26
|
const OrthogonalProperties = [
|
|
24
27
|
ResourceID,
|
|
28
|
+
ResourceFQN,
|
|
29
|
+
ResourceHandle,
|
|
25
30
|
Provider,
|
|
26
31
|
Input,
|
|
27
32
|
Value,
|
|
@@ -50,13 +55,14 @@ export type Resource<In extends any[] = any[], Out = any> = {
|
|
|
50
55
|
Export<
|
|
51
56
|
// the resource handler may export another Output - we need to resolve it first
|
|
52
57
|
// then, export that resolved value
|
|
53
|
-
|
|
58
|
+
Resolved<Out>
|
|
54
59
|
>;
|
|
55
60
|
|
|
56
61
|
export interface BaseContext {
|
|
57
62
|
quiet: boolean;
|
|
58
63
|
stage: string;
|
|
59
64
|
resourceID: ResourceID;
|
|
65
|
+
resourceFQN: ResourceFQN;
|
|
60
66
|
scope: IScope;
|
|
61
67
|
get<T>(key: string): Promise<T | undefined>;
|
|
62
68
|
set<T>(key: string, value: T): Promise<void>;
|
|
@@ -119,18 +125,24 @@ export function isResource(value: any): value is Resource {
|
|
|
119
125
|
return value?.[ResourceID] !== undefined;
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
const applied = new Map<Object, any>();
|
|
129
|
+
|
|
130
|
+
const destroyed = new WeakMap<Resource<any, any>, Promise<void>>();
|
|
131
|
+
|
|
132
|
+
const i = 0;
|
|
133
|
+
|
|
122
134
|
export function Resource<
|
|
123
|
-
const Type extends ResourceType,
|
|
135
|
+
// const Type extends ResourceType,
|
|
124
136
|
Args extends any[],
|
|
125
137
|
Out,
|
|
126
138
|
>(
|
|
127
|
-
type:
|
|
139
|
+
type: string,
|
|
128
140
|
options: Partial<ProviderOptions>,
|
|
129
141
|
func: (
|
|
130
142
|
ctx: Context<Out>,
|
|
131
143
|
...args: Args
|
|
132
144
|
) => Promise<input<Out> | void> | input<Out> | void,
|
|
133
|
-
): Provider<
|
|
145
|
+
): Provider<string, Args, Awaited<Out>>;
|
|
134
146
|
|
|
135
147
|
export function Resource<
|
|
136
148
|
const Type extends ResourceType,
|
|
@@ -172,6 +184,8 @@ export function Resource<
|
|
|
172
184
|
|
|
173
185
|
interface Resource {
|
|
174
186
|
[ResourceID]: ResourceID;
|
|
187
|
+
[ResourceFQN]: string;
|
|
188
|
+
[ResourceHandle]: Object;
|
|
175
189
|
[Provider]: Provider<Type, Args, Out>;
|
|
176
190
|
[Input]: Inputs<Args>;
|
|
177
191
|
[Value]?: Out;
|
|
@@ -196,7 +210,12 @@ export function Resource<
|
|
|
196
210
|
}
|
|
197
211
|
scope.nodes.set(id, node as any);
|
|
198
212
|
|
|
213
|
+
// const resourceFQN = scope.getScopePath(id) + "/" + id;
|
|
214
|
+
|
|
199
215
|
this[ResourceID] = id;
|
|
216
|
+
this[ResourceHandle] = new Object();
|
|
217
|
+
// this[ResourceFQN] = resourceFQN;
|
|
218
|
+
// this[ResourceFQN] = i++;
|
|
200
219
|
this[Provider] = Resource as any;
|
|
201
220
|
this[Input] = input;
|
|
202
221
|
this[Value] = undefined;
|
|
@@ -216,6 +235,8 @@ export function Resource<
|
|
|
216
235
|
get(target: any, prop) {
|
|
217
236
|
if (OrthogonalProperties.includes(prop as any)) {
|
|
218
237
|
return target[prop];
|
|
238
|
+
} else if (prop === "apply") {
|
|
239
|
+
return target[Apply].bind(target);
|
|
219
240
|
} else {
|
|
220
241
|
return target[Apply]((value: Out) => value[prop as keyof Out]);
|
|
221
242
|
}
|
|
@@ -223,6 +244,10 @@ export function Resource<
|
|
|
223
244
|
});
|
|
224
245
|
}
|
|
225
246
|
|
|
247
|
+
public apply<U>(fn: (value: Out) => U): Output<U> {
|
|
248
|
+
return this[Apply](fn);
|
|
249
|
+
}
|
|
250
|
+
|
|
226
251
|
public [Apply]<U>(fn: (value: Out) => U): Output<U> {
|
|
227
252
|
return new OutputChain<Out, U>(this as any, fn);
|
|
228
253
|
}
|
|
@@ -264,132 +289,146 @@ export function Resource<
|
|
|
264
289
|
): Promise<Awaited<Out | void>> {
|
|
265
290
|
// const stack = resource[ResourceStack];
|
|
266
291
|
const resourceID = resource[ResourceID];
|
|
267
|
-
|
|
268
|
-
let resourceState: State | undefined = await stateStore.get(resourceID);
|
|
269
|
-
if (resourceState === undefined) {
|
|
270
|
-
resourceState = {
|
|
271
|
-
provider: type,
|
|
272
|
-
status: "creating",
|
|
273
|
-
data: {},
|
|
274
|
-
output: undefined,
|
|
275
|
-
deps: [...deps],
|
|
276
|
-
inputs,
|
|
277
|
-
};
|
|
278
|
-
await stateStore.set(resourceID, resourceState);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
292
|
const resourceFQN = `${resource[Scope].getScopePath(stage)}/${resourceID}`;
|
|
293
|
+
const cacheKey = resource[ResourceHandle];
|
|
294
|
+
if (!applied.has(cacheKey)) {
|
|
295
|
+
let res, rej;
|
|
296
|
+
const promise = new Promise<Awaited<Out | void>>((resolve, reject) => {
|
|
297
|
+
res = resolve;
|
|
298
|
+
rej = reject;
|
|
299
|
+
});
|
|
300
|
+
applied.set(cacheKey, promise);
|
|
301
|
+
update().then(res).catch(rej);
|
|
302
|
+
return promise;
|
|
303
|
+
}
|
|
304
|
+
return await applied.get(cacheKey);
|
|
305
|
+
|
|
306
|
+
async function update(): Promise<Awaited<Out | void>> {
|
|
307
|
+
let resourceState: State | undefined = await stateStore.get(resourceID);
|
|
308
|
+
if (resourceState === undefined) {
|
|
309
|
+
resourceState = {
|
|
310
|
+
provider: type,
|
|
311
|
+
status: "creating",
|
|
312
|
+
data: {},
|
|
313
|
+
output: undefined,
|
|
314
|
+
deps: [...deps],
|
|
315
|
+
inputs,
|
|
316
|
+
};
|
|
317
|
+
await stateStore.set(resourceID, resourceState);
|
|
318
|
+
}
|
|
282
319
|
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
resourceState.status === "created" ||
|
|
286
|
-
resourceState.status === "updated"
|
|
287
|
-
) {
|
|
320
|
+
// Skip update if inputs haven't changed and resource is in a stable state
|
|
288
321
|
if (
|
|
289
|
-
|
|
290
|
-
|
|
322
|
+
resourceState.status === "created" ||
|
|
323
|
+
resourceState.status === "updated"
|
|
291
324
|
) {
|
|
292
|
-
if (
|
|
293
|
-
|
|
325
|
+
if (
|
|
326
|
+
JSON.stringify(resourceState.inputs) === JSON.stringify(inputs) &&
|
|
327
|
+
!resource[Options].alwaysUpdate
|
|
328
|
+
) {
|
|
329
|
+
if (!options?.quiet) {
|
|
330
|
+
console.log(`Skip: ${resourceFQN} (no changes)`);
|
|
331
|
+
}
|
|
332
|
+
if (resourceState.output !== undefined) {
|
|
333
|
+
resource[Provide](resourceState.output);
|
|
334
|
+
}
|
|
335
|
+
return resourceState.output;
|
|
294
336
|
}
|
|
295
|
-
if (resourceState.output !== undefined) {
|
|
296
|
-
resource[Provide](resourceState.output);
|
|
297
|
-
}
|
|
298
|
-
return resourceState.output;
|
|
299
337
|
}
|
|
300
|
-
}
|
|
301
338
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
339
|
+
const event = resourceState.status === "creating" ? "create" : "update";
|
|
340
|
+
resourceState.status = event === "create" ? "creating" : "updating";
|
|
341
|
+
resourceState.oldInputs = resourceState.inputs;
|
|
342
|
+
resourceState.inputs = inputs;
|
|
306
343
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
344
|
+
if (!options?.quiet) {
|
|
345
|
+
console.log(
|
|
346
|
+
`${event === "create" ? "Create" : "Update"}: ${resourceFQN}`,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
312
349
|
|
|
313
|
-
|
|
350
|
+
await stateStore.set(resourceID, resourceState);
|
|
314
351
|
|
|
315
|
-
|
|
352
|
+
let isReplaced = false;
|
|
353
|
+
|
|
354
|
+
const quiet = options.quiet ?? false;
|
|
355
|
+
|
|
356
|
+
const evaluated = await pushScope(
|
|
357
|
+
resource[Scope],
|
|
358
|
+
resourceID,
|
|
359
|
+
async () => {
|
|
360
|
+
const result = await func(
|
|
361
|
+
{
|
|
362
|
+
stage,
|
|
363
|
+
resourceID,
|
|
364
|
+
resourceFQN,
|
|
365
|
+
event,
|
|
366
|
+
scope: getScope(),
|
|
367
|
+
output: resourceState.output,
|
|
368
|
+
replace: () => {
|
|
369
|
+
if (isReplaced) {
|
|
370
|
+
console.warn(
|
|
371
|
+
`Resource ${type} ${resourceFQN} is already marked as REPLACE`,
|
|
372
|
+
);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
isReplaced = true;
|
|
376
|
+
deletions.push({
|
|
377
|
+
id: resourceID,
|
|
378
|
+
data: {
|
|
379
|
+
...resourceState!.data,
|
|
380
|
+
},
|
|
381
|
+
inputs: inputs,
|
|
382
|
+
});
|
|
383
|
+
},
|
|
384
|
+
get: (key) => resourceState!.data[key],
|
|
385
|
+
set: async (key, value) => {
|
|
386
|
+
resourceState!.data[key] = value;
|
|
387
|
+
await stateStore.set(resourceID, resourceState!);
|
|
388
|
+
},
|
|
389
|
+
delete: async (key) => {
|
|
390
|
+
const value = resourceState!.data[key];
|
|
391
|
+
delete resourceState!.data[key];
|
|
392
|
+
await stateStore.set(resourceID, resourceState!);
|
|
393
|
+
return value;
|
|
394
|
+
},
|
|
395
|
+
quiet,
|
|
396
|
+
},
|
|
397
|
+
...inputs,
|
|
398
|
+
);
|
|
316
399
|
|
|
317
|
-
|
|
400
|
+
if (result === undefined) {
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
318
403
|
|
|
319
|
-
|
|
320
|
-
resource[Scope],
|
|
321
|
-
resourceID,
|
|
322
|
-
async () => {
|
|
323
|
-
const result = await func(
|
|
324
|
-
{
|
|
404
|
+
const evaluated = await apply(result as Out, {
|
|
325
405
|
stage,
|
|
326
|
-
resourceID,
|
|
327
|
-
event,
|
|
328
406
|
scope: getScope(),
|
|
329
|
-
output: resourceState.output,
|
|
330
|
-
replace: () => {
|
|
331
|
-
if (isReplaced) {
|
|
332
|
-
console.warn(
|
|
333
|
-
`Resource ${type} ${resourceFQN} is already marked as REPLACE`,
|
|
334
|
-
);
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
isReplaced = true;
|
|
338
|
-
deletions.push({
|
|
339
|
-
id: resourceID,
|
|
340
|
-
data: {
|
|
341
|
-
...resourceState!.data,
|
|
342
|
-
},
|
|
343
|
-
inputs: inputs,
|
|
344
|
-
});
|
|
345
|
-
},
|
|
346
|
-
get: (key) => resourceState!.data[key],
|
|
347
|
-
set: async (key, value) => {
|
|
348
|
-
resourceState!.data[key] = value;
|
|
349
|
-
await stateStore.set(resourceID, resourceState!);
|
|
350
|
-
},
|
|
351
|
-
delete: async (key) => {
|
|
352
|
-
const value = resourceState!.data[key];
|
|
353
|
-
delete resourceState!.data[key];
|
|
354
|
-
await stateStore.set(resourceID, resourceState!);
|
|
355
|
-
return value;
|
|
356
|
-
},
|
|
357
407
|
quiet,
|
|
358
|
-
}
|
|
359
|
-
...inputs,
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
if (result === undefined) {
|
|
363
|
-
return undefined;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const evaluated = await apply(result as Out, {
|
|
367
|
-
stage,
|
|
368
|
-
scope: getScope(),
|
|
369
|
-
quiet,
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
return evaluated;
|
|
373
|
-
},
|
|
374
|
-
);
|
|
408
|
+
});
|
|
375
409
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
`${event === "create" ? "Created" : "Updated"}: ${resourceFQN}`,
|
|
410
|
+
return evaluated;
|
|
411
|
+
},
|
|
379
412
|
);
|
|
413
|
+
|
|
414
|
+
if (!options?.quiet) {
|
|
415
|
+
console.log(
|
|
416
|
+
`${event === "create" ? "Created" : "Updated"}: ${resourceFQN}`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
await stateStore.set(resourceID, {
|
|
420
|
+
provider: type,
|
|
421
|
+
data: resourceState.data,
|
|
422
|
+
status: event === "create" ? "created" : "updated",
|
|
423
|
+
output: evaluated,
|
|
424
|
+
inputs,
|
|
425
|
+
deps: [...deps],
|
|
426
|
+
});
|
|
427
|
+
if (evaluated !== undefined) {
|
|
428
|
+
resource[Provide](evaluated as Out);
|
|
429
|
+
}
|
|
430
|
+
return evaluated as Awaited<Out>;
|
|
380
431
|
}
|
|
381
|
-
await stateStore.set(resourceID, {
|
|
382
|
-
provider: type,
|
|
383
|
-
data: resourceState.data,
|
|
384
|
-
status: event === "create" ? "created" : "updated",
|
|
385
|
-
output: evaluated,
|
|
386
|
-
inputs,
|
|
387
|
-
deps: [...deps],
|
|
388
|
-
});
|
|
389
|
-
if (evaluated !== undefined) {
|
|
390
|
-
resource[Provide](evaluated as Out);
|
|
391
|
-
}
|
|
392
|
-
return evaluated as Awaited<Out>;
|
|
393
432
|
}
|
|
394
433
|
|
|
395
434
|
static async delete(
|
|
@@ -400,8 +439,9 @@ export function Resource<
|
|
|
400
439
|
inputs: Args,
|
|
401
440
|
options: DestroyOptions,
|
|
402
441
|
) {
|
|
442
|
+
const { Scope } = await import("./scope");
|
|
403
443
|
const resourceFQN = `${scope.getScopePath(stage)}/${resourceID}`;
|
|
404
|
-
const nestedScope = new
|
|
444
|
+
const nestedScope = new Scope(resourceID, scope);
|
|
405
445
|
|
|
406
446
|
await alchemize({
|
|
407
447
|
mode: "destroy",
|
|
@@ -421,6 +461,7 @@ export function Resource<
|
|
|
421
461
|
stage,
|
|
422
462
|
scope,
|
|
423
463
|
resourceID: resourceID,
|
|
464
|
+
resourceFQN: resourceFQN,
|
|
424
465
|
event: "delete",
|
|
425
466
|
output: state.output,
|
|
426
467
|
replace() {
|
package/src/scope.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import type { Output } from "./output";
|
|
4
4
|
import type { Provider, Resource, ResourceID } from "./resource";
|
|
5
5
|
|
|
6
|
-
const scopeGlobal = new Map<string | null, Scope>();
|
|
7
|
-
|
|
8
6
|
const scopeStorage = new AsyncLocalStorage<Scope>();
|
|
9
7
|
|
|
10
8
|
export class Scope {
|
|
@@ -16,6 +14,8 @@ export class Scope {
|
|
|
16
14
|
}
|
|
17
15
|
>();
|
|
18
16
|
|
|
17
|
+
public readonly callbacks: Output<void>[] = [];
|
|
18
|
+
|
|
19
19
|
constructor(
|
|
20
20
|
public readonly scopeName: string | null = null,
|
|
21
21
|
public readonly parent: Scope | undefined = undefined,
|
|
@@ -29,6 +29,8 @@ export class Scope {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export const rootScope = new Scope(null);
|
|
33
|
+
|
|
32
34
|
export function getScope(): Scope {
|
|
33
35
|
return scopeStorage.getStore() ?? rootScope;
|
|
34
36
|
}
|
|
@@ -37,13 +39,7 @@ export function withScope<T>(
|
|
|
37
39
|
scope: Scope,
|
|
38
40
|
fn: () => T | Promise<T>,
|
|
39
41
|
): Promise<T> {
|
|
40
|
-
return scopeStorage.run(scope, async () =>
|
|
41
|
-
try {
|
|
42
|
-
return await fn();
|
|
43
|
-
} catch (error) {
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
});
|
|
42
|
+
return scopeStorage.run(scope, async () => fn());
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
export function pushScope<T>(
|
|
@@ -51,6 +47,5 @@ export function pushScope<T>(
|
|
|
51
47
|
scopeName: string,
|
|
52
48
|
fn: () => T | Promise<T>,
|
|
53
49
|
): Promise<T> {
|
|
54
|
-
|
|
55
|
-
return withScope(newScope, fn);
|
|
50
|
+
return scopeStorage.run(new Scope(scopeName, parent), async () => fn());
|
|
56
51
|
}
|