alchemy 0.1.18 → 0.2.1
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 +506 -0
- package/lib/alchemy.d.ts +67 -0
- package/lib/alchemy.js +48 -0
- package/lib/apply.d.ts +3 -20
- package/lib/apply.js +95 -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 +2 -1
- package/lib/cloudflare/index.js +2 -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 +25 -37
- 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 +38 -50
- package/lib/cloudflare/wrangler.json.d.ts +243 -0
- package/lib/cloudflare/wrangler.json.js +35 -0
- package/lib/cloudflare/zone-settings.d.ts +301 -0
- package/lib/cloudflare/zone-settings.js +1 -0
- package/lib/cloudflare/zone.d.ts +196 -0
- package/lib/cloudflare/zone.js +175 -0
- 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/project/index.d.ts +1 -0
- package/lib/project/index.js +1 -0
- package/lib/project/vite.d.ts +39 -0
- package/lib/project/vite.js +68 -0
- package/lib/resource.d.ts +34 -62
- package/lib/resource.js +29 -270
- package/lib/scope.d.ts +37 -14
- package/lib/scope.js +97 -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 +20 -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/rm.d.ts +1 -0
- package/lib/util/rm.js +11 -0
- 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 +7 -5
- package/src/alchemy.ts +127 -0
- package/src/apply.ts +116 -115
- 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/index.ts +2 -0
- package/src/cloudflare/kv-namespace.ts +35 -33
- package/src/cloudflare/static-site-router.ts +0 -3
- package/src/cloudflare/static-site.ts +55 -71
- package/src/cloudflare/worker.ts +106 -112
- package/src/cloudflare/wrangler.json.ts +309 -0
- package/src/cloudflare/zone-settings.ts +368 -0
- package/src/cloudflare/zone.ts +491 -0
- 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/project/index.ts +1 -0
- package/src/project/vite.ts +152 -0
- package/src/resource.ts +101 -462
- package/src/scope.ts +125 -36
- package/src/secret.ts +9 -32
- package/src/state.ts +38 -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/rm.ts +11 -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/alchemy.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { DestroyedSignal, destroy } from "./destroy";
|
|
2
|
+
import { Scope } from "./scope";
|
|
3
|
+
import { secret } from "./secret";
|
|
4
|
+
import type { StateStoreType } from "./state";
|
|
5
|
+
|
|
6
|
+
// TODO: support browser
|
|
7
|
+
const DEFAULT_STAGE = process.env.ALCHEMY_STAGE ?? process.env.USER ?? "dev";
|
|
8
|
+
|
|
9
|
+
function _alchemy(appName: string, options: Omit<AlchemyOptions, "appName">) {
|
|
10
|
+
return scope(undefined, {
|
|
11
|
+
...options,
|
|
12
|
+
appName,
|
|
13
|
+
stage: options.stage,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
_alchemy.destroy = destroy;
|
|
17
|
+
_alchemy.run = run;
|
|
18
|
+
_alchemy.scope = scope;
|
|
19
|
+
_alchemy.secret = secret;
|
|
20
|
+
|
|
21
|
+
// alchemy type is to semantically highlight `alchemy` as a type (keyword)
|
|
22
|
+
export type alchemy = Alchemy;
|
|
23
|
+
|
|
24
|
+
export const alchemy: Alchemy = _alchemy as any;
|
|
25
|
+
|
|
26
|
+
// Alchemy is for module augmentation
|
|
27
|
+
export interface Alchemy {
|
|
28
|
+
scope: typeof scope;
|
|
29
|
+
run: typeof run;
|
|
30
|
+
destroy: typeof destroy;
|
|
31
|
+
secret: typeof secret;
|
|
32
|
+
(...parameters: Parameters<typeof scope>): ReturnType<typeof scope>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AlchemyOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The name of the application.
|
|
38
|
+
*/
|
|
39
|
+
appName?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Determines whether the resources will be created/updated or deleted.
|
|
42
|
+
*
|
|
43
|
+
* @default "up"
|
|
44
|
+
*/
|
|
45
|
+
phase?: "up" | "destroy";
|
|
46
|
+
/**
|
|
47
|
+
* Name to scope the resource state under (e.g. `.alchemy/{stage}/..`).
|
|
48
|
+
*
|
|
49
|
+
* @default - your POSIX username
|
|
50
|
+
*/
|
|
51
|
+
stage?: string;
|
|
52
|
+
/**
|
|
53
|
+
* If true, will not prune resources that were dropped from the root stack.
|
|
54
|
+
*
|
|
55
|
+
* @default true
|
|
56
|
+
*/
|
|
57
|
+
destroyOrphans?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* A custom state store to use instead of the default file system store.
|
|
60
|
+
*/
|
|
61
|
+
stateStore?: StateStoreType;
|
|
62
|
+
/**
|
|
63
|
+
* A custom scope to use as a parent.
|
|
64
|
+
*/
|
|
65
|
+
parent?: Scope;
|
|
66
|
+
/**
|
|
67
|
+
* If true, will not print any Create/Update/Delete messages.
|
|
68
|
+
*
|
|
69
|
+
* @default false
|
|
70
|
+
*/
|
|
71
|
+
quiet?: boolean;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A passphrase to use to encrypt/decrypt secrets.
|
|
75
|
+
*/
|
|
76
|
+
password?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Enter a new scope synchronously.
|
|
81
|
+
* @param options
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
84
|
+
function scope(
|
|
85
|
+
id: string | undefined,
|
|
86
|
+
options?: AlchemyOptions,
|
|
87
|
+
// TODO: maybe we want to allow using _ = await alchemy.scope(import.meta)
|
|
88
|
+
// | [meta: ImportMeta]
|
|
89
|
+
): Scope {
|
|
90
|
+
const scope = new Scope({
|
|
91
|
+
...options,
|
|
92
|
+
appName: options?.appName,
|
|
93
|
+
stage: options?.stage ?? DEFAULT_STAGE,
|
|
94
|
+
scopeName: id,
|
|
95
|
+
parent: options?.parent ?? Scope.get(),
|
|
96
|
+
});
|
|
97
|
+
scope.enter();
|
|
98
|
+
return scope;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function run<T>(
|
|
102
|
+
...args:
|
|
103
|
+
| [id: string, fn: (this: Scope, scope: Scope) => Promise<T>]
|
|
104
|
+
| [
|
|
105
|
+
id: string,
|
|
106
|
+
options: AlchemyOptions,
|
|
107
|
+
fn: (this: Scope, scope: Scope) => Promise<T>,
|
|
108
|
+
]
|
|
109
|
+
): Promise<T> {
|
|
110
|
+
const [id, options, fn] =
|
|
111
|
+
typeof args[1] === "function"
|
|
112
|
+
? [args[0], undefined, args[1]]
|
|
113
|
+
: (args as [
|
|
114
|
+
string,
|
|
115
|
+
AlchemyOptions | undefined,
|
|
116
|
+
(this: Scope, scope: Scope) => Promise<T>,
|
|
117
|
+
]);
|
|
118
|
+
await using scope = alchemy.scope(id, options);
|
|
119
|
+
try {
|
|
120
|
+
return await fn.bind(scope)(scope);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (!(error instanceof DestroyedSignal)) {
|
|
123
|
+
scope.fail();
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
package/src/apply.ts
CHANGED
|
@@ -1,135 +1,136 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { alchemy } from "./alchemy";
|
|
2
|
+
import { context } from "./context";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
PROVIDERS,
|
|
5
|
+
type PendingResource,
|
|
6
|
+
type Provider,
|
|
6
7
|
type Resource,
|
|
7
|
-
|
|
8
|
-
isResource,
|
|
8
|
+
type ResourceProps,
|
|
9
9
|
} from "./resource";
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import type { StateStore } from "./state";
|
|
10
|
+
import type { State } from "./state";
|
|
11
|
+
import { serialize } from "./util/serde";
|
|
13
12
|
|
|
14
13
|
export interface ApplyOptions {
|
|
15
|
-
stage: string;
|
|
16
|
-
scope: IScope;
|
|
17
|
-
stateStore: StateStore;
|
|
18
14
|
quiet?: boolean;
|
|
15
|
+
alwaysUpdate?: boolean;
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
export async function apply<Out extends Resource>(
|
|
19
|
+
resource: PendingResource<Out>,
|
|
20
|
+
props: ResourceProps,
|
|
21
|
+
options?: ApplyOptions,
|
|
22
|
+
): Promise<Awaited<Out>> {
|
|
23
|
+
const scope = resource.Scope;
|
|
24
|
+
try {
|
|
25
|
+
const quiet = props.quiet ?? scope.quiet;
|
|
26
|
+
await scope.init();
|
|
27
|
+
let state: State | undefined = (await scope.state.get(resource.ID))!;
|
|
28
|
+
const provider: Provider = PROVIDERS.get(resource.Kind);
|
|
29
|
+
if (provider === undefined) {
|
|
30
|
+
throw new Error(`Provider "${resource.Kind}" not found`);
|
|
31
|
+
}
|
|
32
|
+
if (state === undefined) {
|
|
33
|
+
state = {
|
|
34
|
+
kind: resource.Kind,
|
|
35
|
+
id: resource.ID,
|
|
36
|
+
fqn: resource.FQN,
|
|
37
|
+
seq: resource.Seq,
|
|
38
|
+
status: "creating",
|
|
39
|
+
data: {},
|
|
40
|
+
output: {
|
|
41
|
+
ID: resource.ID,
|
|
42
|
+
FQN: resource.FQN,
|
|
43
|
+
Kind: resource.Kind,
|
|
44
|
+
Scope: scope,
|
|
45
|
+
Seq: resource.Seq,
|
|
46
|
+
},
|
|
47
|
+
// deps: [...deps],
|
|
48
|
+
props,
|
|
49
|
+
};
|
|
50
|
+
await scope.state.set(resource.ID, state);
|
|
51
|
+
}
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
public readonly value: T,
|
|
43
|
-
public readonly deps: string[] = [],
|
|
44
|
-
) {}
|
|
45
|
-
}
|
|
53
|
+
const alwaysUpdate =
|
|
54
|
+
options?.alwaysUpdate ?? provider.options?.alwaysUpdate ?? false;
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
// Skip update if inputs haven't changed and resource is in a stable state
|
|
57
|
+
if (state.status === "created" || state.status === "updated") {
|
|
58
|
+
if (
|
|
59
|
+
JSON.stringify(state.props) ===
|
|
60
|
+
JSON.stringify(await serialize(scope, props)) &&
|
|
61
|
+
alwaysUpdate !== true
|
|
62
|
+
) {
|
|
63
|
+
if (!quiet) {
|
|
64
|
+
console.log(`Skip: "${resource.FQN}" (no changes)`);
|
|
65
|
+
}
|
|
66
|
+
// if (resourceState.output !== undefined) {
|
|
67
|
+
// resource[Provide](resourceState.output);
|
|
68
|
+
// }
|
|
69
|
+
return state.output as Awaited<Out>;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
48
72
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const stage = options.stage;
|
|
54
|
-
const stateStore = options.stateStore;
|
|
55
|
-
if (isResource(output)) {
|
|
56
|
-
const resource = output;
|
|
57
|
-
const resourceID = resource[ResourceID];
|
|
58
|
-
const evaluated = await Promise.all(
|
|
59
|
-
resource[Input].map((r) => evaluate<any>(r, options)),
|
|
60
|
-
);
|
|
73
|
+
const phase = state.status === "creating" ? "create" : "update";
|
|
74
|
+
state.status = phase === "create" ? "creating" : "updating";
|
|
75
|
+
state.oldProps = state.props;
|
|
76
|
+
state.props = props;
|
|
61
77
|
|
|
62
|
-
if (
|
|
63
|
-
|
|
78
|
+
if (!quiet) {
|
|
79
|
+
console.log(
|
|
80
|
+
`${phase === "create" ? "Create" : "Update"}: "${resource.FQN}"`,
|
|
81
|
+
);
|
|
64
82
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
|
|
84
|
+
await scope.state.set(resource.ID, state);
|
|
85
|
+
|
|
86
|
+
let isReplaced = false;
|
|
87
|
+
|
|
88
|
+
const ctx = context({
|
|
89
|
+
scope,
|
|
90
|
+
phase,
|
|
91
|
+
kind: resource.Kind,
|
|
92
|
+
id: resource.ID,
|
|
93
|
+
fqn: resource.FQN,
|
|
94
|
+
seq: resource.Seq,
|
|
95
|
+
state,
|
|
96
|
+
replace: () => {
|
|
97
|
+
if (isReplaced) {
|
|
98
|
+
console.warn(
|
|
99
|
+
`Resource ${resource.Kind} ${resource.FQN} is already marked as REPLACE`,
|
|
100
|
+
);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
isReplaced = true;
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const output = await alchemy.run(resource.ID, async () => {
|
|
108
|
+
return provider.handler.bind(ctx)(resource.ID, props);
|
|
70
109
|
});
|
|
71
|
-
// set this eagerly to avoid double-apply (caused by recursive applies to inputs)
|
|
72
|
-
cache.set(resource, promise);
|
|
73
110
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const result: T = await resource[Provider].update(
|
|
78
|
-
stage,
|
|
79
|
-
resource,
|
|
80
|
-
deps,
|
|
81
|
-
inputs as [],
|
|
82
|
-
stateStore,
|
|
83
|
-
options,
|
|
111
|
+
if (!quiet) {
|
|
112
|
+
console.log(
|
|
113
|
+
`${phase === "create" ? "Created" : "Updated"}: "${resource.FQN}"`,
|
|
84
114
|
);
|
|
85
|
-
resolve!(new Evaluated(result, [resourceID, ...deps]));
|
|
86
|
-
} catch (error) {
|
|
87
|
-
console.error(error);
|
|
88
|
-
reject!(error);
|
|
89
115
|
}
|
|
90
|
-
return promise;
|
|
91
|
-
} else if (output instanceof OutputChain) {
|
|
92
|
-
const inside = output as unknown as {
|
|
93
|
-
parent: Output<any>;
|
|
94
|
-
fn: (value: any) => T;
|
|
95
|
-
};
|
|
96
|
-
const parent = await evaluate(inside.parent, options);
|
|
97
|
-
const ret = inside.fn(parent.value);
|
|
98
|
-
// the ret may be an Output (e.g. in the flatMap case), so we need to evaluate it and include its deps
|
|
99
|
-
const evaluated = await evaluate(ret, options);
|
|
100
|
-
return new Evaluated<T>(evaluated.value, [
|
|
101
|
-
...parent.deps,
|
|
102
|
-
...evaluated.deps,
|
|
103
|
-
]);
|
|
104
|
-
} else if (Array.isArray(output)) {
|
|
105
|
-
const evaluatedItems = await Promise.all(
|
|
106
|
-
output.map((item) => evaluate(item, options)),
|
|
107
|
-
);
|
|
108
|
-
return new Evaluated(
|
|
109
|
-
evaluatedItems.map((e) => e.value) as unknown as T,
|
|
110
|
-
evaluatedItems.flatMap((e) => e.deps),
|
|
111
|
-
);
|
|
112
|
-
} else if (output instanceof Date) {
|
|
113
|
-
return new Evaluated(output as T);
|
|
114
|
-
} else if (output instanceof Secret) {
|
|
115
|
-
return new Evaluated(output as T);
|
|
116
|
-
} else if (output && typeof output === "object") {
|
|
117
|
-
const entries = Object.entries(output);
|
|
118
|
-
const evaluatedEntries = await Promise.all(
|
|
119
|
-
entries.map(
|
|
120
|
-
async ([key, value]) => [key, await evaluate(value, options)] as const,
|
|
121
|
-
),
|
|
122
|
-
);
|
|
123
116
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
117
|
+
await scope.state.set(resource.ID, {
|
|
118
|
+
kind: resource.Kind,
|
|
119
|
+
id: resource.ID,
|
|
120
|
+
fqn: resource.FQN,
|
|
121
|
+
seq: resource.Seq,
|
|
122
|
+
data: state.data,
|
|
123
|
+
status: phase === "create" ? "created" : "updated",
|
|
124
|
+
output,
|
|
125
|
+
props,
|
|
126
|
+
// deps: [...deps],
|
|
127
|
+
});
|
|
128
|
+
// if (output !== undefined) {
|
|
129
|
+
// resource[Provide](output as Out);
|
|
130
|
+
// }
|
|
131
|
+
return output as any;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
scope.fail();
|
|
134
|
+
throw error;
|
|
131
135
|
}
|
|
132
|
-
|
|
133
|
-
// Base case: primitive value
|
|
134
|
-
return new Evaluated<T>(output as T);
|
|
135
136
|
}
|
package/src/aws/bucket.ts
CHANGED
|
@@ -10,16 +10,16 @@ import {
|
|
|
10
10
|
PutBucketTaggingCommand,
|
|
11
11
|
S3Client,
|
|
12
12
|
} from "@aws-sdk/client-s3";
|
|
13
|
-
import {
|
|
13
|
+
import type { Context } from "../context";
|
|
14
14
|
import { Resource } from "../resource";
|
|
15
|
+
import { ignore } from "../util/ignore";
|
|
15
16
|
|
|
16
17
|
export interface BucketProps {
|
|
17
18
|
bucketName: string;
|
|
18
19
|
tags?: Record<string, string>;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export interface
|
|
22
|
-
id: string; // Same as bucketName for AWS
|
|
22
|
+
export interface Bucket extends Resource<"s3::Bucket">, BucketProps {
|
|
23
23
|
arn: string;
|
|
24
24
|
bucketDomainName: string; // Format: bucket-name.s3.amazonaws.com
|
|
25
25
|
bucketRegionalDomainName?: string; // Format: bucket-name.s3.region.amazonaws.com
|
|
@@ -31,12 +31,12 @@ export interface BucketOutput extends BucketProps {
|
|
|
31
31
|
acl?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export
|
|
34
|
+
export const Bucket = Resource(
|
|
35
35
|
"s3::Bucket",
|
|
36
|
-
async (
|
|
36
|
+
async function (this: Context<Bucket>, id: string, props: BucketProps) {
|
|
37
37
|
const client = new S3Client({});
|
|
38
38
|
|
|
39
|
-
if (
|
|
39
|
+
if (this.phase === "delete") {
|
|
40
40
|
await ignore(NoSuchBucket.name, () =>
|
|
41
41
|
client.send(
|
|
42
42
|
new DeleteBucketCommand({
|
|
@@ -44,7 +44,7 @@ export class Bucket extends Resource(
|
|
|
44
44
|
}),
|
|
45
45
|
),
|
|
46
46
|
);
|
|
47
|
-
return
|
|
47
|
+
return this.destroy();
|
|
48
48
|
} else {
|
|
49
49
|
try {
|
|
50
50
|
// Check if bucket exists
|
|
@@ -55,7 +55,7 @@ export class Bucket extends Resource(
|
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
// Update tags if they changed and bucket exists
|
|
58
|
-
if (
|
|
58
|
+
if (this.phase === "update" && props.tags) {
|
|
59
59
|
await client.send(
|
|
60
60
|
new PutBucketTaggingCommand({
|
|
61
61
|
Bucket: props.bucketName,
|
|
@@ -121,9 +121,8 @@ export class Bucket extends Resource(
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
return this({
|
|
125
125
|
bucketName: props.bucketName,
|
|
126
|
-
id: props.bucketName,
|
|
127
126
|
arn: `arn:aws:s3:::${props.bucketName}`,
|
|
128
127
|
bucketDomainName: `${props.bucketName}.s3.amazonaws.com`,
|
|
129
128
|
bucketRegionalDomainName: `${props.bucketName}.s3.${region}.amazonaws.com`,
|
|
@@ -132,12 +131,10 @@ export class Bucket extends Resource(
|
|
|
132
131
|
versioningEnabled: versioningResponse.Status === "Enabled",
|
|
133
132
|
acl: aclResponse.Grants?.[0]?.Permission?.toLowerCase(),
|
|
134
133
|
...(tags && { tags }),
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
return output;
|
|
134
|
+
});
|
|
138
135
|
}
|
|
139
136
|
},
|
|
140
|
-
)
|
|
137
|
+
);
|
|
141
138
|
|
|
142
139
|
// Helper function to get S3 hosted zone IDs by region
|
|
143
140
|
function getHostedZoneId(region: string): string {
|
package/src/aws/function.ts
CHANGED
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
} from "@aws-sdk/client-lambda";
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import type { Context } from "../context";
|
|
16
|
+
import { Resource } from "../resource";
|
|
17
|
+
import { ignore } from "../util/ignore";
|
|
17
18
|
|
|
18
19
|
async function resolveRegion(client: LambdaClient): Promise<string> {
|
|
19
20
|
const region = client.config.region;
|
|
@@ -47,8 +48,7 @@ export interface FunctionProps {
|
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
export interface
|
|
51
|
-
id: string; // Same as functionName
|
|
51
|
+
export interface Function extends Resource<"lambda::Function">, FunctionProps {
|
|
52
52
|
arn: string;
|
|
53
53
|
lastModified: string;
|
|
54
54
|
version: string;
|
|
@@ -71,15 +71,15 @@ export interface FunctionOutput extends FunctionProps {
|
|
|
71
71
|
signingJobArn?: string;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
export
|
|
74
|
+
export const Function = Resource(
|
|
75
75
|
"lambda::Function",
|
|
76
|
-
async (
|
|
76
|
+
async function (this: Context<Function>, id: string, props: FunctionProps) {
|
|
77
77
|
const client = new LambdaClient({});
|
|
78
78
|
const region = await resolveRegion(client);
|
|
79
79
|
|
|
80
80
|
const code = await zipCode(props.zipPath);
|
|
81
81
|
|
|
82
|
-
if (
|
|
82
|
+
if (this.phase === "delete") {
|
|
83
83
|
await ignore(ResourceNotFoundException.name, () =>
|
|
84
84
|
client.send(
|
|
85
85
|
new DeleteFunctionCommand({
|
|
@@ -88,34 +88,17 @@ export class Function extends Resource(
|
|
|
88
88
|
),
|
|
89
89
|
);
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
...props,
|
|
94
|
-
id: props.functionName,
|
|
95
|
-
arn: `arn:aws:lambda:${region}:${process.env.AWS_ACCOUNT_ID}:function:${props.functionName}`,
|
|
96
|
-
lastModified: new Date().toISOString(),
|
|
97
|
-
version: "$LATEST",
|
|
98
|
-
qualifiedArn: `arn:aws:lambda:${region}:${process.env.AWS_ACCOUNT_ID}:function:${props.functionName}:$LATEST`,
|
|
99
|
-
invokeArn: `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${region}:${process.env.AWS_ACCOUNT_ID}:function:${props.functionName}/invocations`,
|
|
100
|
-
sourceCodeHash: "",
|
|
101
|
-
sourceCodeSize: 0,
|
|
102
|
-
architectures: props.architecture
|
|
103
|
-
? [props.architecture]
|
|
104
|
-
: [Architecture.x86_64],
|
|
105
|
-
revisionId: "",
|
|
106
|
-
state: "Inactive",
|
|
107
|
-
packageType: "Zip",
|
|
108
|
-
};
|
|
91
|
+
return this.destroy();
|
|
109
92
|
} else {
|
|
110
93
|
try {
|
|
111
94
|
// Check if function exists
|
|
112
|
-
|
|
95
|
+
await client.send(
|
|
113
96
|
new GetFunctionCommand({
|
|
114
97
|
FunctionName: props.functionName,
|
|
115
98
|
}),
|
|
116
99
|
);
|
|
117
100
|
|
|
118
|
-
if (
|
|
101
|
+
if (this.phase === "update") {
|
|
119
102
|
// Wait for function to stabilize
|
|
120
103
|
await waitForFunctionStabilization(client, props.functionName);
|
|
121
104
|
|
|
@@ -228,9 +211,8 @@ export class Function extends Resource(
|
|
|
228
211
|
),
|
|
229
212
|
]);
|
|
230
213
|
|
|
231
|
-
|
|
214
|
+
return this({
|
|
232
215
|
...props,
|
|
233
|
-
id: props.functionName,
|
|
234
216
|
arn: config.FunctionArn!,
|
|
235
217
|
lastModified: config.LastModified!,
|
|
236
218
|
version: config.Version!,
|
|
@@ -251,12 +233,10 @@ export class Function extends Resource(
|
|
|
251
233
|
packageType: config.PackageType!,
|
|
252
234
|
signingProfileVersionArn: config.SigningProfileVersionArn,
|
|
253
235
|
signingJobArn: config.SigningJobArn,
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
return output;
|
|
236
|
+
});
|
|
257
237
|
}
|
|
258
238
|
},
|
|
259
|
-
)
|
|
239
|
+
);
|
|
260
240
|
|
|
261
241
|
// Helper to wait for function to stabilize
|
|
262
242
|
async function waitForFunctionStabilization(
|
package/src/aws/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { Input } from "../../input";
|
|
2
|
-
import { isOutput } from "../../output";
|
|
3
1
|
import { OIDCProvider, type OIDCProviderProps } from "./oidc-provider";
|
|
4
2
|
|
|
5
3
|
/**
|
|
@@ -13,22 +11,19 @@ export interface GitHubOIDCProviderProps
|
|
|
13
11
|
extends Omit<OIDCProviderProps, "thumbprint"> {
|
|
14
12
|
owner: string;
|
|
15
13
|
repository: string;
|
|
16
|
-
thumbprint?: string;
|
|
17
14
|
}
|
|
15
|
+
export type GitHubOIDCProvider = ReturnType<typeof GitHubOIDCProvider>;
|
|
18
16
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
17
|
+
export const GitHubOIDCProvider = async (
|
|
18
|
+
id: string,
|
|
19
|
+
props: GitHubOIDCProviderProps,
|
|
20
|
+
) => {
|
|
21
|
+
return OIDCProvider(id, {
|
|
22
|
+
owner: props.owner,
|
|
23
|
+
repository: props.repository,
|
|
24
|
+
roleArn: props.roleArn,
|
|
25
|
+
branches: props.branches,
|
|
26
|
+
environments: props.environments,
|
|
27
|
+
thumbprint: DEFAULT_GITHUB_THUMBPRINT,
|
|
28
|
+
});
|
|
29
|
+
};
|