alchemy 0.1.18 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/alchemy.d.ts +67 -0
- package/lib/alchemy.js +40 -0
- package/lib/apply.d.ts +3 -20
- package/lib/apply.js +86 -82
- package/lib/aws/account-id.d.ts +0 -1
- package/lib/aws/account-id.js +0 -1
- package/lib/aws/bucket.d.ts +4 -7
- package/lib/aws/bucket.js +8 -12
- package/lib/aws/function.d.ts +4 -7
- package/lib/aws/function.js +9 -30
- package/lib/aws/index.d.ts +1 -1
- package/lib/aws/index.js +1 -1
- package/lib/aws/oidc/github-oidc-provider.d.ts +2 -6
- package/lib/aws/oidc/github-oidc-provider.js +10 -15
- package/lib/aws/oidc/index.d.ts +0 -1
- package/lib/aws/oidc/index.js +0 -1
- package/lib/aws/oidc/oidc-provider.d.ts +4 -6
- package/lib/aws/oidc/oidc-provider.js +9 -11
- package/lib/aws/policy-attachment.d.ts +9 -0
- package/lib/aws/policy-attachment.js +20 -0
- package/lib/aws/policy.d.ts +4 -16
- package/lib/aws/policy.js +9 -40
- package/lib/aws/queue.d.ts +4 -34
- package/lib/aws/queue.js +10 -20
- package/lib/aws/role.d.ts +4 -7
- package/lib/aws/role.js +17 -26
- package/lib/aws/ses.d.ts +4 -10
- package/lib/aws/ses.js +118 -131
- package/lib/aws/table.d.ts +4 -7
- package/lib/aws/table.js +8 -11
- package/lib/cloudflare/api.d.ts +0 -1
- package/lib/cloudflare/api.js +0 -1
- package/lib/cloudflare/asset-manifest.d.ts +0 -1
- package/lib/cloudflare/asset-manifest.js +0 -1
- package/lib/cloudflare/auth.d.ts +0 -1
- package/lib/cloudflare/auth.js +0 -1
- package/lib/cloudflare/bindings.d.ts +1 -1
- package/lib/cloudflare/bindings.js +3 -2
- package/lib/cloudflare/bound.d.ts +0 -1
- package/lib/cloudflare/bound.js +0 -1
- package/lib/cloudflare/bucket.d.ts +7 -15
- package/lib/cloudflare/bucket.js +12 -19
- package/lib/cloudflare/durable-object-namespace.d.ts +0 -3
- package/lib/cloudflare/durable-object-namespace.js +0 -4
- package/lib/cloudflare/generate-asset-manifest.d.ts +0 -1
- package/lib/cloudflare/generate-asset-manifest.js +1 -2
- package/lib/cloudflare/index.d.ts +0 -1
- package/lib/cloudflare/index.js +0 -1
- package/lib/cloudflare/kv-namespace.d.ts +6 -9
- package/lib/cloudflare/kv-namespace.js +16 -20
- package/lib/cloudflare/state.d.ts +0 -1
- package/lib/cloudflare/state.js +0 -1
- package/lib/cloudflare/static-site-router.d.ts +0 -1
- package/lib/cloudflare/static-site-router.js +0 -4
- package/lib/cloudflare/static-site.d.ts +5 -11
- package/lib/cloudflare/static-site.js +16 -28
- package/lib/cloudflare/types.d.ts +0 -1
- package/lib/cloudflare/types.js +0 -1
- package/lib/cloudflare/upload-asset-manifest.d.ts +0 -1
- package/lib/cloudflare/upload-asset-manifest.js +0 -1
- package/lib/cloudflare/worker-metadata.d.ts +0 -1
- package/lib/cloudflare/worker-metadata.js +0 -1
- package/lib/cloudflare/worker-migration.d.ts +0 -1
- package/lib/cloudflare/worker-migration.js +0 -1
- package/lib/cloudflare/worker.d.ts +8 -14
- package/lib/cloudflare/worker.js +37 -49
- package/lib/context.d.ts +58 -0
- package/lib/context.js +36 -0
- package/lib/destroy.d.ts +10 -13
- package/lib/destroy.js +104 -43
- package/lib/{esbuild.d.ts → esbuild/bundle.d.ts} +4 -18
- package/lib/{esbuild.js → esbuild/bundle.js} +10 -24
- package/lib/esbuild/index.d.ts +1 -0
- package/lib/esbuild/index.js +1 -0
- package/lib/fs/file.d.ts +13 -0
- package/lib/fs/file.js +20 -0
- package/lib/fs/folder.d.ts +10 -0
- package/lib/fs/folder.js +16 -0
- package/lib/fs/index.d.ts +2 -0
- package/lib/fs/index.js +2 -0
- package/lib/github/client.d.ts +0 -1
- package/lib/github/client.js +0 -1
- package/lib/github/index.d.ts +0 -1
- package/lib/github/index.js +0 -1
- package/lib/github/secret.d.ts +4 -6
- package/lib/github/secret.js +10 -12
- package/lib/index.d.ts +6 -10
- package/lib/index.js +5 -9
- package/lib/resource.d.ts +34 -62
- package/lib/resource.js +29 -270
- package/lib/scope.d.ts +35 -14
- package/lib/scope.js +78 -23
- package/lib/secret.d.ts +2 -7
- package/lib/secret.js +7 -20
- package/lib/state.d.ts +14 -10
- package/lib/state.js +15 -14
- package/lib/stripe/index.d.ts +0 -1
- package/lib/stripe/index.js +0 -1
- package/lib/stripe/price.d.ts +4 -5
- package/lib/stripe/price.js +10 -20
- package/lib/stripe/product.d.ts +4 -5
- package/lib/stripe/product.js +10 -20
- package/lib/stripe/webhook.d.ts +4 -6
- package/lib/stripe/webhook.js +12 -25
- package/lib/test/bun.d.ts +19 -0
- package/lib/test/bun.js +55 -0
- package/lib/{utils → util}/content-type.d.ts +0 -1
- package/lib/{utils → util}/content-type.js +0 -1
- package/lib/{encrypt.d.ts → util/encrypt.d.ts} +0 -1
- package/lib/{encrypt.js → util/encrypt.js} +0 -1
- package/lib/{error.d.ts → util/ignore.d.ts} +0 -1
- package/lib/{error.js → util/ignore.js} +0 -1
- package/lib/{utils → util}/retry.d.ts +0 -1
- package/lib/{utils → util}/retry.js +0 -1
- package/lib/util/serde.d.ts +3 -0
- package/lib/{serde.js → util/serde.js} +19 -10
- package/lib/util/slugify.d.ts +1 -0
- package/lib/util/slugify.js +3 -0
- package/package.json +5 -4
- package/src/alchemy.ts +120 -0
- package/src/apply.ts +109 -114
- package/src/aws/bucket.ts +11 -14
- package/src/aws/function.ts +13 -33
- package/src/aws/index.ts +1 -0
- package/src/aws/oidc/github-oidc-provider.ts +14 -19
- package/src/aws/oidc/oidc-provider.ts +19 -12
- package/src/aws/policy-attachment.ts +51 -0
- package/src/aws/policy.ts +15 -60
- package/src/aws/queue.ts +17 -21
- package/src/aws/role.ts +25 -28
- package/src/aws/ses.ts +150 -164
- package/src/aws/table.ts +15 -15
- package/src/cloudflare/bindings.ts +8 -0
- package/src/cloudflare/bucket.ts +39 -47
- package/src/cloudflare/durable-object-namespace.ts +0 -10
- package/src/cloudflare/generate-asset-manifest.ts +1 -1
- package/src/cloudflare/kv-namespace.ts +35 -33
- package/src/cloudflare/static-site-router.ts +0 -3
- package/src/cloudflare/static-site.ts +44 -60
- package/src/cloudflare/worker.ts +105 -111
- package/src/context.ts +122 -0
- package/src/destroy.ts +124 -73
- package/src/{esbuild.ts → esbuild/bundle.ts} +16 -28
- package/src/esbuild/index.ts +1 -0
- package/src/fs/file.ts +39 -0
- package/src/fs/folder.ts +32 -0
- package/src/fs/index.ts +2 -0
- package/src/github/secret.ts +19 -16
- package/src/index.ts +7 -15
- package/src/resource.ts +101 -462
- package/src/scope.ts +101 -36
- package/src/secret.ts +9 -32
- package/src/state.ts +30 -21
- package/src/stripe/price.ts +17 -22
- package/src/stripe/product.ts +17 -22
- package/src/stripe/webhook.ts +21 -27
- package/src/test/bun.ts +105 -0
- package/src/util/serde.ts +53 -0
- package/src/util/slugify.ts +3 -0
- package/lib/$.d.ts +0 -12
- package/lib/$.d.ts.map +0 -1
- package/lib/$.js +0 -42
- package/lib/$.js.map +0 -1
- package/lib/alchemize.d.ts +0 -47
- package/lib/alchemize.d.ts.map +0 -1
- package/lib/alchemize.js +0 -116
- package/lib/alchemize.js.map +0 -1
- package/lib/apply.d.ts.map +0 -1
- package/lib/apply.js.map +0 -1
- package/lib/aws/account-id.d.ts.map +0 -1
- package/lib/aws/account-id.js.map +0 -1
- package/lib/aws/bucket.d.ts.map +0 -1
- package/lib/aws/bucket.js.map +0 -1
- package/lib/aws/function.d.ts.map +0 -1
- package/lib/aws/function.js.map +0 -1
- package/lib/aws/index.d.ts.map +0 -1
- package/lib/aws/index.js.map +0 -1
- package/lib/aws/oidc/github-oidc-provider.d.ts.map +0 -1
- package/lib/aws/oidc/github-oidc-provider.js.map +0 -1
- package/lib/aws/oidc/index.d.ts.map +0 -1
- package/lib/aws/oidc/index.js.map +0 -1
- package/lib/aws/oidc/oidc-provider.d.ts.map +0 -1
- package/lib/aws/oidc/oidc-provider.js.map +0 -1
- package/lib/aws/policy.d.ts.map +0 -1
- package/lib/aws/policy.js.map +0 -1
- package/lib/aws/queue.d.ts.map +0 -1
- package/lib/aws/queue.js.map +0 -1
- package/lib/aws/role.d.ts.map +0 -1
- package/lib/aws/role.js.map +0 -1
- package/lib/aws/ses.d.ts.map +0 -1
- package/lib/aws/ses.js.map +0 -1
- package/lib/aws/table.d.ts.map +0 -1
- package/lib/aws/table.js.map +0 -1
- package/lib/cloudflare/api.d.ts.map +0 -1
- package/lib/cloudflare/api.js.map +0 -1
- package/lib/cloudflare/asset-manifest.d.ts.map +0 -1
- package/lib/cloudflare/asset-manifest.js.map +0 -1
- package/lib/cloudflare/auth.d.ts.map +0 -1
- package/lib/cloudflare/auth.js.map +0 -1
- package/lib/cloudflare/bindings.d.ts.map +0 -1
- package/lib/cloudflare/bindings.js.map +0 -1
- package/lib/cloudflare/bound.d.ts.map +0 -1
- package/lib/cloudflare/bound.js.map +0 -1
- package/lib/cloudflare/bucket.d.ts.map +0 -1
- package/lib/cloudflare/bucket.js.map +0 -1
- package/lib/cloudflare/durable-object-namespace.d.ts.map +0 -1
- package/lib/cloudflare/durable-object-namespace.js.map +0 -1
- package/lib/cloudflare/generate-asset-manifest.d.ts.map +0 -1
- package/lib/cloudflare/generate-asset-manifest.js.map +0 -1
- package/lib/cloudflare/index.d.ts.map +0 -1
- package/lib/cloudflare/index.js.map +0 -1
- package/lib/cloudflare/kv-namespace.d.ts.map +0 -1
- package/lib/cloudflare/kv-namespace.js.map +0 -1
- package/lib/cloudflare/state.d.ts.map +0 -1
- package/lib/cloudflare/state.js.map +0 -1
- package/lib/cloudflare/static-site-router.d.ts.map +0 -1
- package/lib/cloudflare/static-site-router.js.map +0 -1
- package/lib/cloudflare/static-site.d.ts.map +0 -1
- package/lib/cloudflare/static-site.js.map +0 -1
- package/lib/cloudflare/types.d.ts.map +0 -1
- package/lib/cloudflare/types.js.map +0 -1
- package/lib/cloudflare/upload-asset-manifest.d.ts.map +0 -1
- package/lib/cloudflare/upload-asset-manifest.js.map +0 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +0 -1
- package/lib/cloudflare/worker-metadata.js.map +0 -1
- package/lib/cloudflare/worker-migration.d.ts.map +0 -1
- package/lib/cloudflare/worker-migration.js.map +0 -1
- package/lib/cloudflare/worker.d.ts.map +0 -1
- package/lib/cloudflare/worker.js.map +0 -1
- package/lib/destroy.d.ts.map +0 -1
- package/lib/destroy.js.map +0 -1
- package/lib/encrypt.d.ts.map +0 -1
- package/lib/encrypt.js.map +0 -1
- package/lib/error.d.ts.map +0 -1
- package/lib/error.js.map +0 -1
- package/lib/esbuild.d.ts.map +0 -1
- package/lib/esbuild.js.map +0 -1
- package/lib/fs.d.ts +0 -12
- package/lib/fs.d.ts.map +0 -1
- package/lib/fs.js +0 -41
- package/lib/fs.js.map +0 -1
- package/lib/github/client.d.ts.map +0 -1
- package/lib/github/client.js.map +0 -1
- package/lib/github/index.d.ts.map +0 -1
- package/lib/github/index.js.map +0 -1
- package/lib/github/secret.d.ts.map +0 -1
- package/lib/github/secret.js.map +0 -1
- package/lib/global.d.ts +0 -16
- package/lib/global.d.ts.map +0 -1
- package/lib/global.js +0 -42
- package/lib/global.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/input.d.ts +0 -7
- package/lib/input.d.ts.map +0 -1
- package/lib/input.js +0 -2
- package/lib/input.js.map +0 -1
- package/lib/main.d.ts +0 -3
- package/lib/main.d.ts.map +0 -1
- package/lib/main.js +0 -32
- package/lib/main.js.map +0 -1
- package/lib/output.d.ts +0 -25
- package/lib/output.d.ts.map +0 -1
- package/lib/output.js +0 -23
- package/lib/output.js.map +0 -1
- package/lib/print.d.ts +0 -7
- package/lib/print.d.ts.map +0 -1
- package/lib/print.js +0 -10
- package/lib/print.js.map +0 -1
- package/lib/resource.d.ts.map +0 -1
- package/lib/resource.js.map +0 -1
- package/lib/scope.d.ts.map +0 -1
- package/lib/scope.js.map +0 -1
- package/lib/secret.d.ts.map +0 -1
- package/lib/secret.js.map +0 -1
- package/lib/serde.d.ts +0 -3
- package/lib/serde.d.ts.map +0 -1
- package/lib/serde.js.map +0 -1
- package/lib/slug.d.ts +0 -2
- package/lib/slug.d.ts.map +0 -1
- package/lib/slug.js +0 -4
- package/lib/slug.js.map +0 -1
- package/lib/state.d.ts.map +0 -1
- package/lib/state.js.map +0 -1
- package/lib/stripe/index.d.ts.map +0 -1
- package/lib/stripe/index.js.map +0 -1
- package/lib/stripe/price.d.ts.map +0 -1
- package/lib/stripe/price.js.map +0 -1
- package/lib/stripe/product.d.ts.map +0 -1
- package/lib/stripe/product.js.map +0 -1
- package/lib/stripe/webhook.d.ts.map +0 -1
- package/lib/stripe/webhook.js.map +0 -1
- package/lib/utils/content-type.d.ts.map +0 -1
- package/lib/utils/content-type.js.map +0 -1
- package/lib/utils/retry.d.ts.map +0 -1
- package/lib/utils/retry.js.map +0 -1
- package/src/$.ts +0 -69
- package/src/alchemize.ts +0 -195
- package/src/fs.ts +0 -47
- package/src/global.ts +0 -63
- package/src/input.ts +0 -19
- package/src/main.ts +0 -34
- package/src/output.ts +0 -75
- package/src/print.ts +0 -18
- package/src/serde.ts +0 -46
- package/src/slug.ts +0 -3
- /package/src/{utils → util}/content-type.ts +0 -0
- /package/src/{encrypt.ts → util/encrypt.ts} +0 -0
- /package/src/{error.ts → util/ignore.ts} +0 -0
- /package/src/{utils → util}/retry.ts +0 -0
package/src/resource.ts
CHANGED
|
@@ -1,40 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { type Export, type Output, OutputChain, type Resolved } from "./output";
|
|
7
|
-
import { type Scope as IScope, getScope, pushScope } from "./scope";
|
|
8
|
-
import type { State, StateStore } from "./state";
|
|
1
|
+
import { apply } from "./apply";
|
|
2
|
+
import type { Context } from "./context";
|
|
3
|
+
import { Scope as _Scope } from "./scope";
|
|
4
|
+
|
|
5
|
+
export const PROVIDERS = new Map<ResourceKind, Provider<string, any>>();
|
|
9
6
|
|
|
10
7
|
export type ResourceID = string;
|
|
11
8
|
export type ResourceFQN = string;
|
|
12
|
-
export type
|
|
13
|
-
|
|
14
|
-
export const ResourceID = Symbol.for("ResourceID");
|
|
15
|
-
export const ResourceFQN = Symbol.for("ResourceFQN");
|
|
16
|
-
export const ResourceHandle = Symbol.for("ResourceHandle");
|
|
17
|
-
export const Provider = Symbol.for("Provider");
|
|
18
|
-
export const Input = Symbol.for("Input");
|
|
19
|
-
export const Value = Symbol.for("Value");
|
|
20
|
-
export const Apply = Symbol.for("Apply");
|
|
21
|
-
export const Provide = Symbol.for("Provide");
|
|
22
|
-
export const Scope = Symbol.for("Scope");
|
|
23
|
-
export const Options = Symbol.for("Options");
|
|
24
|
-
|
|
25
|
-
// properties that pierce through the Proxy
|
|
26
|
-
const OrthogonalProperties = [
|
|
27
|
-
ResourceID,
|
|
28
|
-
ResourceFQN,
|
|
29
|
-
ResourceHandle,
|
|
30
|
-
Provider,
|
|
31
|
-
Input,
|
|
32
|
-
Value,
|
|
33
|
-
Apply,
|
|
34
|
-
Provide,
|
|
35
|
-
Scope,
|
|
36
|
-
Options,
|
|
37
|
-
] as const;
|
|
9
|
+
export type ResourceKind = string;
|
|
38
10
|
|
|
39
11
|
export interface ProviderOptions {
|
|
40
12
|
/**
|
|
@@ -43,451 +15,118 @@ export interface ProviderOptions {
|
|
|
43
15
|
alwaysUpdate: boolean;
|
|
44
16
|
}
|
|
45
17
|
|
|
46
|
-
export type
|
|
47
|
-
[
|
|
48
|
-
|
|
49
|
-
[Input]: Inputs<In>;
|
|
50
|
-
[Value]?: Out;
|
|
51
|
-
[Apply]: <O>(value: Out) => O;
|
|
52
|
-
[Provide]: (value: Out) => void;
|
|
53
|
-
[Options]: ProviderOptions;
|
|
54
|
-
} & Output<Out> &
|
|
55
|
-
Export<
|
|
56
|
-
// the resource handler may export another Output - we need to resolve it first
|
|
57
|
-
// then, export that resolved value
|
|
58
|
-
Resolved<Out>
|
|
59
|
-
>;
|
|
60
|
-
|
|
61
|
-
export interface BaseContext {
|
|
62
|
-
quiet: boolean;
|
|
63
|
-
stage: string;
|
|
64
|
-
resourceID: ResourceID;
|
|
65
|
-
resourceFQN: ResourceFQN;
|
|
66
|
-
scope: IScope;
|
|
67
|
-
get<T>(key: string): Promise<T | undefined>;
|
|
68
|
-
set<T>(key: string, value: T): Promise<void>;
|
|
69
|
-
delete<T>(key: string): Promise<T | undefined>;
|
|
70
|
-
/**
|
|
71
|
-
* Indicate that this resource is being replaced.
|
|
72
|
-
* This will cause the resource to be deleted at the end of the stack's CREATE phase.
|
|
73
|
-
*/
|
|
74
|
-
replace(): void;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface CreateContext extends BaseContext {
|
|
78
|
-
event: "create";
|
|
79
|
-
output?: undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface UpdateContext<Outputs> extends BaseContext {
|
|
83
|
-
event: "update";
|
|
84
|
-
output: Outputs;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface DeleteContext<Outputs> extends BaseContext {
|
|
88
|
-
event: "delete";
|
|
89
|
-
output: Outputs;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export type Context<Outputs> =
|
|
93
|
-
| CreateContext
|
|
94
|
-
| UpdateContext<Outputs>
|
|
95
|
-
| DeleteContext<Outputs>;
|
|
18
|
+
export type ResourceProps = {
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
};
|
|
96
21
|
|
|
97
22
|
export type Provider<
|
|
98
|
-
Type extends
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
23
|
+
Type extends string = string,
|
|
24
|
+
F extends ResourceLifecycleHandler = ResourceLifecycleHandler,
|
|
25
|
+
> = F &
|
|
26
|
+
IsClass & {
|
|
27
|
+
type: Type;
|
|
28
|
+
options: Partial<ProviderOptions> | undefined;
|
|
29
|
+
handler: F;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type PendingResource<
|
|
33
|
+
Out = unknown,
|
|
34
|
+
Kind extends ResourceKind = ResourceKind,
|
|
35
|
+
ID extends ResourceID = ResourceID,
|
|
36
|
+
FQN extends ResourceFQN = ResourceFQN,
|
|
37
|
+
Scope extends _Scope = _Scope,
|
|
38
|
+
Seq extends number = number,
|
|
39
|
+
> = Promise<Out> & {
|
|
40
|
+
Kind: Kind;
|
|
41
|
+
ID: ID;
|
|
42
|
+
FQN: FQN;
|
|
43
|
+
Seq: Seq;
|
|
44
|
+
Scope: Scope;
|
|
45
|
+
signal: () => void;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export interface Resource<
|
|
49
|
+
// give each name types for syntax highlighting (differentiation)
|
|
50
|
+
Kind extends ResourceKind = ResourceKind,
|
|
51
|
+
ID extends ResourceID = ResourceID,
|
|
52
|
+
FQN extends ResourceFQN = ResourceFQN,
|
|
53
|
+
Scope extends _Scope = _Scope,
|
|
54
|
+
Seq extends number = number,
|
|
55
|
+
> {
|
|
56
|
+
// use capital letters to avoid collision with conventional camelCase typescript properties
|
|
57
|
+
Kind: Kind;
|
|
58
|
+
ID: ID;
|
|
59
|
+
FQN: FQN;
|
|
60
|
+
Scope: Scope;
|
|
61
|
+
Seq: Seq;
|
|
126
62
|
}
|
|
127
63
|
|
|
128
|
-
|
|
64
|
+
// helper for semantic syntax highlighting (color as a type/class instead of function/value)
|
|
65
|
+
type IsClass = {
|
|
66
|
+
new (_: never): never;
|
|
67
|
+
};
|
|
129
68
|
|
|
130
|
-
|
|
69
|
+
type ResourceLifecycleHandler = (
|
|
70
|
+
this: Context<any>,
|
|
71
|
+
id: string,
|
|
72
|
+
props: any,
|
|
73
|
+
) => Promise<Resource<string>>;
|
|
131
74
|
|
|
132
|
-
|
|
75
|
+
// see: https://x.com/samgoodwin89/status/1904640134097887653
|
|
76
|
+
type Handler<F extends (...args: any[]) => any> =
|
|
77
|
+
| F
|
|
78
|
+
| (((this: any, id: string, props: Parameters<F>[1]) => never) & IsClass);
|
|
133
79
|
|
|
134
80
|
export function Resource<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
>(
|
|
139
|
-
type: string,
|
|
140
|
-
options: Partial<ProviderOptions>,
|
|
141
|
-
func: (
|
|
142
|
-
ctx: Context<Out>,
|
|
143
|
-
...args: Args
|
|
144
|
-
) => Promise<input<Out> | void> | input<Out> | void,
|
|
145
|
-
): Provider<string, Args, Awaited<Out>>;
|
|
81
|
+
const Type extends string,
|
|
82
|
+
F extends ResourceLifecycleHandler,
|
|
83
|
+
>(type: Type, fn: F): Handler<F>;
|
|
146
84
|
|
|
147
85
|
export function Resource<
|
|
148
|
-
const Type extends
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
>(
|
|
152
|
-
type: Type,
|
|
153
|
-
func: (
|
|
154
|
-
ctx: Context<Out>,
|
|
155
|
-
...args: Args
|
|
156
|
-
) => Promise<input<Out> | void> | input<Out> | void,
|
|
157
|
-
): Provider<Type, Args, Awaited<Out>>;
|
|
86
|
+
const Type extends string,
|
|
87
|
+
F extends ResourceLifecycleHandler,
|
|
88
|
+
>(type: Type, options: Partial<ProviderOptions>, fn: F): Handler<F>;
|
|
158
89
|
|
|
159
90
|
export function Resource<
|
|
160
|
-
const Type extends
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
type: Type,
|
|
165
|
-
...args:
|
|
166
|
-
| [
|
|
167
|
-
Partial<ProviderOptions>,
|
|
168
|
-
(
|
|
169
|
-
ctx: Context<Out>,
|
|
170
|
-
...args: Args
|
|
171
|
-
) => Promise<input<Out> | void> | input<Out> | void,
|
|
172
|
-
]
|
|
173
|
-
| [
|
|
174
|
-
(
|
|
175
|
-
ctx: Context<Out>,
|
|
176
|
-
...args: Args
|
|
177
|
-
) => Promise<input<Out> | void> | input<Out> | void,
|
|
178
|
-
]
|
|
179
|
-
): Provider<Type, Args, Awaited<Out>> {
|
|
180
|
-
if (providers.has(type)) {
|
|
91
|
+
const Type extends ResourceKind,
|
|
92
|
+
F extends ResourceLifecycleHandler,
|
|
93
|
+
>(type: Type, ...args: [Partial<ProviderOptions>, F] | [F]): Handler<F> {
|
|
94
|
+
if (PROVIDERS.has(type)) {
|
|
181
95
|
throw new Error(`Resource ${type} already exists`);
|
|
182
96
|
}
|
|
183
|
-
const [options,
|
|
184
|
-
|
|
185
|
-
interface Resource {
|
|
186
|
-
[ResourceID]: ResourceID;
|
|
187
|
-
[ResourceFQN]: string;
|
|
188
|
-
[ResourceHandle]: Object;
|
|
189
|
-
[Provider]: Provider<Type, Args, Out>;
|
|
190
|
-
[Input]: Inputs<Args>;
|
|
191
|
-
[Value]?: Out;
|
|
192
|
-
[Scope]: IScope;
|
|
193
|
-
[Options]: ProviderOptions;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
class Resource {
|
|
197
|
-
static readonly type = type;
|
|
198
|
-
|
|
199
|
-
constructor(id: ResourceID, ...input: Inputs<Args>) {
|
|
200
|
-
const scope = getScope();
|
|
201
|
-
const node = {
|
|
202
|
-
provider: Resource,
|
|
203
|
-
resource: this,
|
|
204
|
-
} as const;
|
|
205
|
-
|
|
206
|
-
if (scope.nodes.has(id)) {
|
|
207
|
-
// TODO(sam): do we want to throw?
|
|
208
|
-
// it's kind of awesome that you can re-create a resource and call apply
|
|
209
|
-
// console.warn(`Resource ${id} already exists in the stack: ${stack.id}`);
|
|
210
|
-
}
|
|
211
|
-
scope.nodes.set(id, node as any);
|
|
212
|
-
|
|
213
|
-
// const resourceFQN = scope.getScopePath(id) + "/" + id;
|
|
214
|
-
|
|
215
|
-
this[ResourceID] = id;
|
|
216
|
-
this[ResourceHandle] = new Object();
|
|
217
|
-
// this[ResourceFQN] = resourceFQN;
|
|
218
|
-
// this[ResourceFQN] = i++;
|
|
219
|
-
this[Provider] = Resource as any;
|
|
220
|
-
this[Input] = input;
|
|
221
|
-
this[Value] = undefined;
|
|
222
|
-
this[Scope] = scope;
|
|
223
|
-
this[Provide] = (value: Out) => {
|
|
224
|
-
this[Value] = value;
|
|
225
|
-
};
|
|
226
|
-
this[Options] = {
|
|
227
|
-
alwaysUpdate: options?.alwaysUpdate ?? false,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
return new Proxy(this, {
|
|
231
|
-
// TODO(sam): this won't work for the sub-class (class Table extends Resource)
|
|
232
|
-
getPrototypeOf() {
|
|
233
|
-
return Resource.prototype;
|
|
234
|
-
},
|
|
235
|
-
get(target: any, prop) {
|
|
236
|
-
if (OrthogonalProperties.includes(prop as any)) {
|
|
237
|
-
return target[prop];
|
|
238
|
-
} else if (prop === "apply") {
|
|
239
|
-
return target[Apply].bind(target);
|
|
240
|
-
} else {
|
|
241
|
-
return target[Apply]((value: Out) => value[prop as keyof Out]);
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
|
-
});
|
|
245
|
-
}
|
|
97
|
+
const [options, handler] = args.length === 2 ? args : [undefined, args[0]];
|
|
246
98
|
|
|
247
|
-
|
|
248
|
-
return this[Apply](fn);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
public [Apply]<U>(fn: (value: Out) => U): Output<U> {
|
|
252
|
-
return new OutputChain<Out, U>(this as any, fn);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
private box?: {
|
|
256
|
-
value: Out;
|
|
257
|
-
} = undefined;
|
|
258
|
-
|
|
259
|
-
public [Provide](value: Out) {
|
|
260
|
-
if (this.box) {
|
|
261
|
-
throw new Error(`Output ${this[ResourceID]} already has a value`);
|
|
262
|
-
}
|
|
263
|
-
this.box = { value };
|
|
264
|
-
const subscribers = this.subscribers;
|
|
265
|
-
this.subscribers = [];
|
|
266
|
-
subscribers.forEach((fn) => fn(value));
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private subscribers: ((value: Out) => void)[] = [];
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* @internal
|
|
273
|
-
*/
|
|
274
|
-
public subscribe(fn: (value: Out) => Promise<void>) {
|
|
275
|
-
if (this.box) {
|
|
276
|
-
fn(this.box.value);
|
|
277
|
-
} else {
|
|
278
|
-
this.subscribers.push(fn);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
static async update(
|
|
283
|
-
stage: string,
|
|
284
|
-
resource: Resource,
|
|
285
|
-
deps: Set<ResourceID>,
|
|
286
|
-
inputs: Args,
|
|
287
|
-
stateStore: StateStore,
|
|
288
|
-
options: ApplyOptions,
|
|
289
|
-
): Promise<Awaited<Out | void>> {
|
|
290
|
-
// const stack = resource[ResourceStack];
|
|
291
|
-
const resourceID = resource[ResourceID];
|
|
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
|
-
}
|
|
319
|
-
|
|
320
|
-
// Skip update if inputs haven't changed and resource is in a stable state
|
|
321
|
-
if (
|
|
322
|
-
resourceState.status === "created" ||
|
|
323
|
-
resourceState.status === "updated"
|
|
324
|
-
) {
|
|
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;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const event = resourceState.status === "creating" ? "create" : "update";
|
|
340
|
-
resourceState.status = event === "create" ? "creating" : "updating";
|
|
341
|
-
resourceState.oldInputs = resourceState.inputs;
|
|
342
|
-
resourceState.inputs = inputs;
|
|
99
|
+
type Out = Awaited<ReturnType<F>>;
|
|
343
100
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
101
|
+
const provider = ((
|
|
102
|
+
resourceID: string,
|
|
103
|
+
props: ResourceProps,
|
|
104
|
+
): Promise<Resource<string>> => {
|
|
105
|
+
const scope = _Scope.current;
|
|
349
106
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
);
|
|
399
|
-
|
|
400
|
-
if (result === undefined) {
|
|
401
|
-
return undefined;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const evaluated = await apply(result as Out, {
|
|
405
|
-
stage,
|
|
406
|
-
scope: getScope(),
|
|
407
|
-
quiet,
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
return evaluated;
|
|
411
|
-
},
|
|
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>;
|
|
431
|
-
}
|
|
107
|
+
if (scope.resources.has(resourceID)) {
|
|
108
|
+
// TODO(sam): do we want to throw?
|
|
109
|
+
// it's kind of awesome that you can re-create a resource and call apply
|
|
110
|
+
// console.warn(`Resource ${id} already exists in the stack: ${stack.id}`);
|
|
432
111
|
}
|
|
433
112
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
if (!options?.quiet) {
|
|
456
|
-
console.log(`Delete: ${resourceFQN}`);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
await func(
|
|
460
|
-
{
|
|
461
|
-
stage,
|
|
462
|
-
scope,
|
|
463
|
-
resourceID: resourceID,
|
|
464
|
-
resourceFQN: resourceFQN,
|
|
465
|
-
event: "delete",
|
|
466
|
-
output: state.output,
|
|
467
|
-
replace() {
|
|
468
|
-
throw new Error("Cannot replace a resource that is being deleted");
|
|
469
|
-
},
|
|
470
|
-
get: (key) => {
|
|
471
|
-
return state.data[key];
|
|
472
|
-
},
|
|
473
|
-
set: async (key, value) => {
|
|
474
|
-
state.data[key] = value;
|
|
475
|
-
},
|
|
476
|
-
delete: async (key) => {
|
|
477
|
-
const value = state.data[key];
|
|
478
|
-
delete state.data[key];
|
|
479
|
-
return value;
|
|
480
|
-
},
|
|
481
|
-
quiet: options.quiet ?? false,
|
|
482
|
-
},
|
|
483
|
-
...inputs,
|
|
484
|
-
);
|
|
485
|
-
|
|
486
|
-
if (!options?.quiet) {
|
|
487
|
-
console.log(`Deleted: ${resourceFQN}`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
providers.set(type, Resource as any);
|
|
492
|
-
return Resource as any;
|
|
113
|
+
// get a sequence number (unique within the scope) for the resource
|
|
114
|
+
const seq = scope.seq();
|
|
115
|
+
const meta = {
|
|
116
|
+
Kind: type,
|
|
117
|
+
ID: resourceID,
|
|
118
|
+
FQN: scope.fqn(resourceID),
|
|
119
|
+
Seq: seq,
|
|
120
|
+
Scope: scope,
|
|
121
|
+
} as any as PendingResource<Out>;
|
|
122
|
+
const promise = apply(meta, props, options);
|
|
123
|
+
const resource = Object.assign(promise, meta);
|
|
124
|
+
scope.resources.set(resourceID, resource);
|
|
125
|
+
return resource;
|
|
126
|
+
}) as Provider<Type, F>;
|
|
127
|
+
provider.type = type;
|
|
128
|
+
provider.handler = handler;
|
|
129
|
+
provider.options = options;
|
|
130
|
+
PROVIDERS.set(type, provider);
|
|
131
|
+
return provider;
|
|
493
132
|
}
|