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.
Files changed (103) hide show
  1. package/lib/$.d.ts +12 -0
  2. package/lib/$.d.ts.map +1 -0
  3. package/lib/$.js +42 -0
  4. package/lib/$.js.map +1 -0
  5. package/lib/alchemize.d.ts.map +1 -1
  6. package/lib/alchemize.js +7 -0
  7. package/lib/alchemize.js.map +1 -1
  8. package/lib/apply.d.ts +2 -2
  9. package/lib/apply.d.ts.map +1 -1
  10. package/lib/apply.js +1 -1
  11. package/lib/apply.js.map +1 -1
  12. package/lib/cloudflare/api.d.ts +4 -0
  13. package/lib/cloudflare/api.d.ts.map +1 -1
  14. package/lib/cloudflare/api.js +6 -0
  15. package/lib/cloudflare/api.js.map +1 -1
  16. package/lib/cloudflare/bindings.d.ts +14 -4
  17. package/lib/cloudflare/bindings.d.ts.map +1 -1
  18. package/lib/cloudflare/bindings.js +0 -5
  19. package/lib/cloudflare/bindings.js.map +1 -1
  20. package/lib/cloudflare/durable-object-namespace.d.ts +23 -0
  21. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -0
  22. package/lib/cloudflare/durable-object-namespace.js +26 -0
  23. package/lib/cloudflare/durable-object-namespace.js.map +1 -0
  24. package/lib/cloudflare/index.d.ts +1 -0
  25. package/lib/cloudflare/index.d.ts.map +1 -1
  26. package/lib/cloudflare/index.js +1 -0
  27. package/lib/cloudflare/index.js.map +1 -1
  28. package/lib/cloudflare/kv-namespace.js +1 -1
  29. package/lib/cloudflare/kv-namespace.js.map +1 -1
  30. package/lib/cloudflare/static-site-router.d.ts +11 -2
  31. package/lib/cloudflare/static-site-router.d.ts.map +1 -1
  32. package/lib/cloudflare/static-site-router.js +98 -76
  33. package/lib/cloudflare/static-site-router.js.map +1 -1
  34. package/lib/cloudflare/static-site.d.ts +15 -15
  35. package/lib/cloudflare/static-site.d.ts.map +1 -1
  36. package/lib/cloudflare/static-site.js +41 -25
  37. package/lib/cloudflare/static-site.js.map +1 -1
  38. package/lib/cloudflare/worker-metadata.d.ts +130 -0
  39. package/lib/cloudflare/worker-metadata.d.ts.map +1 -0
  40. package/lib/cloudflare/worker-metadata.js +2 -0
  41. package/lib/cloudflare/worker-metadata.js.map +1 -0
  42. package/lib/cloudflare/worker-migration.d.ts +110 -0
  43. package/lib/cloudflare/worker-migration.d.ts.map +1 -0
  44. package/lib/cloudflare/worker-migration.js +11 -0
  45. package/lib/cloudflare/worker-migration.js.map +1 -0
  46. package/lib/cloudflare/worker.d.ts +29 -11
  47. package/lib/cloudflare/worker.d.ts.map +1 -1
  48. package/lib/cloudflare/worker.js +316 -322
  49. package/lib/cloudflare/worker.js.map +1 -1
  50. package/lib/destroy.d.ts +1 -1
  51. package/lib/destroy.d.ts.map +1 -1
  52. package/lib/destroy.js +2 -1
  53. package/lib/destroy.js.map +1 -1
  54. package/lib/esbuild.d.ts +1 -1
  55. package/lib/esbuild.d.ts.map +1 -1
  56. package/lib/esbuild.js +3 -1
  57. package/lib/esbuild.js.map +1 -1
  58. package/lib/global.d.ts +0 -2
  59. package/lib/global.d.ts.map +1 -1
  60. package/lib/global.js +0 -2
  61. package/lib/global.js.map +1 -1
  62. package/lib/index.d.ts +2 -0
  63. package/lib/index.d.ts.map +1 -1
  64. package/lib/index.js +2 -0
  65. package/lib/index.js.map +1 -1
  66. package/lib/output.d.ts +4 -3
  67. package/lib/output.d.ts.map +1 -1
  68. package/lib/output.js +10 -3
  69. package/lib/output.js.map +1 -1
  70. package/lib/print.d.ts +7 -0
  71. package/lib/print.d.ts.map +1 -0
  72. package/lib/print.js +10 -0
  73. package/lib/print.js.map +1 -0
  74. package/lib/resource.d.ts +8 -4
  75. package/lib/resource.d.ts.map +1 -1
  76. package/lib/resource.js +125 -91
  77. package/lib/resource.js.map +1 -1
  78. package/lib/scope.d.ts +3 -0
  79. package/lib/scope.d.ts.map +1 -1
  80. package/lib/scope.js +4 -12
  81. package/lib/scope.js.map +1 -1
  82. package/package.json +3 -2
  83. package/src/$.ts +69 -0
  84. package/src/alchemize.ts +12 -0
  85. package/src/apply.ts +4 -4
  86. package/src/cloudflare/api.ts +6 -0
  87. package/src/cloudflare/bindings.ts +37 -26
  88. package/src/cloudflare/durable-object-namespace.ts +43 -0
  89. package/src/cloudflare/index.ts +1 -0
  90. package/src/cloudflare/kv-namespace.ts +1 -1
  91. package/src/cloudflare/static-site-router.ts +124 -82
  92. package/src/cloudflare/static-site.ts +63 -47
  93. package/src/cloudflare/worker-metadata.ts +154 -0
  94. package/src/cloudflare/worker-migration.ts +145 -0
  95. package/src/cloudflare/worker.ts +526 -428
  96. package/src/destroy.ts +2 -2
  97. package/src/esbuild.ts +3 -0
  98. package/src/global.ts +0 -3
  99. package/src/index.ts +2 -1
  100. package/src/output.ts +24 -8
  101. package/src/print.ts +18 -0
  102. package/src/resource.ts +156 -115
  103. package/src/scope.ts +7 -12
package/src/destroy.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { defaultStage, defaultStateStore, rootScope } from "./global";
1
+ import { defaultStage, defaultStateStore } from "./global";
2
2
  import type { Output } from "./output";
3
3
  import { Provider, ResourceID, isResource } from "./resource";
4
- import type { Scope } from "./scope";
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
- public readonly fn: (value: T) => U,
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
- // @ts-expect-error - we know we are an "Output"
26
- return new Output(this, fn);
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 Resolve<O> = O extends Output<infer U>
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]: Resolve<O[k]>;
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
- ? [Resolve<First>, ...ResolveN<Rest>]
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 Resolve } from "./output";
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
- Resolve<Out>
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: 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<Type, Args, Awaited<Out>>;
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
- // Skip update if inputs haven't changed and resource is in a stable state
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
- JSON.stringify(resourceState.inputs) === JSON.stringify(inputs) &&
290
- !resource[Options].alwaysUpdate
322
+ resourceState.status === "created" ||
323
+ resourceState.status === "updated"
291
324
  ) {
292
- if (!options?.quiet) {
293
- console.log(`Skip: ${resourceFQN} (no changes)`);
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
- const event = resourceState.status === "creating" ? "create" : "update";
303
- resourceState.status = event === "create" ? "creating" : "updating";
304
- resourceState.oldInputs = resourceState.inputs;
305
- resourceState.inputs = inputs;
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
- if (!options?.quiet) {
308
- console.log(
309
- `${event === "create" ? "Create" : "Update"}: ${resourceFQN}`,
310
- );
311
- }
344
+ if (!options?.quiet) {
345
+ console.log(
346
+ `${event === "create" ? "Create" : "Update"}: ${resourceFQN}`,
347
+ );
348
+ }
312
349
 
313
- await stateStore.set(resourceID, resourceState);
350
+ await stateStore.set(resourceID, resourceState);
314
351
 
315
- let isReplaced = false;
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
- const quiet = options.quiet ?? false;
400
+ if (result === undefined) {
401
+ return undefined;
402
+ }
318
403
 
319
- const evaluated = await pushScope(
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
- if (!options?.quiet) {
377
- console.log(
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 IScope(resourceID, scope);
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 { rootScope } from "./global";
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
- const newScope = new Scope(scopeName, parent);
55
- return withScope(newScope, fn);
50
+ return scopeStorage.run(new Scope(scopeName, parent), async () => fn());
56
51
  }