alchemy 0.12.20 → 0.13.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/ai/client.d.ts.map +1 -1
- package/lib/ai/client.js +3 -5
- package/lib/ai/client.js.map +1 -1
- package/lib/alchemy.d.ts +2 -1
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +87 -92
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +6 -0
- package/lib/apply.js.map +1 -1
- package/lib/aws/bucket.d.ts.map +1 -1
- package/lib/aws/bucket.js +55 -57
- package/lib/aws/bucket.js.map +1 -1
- package/lib/aws/function.d.ts.map +1 -1
- package/lib/aws/function.js +250 -252
- package/lib/aws/function.js.map +1 -1
- package/lib/aws/policy-attachment.d.ts.map +1 -1
- package/lib/aws/policy-attachment.js +4 -6
- package/lib/aws/policy-attachment.js.map +1 -1
- package/lib/aws/policy.d.ts.map +1 -1
- package/lib/aws/policy.js +63 -65
- package/lib/aws/policy.js.map +1 -1
- package/lib/aws/queue.js +88 -90
- package/lib/aws/queue.js.map +1 -1
- package/lib/aws/table.d.ts.map +1 -1
- package/lib/aws/table.js +95 -99
- package/lib/aws/table.js.map +1 -1
- package/lib/cloudflare/account-api-token.d.ts +1 -1
- package/lib/cloudflare/account-api-token.d.ts.map +1 -1
- package/lib/cloudflare/account-api-token.js +0 -1
- package/lib/cloudflare/account-api-token.js.map +1 -1
- package/lib/cloudflare/ai-gateway.js.map +1 -1
- package/lib/cloudflare/ai.d.ts +1 -1
- package/lib/cloudflare/ai.d.ts.map +1 -1
- package/lib/cloudflare/ai.js.map +1 -1
- package/lib/cloudflare/api.js +1 -5
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/bucket.d.ts +2 -2
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +22 -24
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.d.ts +0 -3
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.d.ts.map +1 -1
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.js +4 -7
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.js.map +1 -1
- package/lib/cloudflare/custom-domain.d.ts.map +1 -1
- package/lib/cloudflare/custom-domain.js +3 -5
- package/lib/cloudflare/custom-domain.js.map +1 -1
- package/lib/cloudflare/d1-database.d.ts +1 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +70 -72
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/d1-migrations.d.ts +1 -1
- package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
- package/lib/cloudflare/d1-migrations.js +2 -3
- package/lib/cloudflare/d1-migrations.js.map +1 -1
- package/lib/cloudflare/dns-records.d.ts.map +1 -1
- package/lib/cloudflare/dns-records.js +2 -4
- package/lib/cloudflare/dns-records.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +1 -1
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js +24 -27
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/pipeline.d.ts +1 -1
- package/lib/cloudflare/pipeline.d.ts.map +1 -1
- package/lib/cloudflare/pipeline.js +32 -34
- package/lib/cloudflare/pipeline.js.map +1 -1
- package/lib/cloudflare/queue-consumer.d.ts +1 -1
- package/lib/cloudflare/queue-consumer.d.ts.map +1 -1
- package/lib/cloudflare/queue-consumer.js +31 -33
- package/lib/cloudflare/queue-consumer.js.map +1 -1
- package/lib/cloudflare/queue.d.ts +1 -1
- package/lib/cloudflare/queue.d.ts.map +1 -1
- package/lib/cloudflare/queue.js +36 -38
- package/lib/cloudflare/queue.js.map +1 -1
- package/lib/cloudflare/r2-rest-state-store.js +4 -8
- package/lib/cloudflare/r2-rest-state-store.js.map +1 -1
- package/lib/cloudflare/types.d.ts.map +1 -1
- package/lib/cloudflare/types.js +1 -3
- package/lib/cloudflare/types.js.map +1 -1
- package/lib/cloudflare/vectorize-index.d.ts.map +1 -1
- package/lib/cloudflare/vectorize-index.js +37 -39
- package/lib/cloudflare/vectorize-index.js.map +1 -1
- package/lib/cloudflare/vectorize-metadata-index.d.ts +1 -1
- package/lib/cloudflare/vectorize-metadata-index.d.ts.map +1 -1
- package/lib/cloudflare/vectorize-metadata-index.js +10 -13
- package/lib/cloudflare/vectorize-metadata-index.js.map +1 -1
- package/lib/cloudflare/worker.js +6 -12
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/workflow.d.ts +1 -1
- package/lib/cloudflare/workflow.d.ts.map +1 -1
- package/lib/cloudflare/workflow.js.map +1 -1
- package/lib/cloudflare/zone.d.ts.map +1 -1
- package/lib/cloudflare/zone.js +48 -50
- package/lib/cloudflare/zone.js.map +1 -1
- package/lib/dns/godaddy.js +1 -1
- package/lib/dns/import-dns.js +1 -1
- package/lib/dns/import-dns.js.map +1 -1
- package/lib/env.js +7 -9
- package/lib/env.js.map +1 -1
- package/lib/esbuild/bundle.js +8 -10
- package/lib/esbuild/bundle.js.map +1 -1
- package/lib/fs/copy-file.d.ts.map +1 -1
- package/lib/fs/copy-file.js +27 -29
- package/lib/fs/copy-file.js.map +1 -1
- package/lib/fs/file.d.ts +1 -1
- package/lib/fs/file.d.ts.map +1 -1
- package/lib/fs/file.js +1 -1
- package/lib/fs/file.js.map +1 -1
- package/lib/fs/folder.d.ts.map +1 -1
- package/lib/fs/folder.js +1 -3
- package/lib/fs/folder.js.map +1 -1
- package/lib/github/client.d.ts.map +1 -1
- package/lib/github/client.js +3 -5
- package/lib/github/client.js.map +1 -1
- package/lib/github/repository-environment.d.ts.map +1 -1
- package/lib/github/repository-environment.js +173 -176
- package/lib/github/repository-environment.js.map +1 -1
- package/lib/github/secret.d.ts.map +1 -1
- package/lib/github/secret.js +97 -99
- package/lib/github/secret.js.map +1 -1
- package/lib/internal/docs/providers.d.ts.map +1 -1
- package/lib/internal/docs/providers.js +5 -7
- package/lib/internal/docs/providers.js.map +1 -1
- package/lib/neon/api.js +1 -2
- package/lib/neon/api.js.map +1 -1
- package/lib/neon/project.js +2 -2
- package/lib/neon/project.js.map +1 -1
- package/lib/os/exec.d.ts +5 -0
- package/lib/os/exec.d.ts.map +1 -1
- package/lib/os/exec.js +88 -56
- package/lib/os/exec.js.map +1 -1
- package/lib/scope.d.ts +3 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +10 -3
- package/lib/scope.js.map +1 -1
- package/lib/secret.d.ts.map +1 -1
- package/lib/secret.js +1 -3
- package/lib/secret.js.map +1 -1
- package/lib/serde.d.ts.map +1 -1
- package/lib/serde.js +13 -15
- package/lib/serde.js.map +1 -1
- package/lib/stripe/price.d.ts.map +1 -1
- package/lib/stripe/price.js +69 -73
- package/lib/stripe/price.js.map +1 -1
- package/lib/stripe/product.d.ts.map +1 -1
- package/lib/stripe/product.js +50 -52
- package/lib/stripe/product.js.map +1 -1
- package/lib/stripe/webhook.d.ts.map +1 -1
- package/lib/stripe/webhook.js +51 -53
- package/lib/stripe/webhook.js.map +1 -1
- package/lib/test/bun.d.ts +1 -1
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/bun.js +1 -0
- package/lib/test/bun.js.map +1 -1
- package/lib/test/prune.d.ts.map +1 -1
- package/lib/test/prune.js.map +1 -1
- package/lib/util/dedent.js +4 -4
- package/lib/util/dedent.js.map +1 -1
- package/lib/web/astro.js +1 -1
- package/lib/web/shadcn.js +4 -4
- package/lib/web/shadcn.js.map +1 -1
- package/lib/web/tailwind.d.ts.map +1 -1
- package/lib/web/tailwind.js +0 -1
- package/lib/web/tailwind.js.map +1 -1
- package/lib/web/vite.js +3 -3
- package/lib/web/vitepress/home-page.d.ts.map +1 -1
- package/lib/web/vitepress/home-page.js +6 -8
- package/lib/web/vitepress/home-page.js.map +1 -1
- package/lib/web/vitepress/process-front-matter-files.js +1 -1
- package/lib/web/vitepress/process-front-matter-files.js.map +1 -1
- package/lib/web/vitepress/vitepress.js +2 -2
- package/lib/web/vitepress/vitepress.js.map +1 -1
- package/package.json +1 -1
- package/src/ai/astro-file.ts +4 -4
- package/src/ai/client.ts +4 -5
- package/src/ai/css-file.ts +4 -4
- package/src/ai/document.ts +3 -3
- package/src/ai/html-file.ts +4 -4
- package/src/ai/json-file.ts +1 -1
- package/src/ai/typescript-file.ts +4 -4
- package/src/ai/vue-file.ts +4 -4
- package/src/ai/yaml-file.ts +1 -1
- package/src/alchemy.ts +111 -104
- package/src/apply.ts +13 -5
- package/src/aws/bucket.ts +72 -75
- package/src/aws/function.ts +296 -297
- package/src/aws/oidc/github-oidc-provider.ts +1 -1
- package/src/aws/oidc/oidc-provider.ts +15 -15
- package/src/aws/policy-attachment.ts +10 -11
- package/src/aws/policy.ts +91 -94
- package/src/aws/queue.ts +111 -111
- package/src/aws/role.ts +24 -24
- package/src/aws/ses.ts +17 -17
- package/src/aws/table.ts +114 -115
- package/src/cloudflare/account-api-token.ts +11 -11
- package/src/cloudflare/account-id.ts +1 -1
- package/src/cloudflare/ai-gateway.ts +4 -4
- package/src/cloudflare/ai.ts +1 -1
- package/src/cloudflare/api-error.ts +1 -1
- package/src/cloudflare/api.ts +7 -11
- package/src/cloudflare/assets.ts +4 -4
- package/src/cloudflare/auth.ts +13 -13
- package/src/cloudflare/bucket.ts +62 -63
- package/src/cloudflare/bundle/hybrid-nodejs-compat.ts +14 -17
- package/src/cloudflare/custom-domain.ts +27 -28
- package/src/cloudflare/d1-database.ts +101 -102
- package/src/cloudflare/d1-migrations.ts +10 -10
- package/src/cloudflare/dns-records.ts +23 -24
- package/src/cloudflare/durable-object-namespace.ts +1 -1
- package/src/cloudflare/event-source.ts +1 -1
- package/src/cloudflare/hyperdrive.ts +3 -3
- package/src/cloudflare/kv-namespace.ts +36 -37
- package/src/cloudflare/nuxt.ts +1 -1
- package/src/cloudflare/permission-groups.ts +7 -7
- package/src/cloudflare/pipeline.ts +55 -56
- package/src/cloudflare/queue-consumer.ts +53 -54
- package/src/cloudflare/queue.ts +56 -57
- package/src/cloudflare/r2-rest-state-store.ts +11 -11
- package/src/cloudflare/redwood.ts +1 -1
- package/src/cloudflare/tanstack-start.ts +1 -1
- package/src/cloudflare/types.ts +1 -2
- package/src/cloudflare/vectorize-index.ts +60 -61
- package/src/cloudflare/vectorize-metadata-index.ts +25 -25
- package/src/cloudflare/vite.ts +1 -1
- package/src/cloudflare/website.ts +3 -3
- package/src/cloudflare/worker.ts +61 -61
- package/src/cloudflare/workflow.ts +4 -4
- package/src/cloudflare/wrangler.json.ts +4 -4
- package/src/cloudflare/zone.ts +69 -70
- package/src/destroy.ts +4 -4
- package/src/dns/godaddy.ts +1 -1
- package/src/dns/import-dns.ts +5 -5
- package/src/encrypt.ts +6 -6
- package/src/env.ts +10 -10
- package/src/esbuild/bundle.ts +14 -14
- package/src/fs/copy-file.ts +35 -36
- package/src/fs/file-system-state-store.ts +4 -4
- package/src/fs/file.ts +9 -8
- package/src/fs/folder.ts +6 -7
- package/src/github/client.ts +15 -14
- package/src/github/repository-environment.ts +206 -210
- package/src/github/secret.ts +111 -116
- package/src/internal/docs/providers.ts +16 -17
- package/src/neon/api-error.ts +1 -1
- package/src/neon/api.ts +6 -6
- package/src/neon/project.ts +18 -17
- package/src/os/exec.ts +107 -66
- package/src/resource.ts +3 -3
- package/src/scope.ts +14 -4
- package/src/secret.ts +2 -3
- package/src/serde.ts +30 -22
- package/src/stripe/price.ts +74 -77
- package/src/stripe/product.ts +55 -56
- package/src/stripe/webhook.ts +56 -57
- package/src/test/bun.ts +7 -6
- package/src/test/prune.ts +4 -4
- package/src/util/dedent.ts +4 -4
- package/src/web/astro.ts +10 -10
- package/src/web/shadcn-component.ts +4 -4
- package/src/web/shadcn.ts +10 -10
- package/src/web/tailwind.ts +2 -3
- package/src/web/vite.ts +11 -11
- package/src/web/vitepress/config.ts +2 -2
- package/src/web/vitepress/custom-theme.ts +7 -7
- package/src/web/vitepress/dependencies.ts +4 -4
- package/src/web/vitepress/home-page.ts +8 -9
- package/src/web/vitepress/process-front-matter-files.ts +5 -5
- package/src/web/vitepress/vitepress.ts +7 -7
package/src/os/exec.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { spawn, type SpawnOptions } from "node:child_process";
|
|
2
2
|
import type { Context } from "../context.js";
|
|
3
3
|
import { Resource } from "../resource.js";
|
|
4
4
|
|
|
@@ -117,87 +117,128 @@ export const Exec = Resource(
|
|
|
117
117
|
async function (
|
|
118
118
|
this: Context<Exec>,
|
|
119
119
|
id: string,
|
|
120
|
-
props: ExecProps
|
|
120
|
+
props: ExecProps,
|
|
121
121
|
): Promise<Exec> {
|
|
122
122
|
if (this.phase === "delete") {
|
|
123
123
|
// Nothing to actually delete for an exec command
|
|
124
124
|
return this.destroy();
|
|
125
|
-
}
|
|
125
|
+
}
|
|
126
|
+
if (
|
|
126
127
|
this.phase === "update" &&
|
|
127
128
|
props.memoize &&
|
|
128
129
|
this.output?.command === props.command
|
|
129
130
|
) {
|
|
130
131
|
// If memoize is enabled and the command hasn't changed, return the existing output
|
|
131
132
|
return this.output;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
133
|
+
}
|
|
134
|
+
// Default values
|
|
135
|
+
let stdout = "";
|
|
136
|
+
let stderr = "";
|
|
137
|
+
let exitCode = 0;
|
|
138
|
+
|
|
139
|
+
// Default to inheriting stdio unless explicitly set to false
|
|
140
|
+
const inheritStdio = props.inheritStdio !== false;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// Parse the command into command and args
|
|
144
|
+
const [cmd, ...args] = props.command.split(/\s+/);
|
|
145
|
+
|
|
146
|
+
// Use spawn for better stdio control
|
|
147
|
+
const childProcess = spawn(cmd, args, {
|
|
148
|
+
cwd: props.cwd || process.cwd(),
|
|
149
|
+
env: { ...process.env, ...props.env },
|
|
150
|
+
shell: true, // Use shell to handle complex commands
|
|
151
|
+
stdio: inheritStdio ? "inherit" : "pipe", // Inherit stdio when requested
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (!inheritStdio) {
|
|
155
|
+
// If not inheriting stdio, collect output manually
|
|
156
|
+
childProcess.stdout?.on("data", (data) => {
|
|
157
|
+
stdout += data.toString();
|
|
151
158
|
});
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
childProcess.stdout?.on("data", (data) => {
|
|
156
|
-
stdout += data.toString();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
childProcess.stderr?.on("data", (data) => {
|
|
160
|
-
stderr += data.toString();
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Wait for the process to complete
|
|
165
|
-
exitCode = await new Promise<number>((resolve, reject) => {
|
|
166
|
-
childProcess.on("close", (code) => {
|
|
167
|
-
resolve(code || 0);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
childProcess.on("error", (err) => {
|
|
171
|
-
stderr += err.toString();
|
|
172
|
-
resolve(1);
|
|
173
|
-
});
|
|
160
|
+
childProcess.stderr?.on("data", (data) => {
|
|
161
|
+
stderr += data.toString();
|
|
174
162
|
});
|
|
175
|
-
} catch (error: any) {
|
|
176
|
-
// If not throwing, capture the error information
|
|
177
|
-
exitCode = 1;
|
|
178
|
-
stderr += String(error);
|
|
179
163
|
}
|
|
180
164
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
165
|
+
// Wait for the process to complete
|
|
166
|
+
exitCode = await new Promise<number>((resolve, reject) => {
|
|
167
|
+
childProcess.on("close", (code) => {
|
|
168
|
+
resolve(code || 0);
|
|
169
|
+
});
|
|
186
170
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
cwd: props.cwd,
|
|
192
|
-
env: props.env,
|
|
193
|
-
memoize: props.memoize,
|
|
194
|
-
inheritStdio,
|
|
195
|
-
exitCode,
|
|
196
|
-
stdout,
|
|
197
|
-
stderr,
|
|
198
|
-
executedAt: Date.now(),
|
|
199
|
-
completed: true,
|
|
171
|
+
childProcess.on("error", (err) => {
|
|
172
|
+
stderr += err.toString();
|
|
173
|
+
resolve(1);
|
|
174
|
+
});
|
|
200
175
|
});
|
|
176
|
+
} catch (error: any) {
|
|
177
|
+
// If not throwing, capture the error information
|
|
178
|
+
exitCode = 1;
|
|
179
|
+
stderr += String(error);
|
|
201
180
|
}
|
|
202
|
-
|
|
181
|
+
|
|
182
|
+
if (exitCode !== 0) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Command failed with exit code ${exitCode}: ${props.command}\n${stderr}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Return the execution result
|
|
189
|
+
return this({
|
|
190
|
+
id,
|
|
191
|
+
command: props.command,
|
|
192
|
+
cwd: props.cwd,
|
|
193
|
+
env: props.env,
|
|
194
|
+
memoize: props.memoize,
|
|
195
|
+
inheritStdio,
|
|
196
|
+
exitCode,
|
|
197
|
+
stdout,
|
|
198
|
+
stderr,
|
|
199
|
+
executedAt: Date.now(),
|
|
200
|
+
completed: true,
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
203
|
);
|
|
204
|
+
|
|
205
|
+
const defaultOptions: SpawnOptions = {
|
|
206
|
+
stdio: "inherit",
|
|
207
|
+
env: {
|
|
208
|
+
...process.env,
|
|
209
|
+
},
|
|
210
|
+
shell: true,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Execute a shell command.
|
|
215
|
+
*/
|
|
216
|
+
export async function exec(
|
|
217
|
+
command: string,
|
|
218
|
+
options?: Partial<SpawnOptions>,
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
const [cmd, ...args] = command.split(/\s+/);
|
|
221
|
+
|
|
222
|
+
return new Promise((resolve, reject) => {
|
|
223
|
+
const child = spawn(cmd, args, {
|
|
224
|
+
...defaultOptions,
|
|
225
|
+
...options,
|
|
226
|
+
env: {
|
|
227
|
+
...defaultOptions.env,
|
|
228
|
+
...options?.env,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
child.on("close", (code) => {
|
|
233
|
+
if (code === 0) {
|
|
234
|
+
resolve();
|
|
235
|
+
} else {
|
|
236
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
child.on("error", (err) => {
|
|
241
|
+
reject(err);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
}
|
package/src/resource.ts
CHANGED
|
@@ -69,7 +69,7 @@ type IsClass = {
|
|
|
69
69
|
type ResourceLifecycleHandler = (
|
|
70
70
|
this: Context<any, any>,
|
|
71
71
|
id: string,
|
|
72
|
-
props: any
|
|
72
|
+
props: any,
|
|
73
73
|
) => Promise<Resource<string>>;
|
|
74
74
|
|
|
75
75
|
// see: https://x.com/samgoodwin89/status/1904640134097887653
|
|
@@ -100,7 +100,7 @@ export function Resource<
|
|
|
100
100
|
|
|
101
101
|
const provider = ((
|
|
102
102
|
resourceID: string,
|
|
103
|
-
props: ResourceProps
|
|
103
|
+
props: ResourceProps,
|
|
104
104
|
): Promise<Resource<string>> => {
|
|
105
105
|
const scope = _Scope.current;
|
|
106
106
|
|
|
@@ -116,7 +116,7 @@ export function Resource<
|
|
|
116
116
|
if (otherResource?.Kind !== type) {
|
|
117
117
|
scope.fail();
|
|
118
118
|
throw new Error(
|
|
119
|
-
`Resource ${resourceID} already exists in the stack and is of a different type: '${otherResource?.Kind}' !== '${type}'
|
|
119
|
+
`Resource ${resourceID} already exists in the stack and is of a different type: '${otherResource?.Kind}' !== '${type}'`,
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
// console.warn(
|
package/src/scope.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { Phase } from "./alchemy.js";
|
|
2
3
|
import { destroy } from "./destroy.js";
|
|
3
4
|
import { FileSystemStateStore } from "./fs/file-system-state-store.js";
|
|
4
5
|
import type { PendingResource, ResourceID } from "./resource.js";
|
|
@@ -14,6 +15,7 @@ export type ScopeOptions = {
|
|
|
14
15
|
password?: string;
|
|
15
16
|
stateStore?: StateStoreType;
|
|
16
17
|
quiet?: boolean;
|
|
18
|
+
phase?: Phase;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
// TODO: support browser
|
|
@@ -40,6 +42,7 @@ export class Scope {
|
|
|
40
42
|
public readonly password: string | undefined;
|
|
41
43
|
public readonly state: StateStore;
|
|
42
44
|
public readonly quiet: boolean;
|
|
45
|
+
public readonly phase: Phase;
|
|
43
46
|
|
|
44
47
|
private isErrored = false;
|
|
45
48
|
|
|
@@ -56,6 +59,11 @@ export class Scope {
|
|
|
56
59
|
this.state = options.stateStore
|
|
57
60
|
? options.stateStore(this)
|
|
58
61
|
: new FileSystemStateStore(this);
|
|
62
|
+
const phase = options.phase ?? this.parent?.phase;
|
|
63
|
+
if (phase === undefined) {
|
|
64
|
+
throw new Error("Phase is required");
|
|
65
|
+
}
|
|
66
|
+
this.phase = phase;
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
public async delete(resourceID: ResourceID) {
|
|
@@ -74,9 +82,8 @@ export class Scope {
|
|
|
74
82
|
const app = this.appName ? [this.appName] : [];
|
|
75
83
|
if (this.parent) {
|
|
76
84
|
return [...this.parent.chain, ...thisScope];
|
|
77
|
-
} else {
|
|
78
|
-
return [...app, this.stage, ...thisScope];
|
|
79
85
|
}
|
|
86
|
+
return [...app, this.stage, ...thisScope];
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
public fail() {
|
|
@@ -110,15 +117,18 @@ export class Scope {
|
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
public async finalize() {
|
|
120
|
+
if (this.phase === "read") {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
113
123
|
if (!this.isErrored) {
|
|
114
124
|
// TODO: need to detect if it is in error
|
|
115
125
|
const resourceIds = await this.state.list();
|
|
116
126
|
const aliveIds = new Set(this.resources.keys());
|
|
117
127
|
const orphanIds = Array.from(
|
|
118
|
-
resourceIds.filter((id) => !aliveIds.has(id))
|
|
128
|
+
resourceIds.filter((id) => !aliveIds.has(id)),
|
|
119
129
|
);
|
|
120
130
|
const orphans = await Promise.all(
|
|
121
|
-
orphanIds.map(async (id) => (await this.state.get(id))!.output)
|
|
131
|
+
orphanIds.map(async (id) => (await this.state.get(id))!.output),
|
|
122
132
|
);
|
|
123
133
|
await destroy.all(orphans, {
|
|
124
134
|
quiet: this.quiet,
|
package/src/secret.ts
CHANGED
|
@@ -100,13 +100,12 @@ export namespace secret {
|
|
|
100
100
|
async function _env(
|
|
101
101
|
name: string,
|
|
102
102
|
value?: string,
|
|
103
|
-
error?: string
|
|
103
|
+
error?: string,
|
|
104
104
|
): Promise<Secret> {
|
|
105
105
|
const result = await alchemy.env(name, value, error);
|
|
106
106
|
if (typeof result === "string") {
|
|
107
107
|
return secret(result);
|
|
108
|
-
} else {
|
|
109
|
-
throw new Error(`Secret environment variable ${name} is not a string`);
|
|
110
108
|
}
|
|
109
|
+
throw new Error(`Secret environment variable ${name} is not a string`);
|
|
111
110
|
}
|
|
112
111
|
}
|
package/src/serde.ts
CHANGED
|
@@ -18,11 +18,12 @@ export async function serialize(
|
|
|
18
18
|
value: any,
|
|
19
19
|
options?: {
|
|
20
20
|
encrypt?: boolean;
|
|
21
|
-
}
|
|
21
|
+
},
|
|
22
22
|
): Promise<any> {
|
|
23
23
|
if (Array.isArray(value)) {
|
|
24
24
|
return Promise.all(value.map((value) => serialize(scope, value, options)));
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof Secret) {
|
|
26
27
|
if (!scope.password) {
|
|
27
28
|
throw new Error("Cannot serialize secret without password");
|
|
28
29
|
}
|
|
@@ -32,26 +33,31 @@ export async function serialize(
|
|
|
32
33
|
? await encrypt(value.unencrypted, scope.password)
|
|
33
34
|
: value.unencrypted,
|
|
34
35
|
};
|
|
35
|
-
}
|
|
36
|
+
}
|
|
37
|
+
if (isType(value)) {
|
|
36
38
|
return {
|
|
37
39
|
"@schema": value.toJSON(),
|
|
38
40
|
};
|
|
39
|
-
}
|
|
41
|
+
}
|
|
42
|
+
if (value instanceof Date) {
|
|
40
43
|
return {
|
|
41
44
|
"@date": value.toISOString(),
|
|
42
45
|
};
|
|
43
|
-
}
|
|
46
|
+
}
|
|
47
|
+
if (value instanceof Scope) {
|
|
44
48
|
return undefined;
|
|
45
|
-
}
|
|
49
|
+
}
|
|
50
|
+
if (value && typeof value === "object") {
|
|
46
51
|
return Object.fromEntries(
|
|
47
52
|
await Promise.all(
|
|
48
53
|
Object.entries(value).map(async ([key, value]) => [
|
|
49
54
|
key,
|
|
50
55
|
await serialize(scope, value, options),
|
|
51
|
-
])
|
|
52
|
-
)
|
|
56
|
+
]),
|
|
57
|
+
),
|
|
53
58
|
);
|
|
54
|
-
}
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === "function") {
|
|
55
61
|
// can't serialize functions
|
|
56
62
|
return undefined;
|
|
57
63
|
}
|
|
@@ -61,28 +67,30 @@ export async function serialize(
|
|
|
61
67
|
export async function deserialize(scope: Scope, value: any): Promise<any> {
|
|
62
68
|
if (Array.isArray(value)) {
|
|
63
69
|
return await Promise.all(
|
|
64
|
-
value.map(async (item) => await deserialize(scope, item))
|
|
70
|
+
value.map(async (item) => await deserialize(scope, item)),
|
|
65
71
|
);
|
|
66
|
-
}
|
|
72
|
+
}
|
|
73
|
+
if (value && typeof value === "object") {
|
|
67
74
|
if (typeof value["@secret"] === "string") {
|
|
68
75
|
if (!scope.password) {
|
|
69
76
|
throw new Error("Cannot deserialize secret without password");
|
|
70
77
|
}
|
|
71
78
|
return new Secret(await decryptWithKey(value["@secret"], scope.password));
|
|
72
|
-
}
|
|
79
|
+
}
|
|
80
|
+
if ("@schema" in value) {
|
|
73
81
|
return value["@schema"];
|
|
74
|
-
}
|
|
82
|
+
}
|
|
83
|
+
if ("@date" in value) {
|
|
75
84
|
return new Date(value["@date"]);
|
|
76
|
-
} else {
|
|
77
|
-
return Object.fromEntries(
|
|
78
|
-
await Promise.all(
|
|
79
|
-
Object.entries(value).map(async ([key, value]) => [
|
|
80
|
-
key,
|
|
81
|
-
await deserialize(scope, value),
|
|
82
|
-
])
|
|
83
|
-
)
|
|
84
|
-
);
|
|
85
85
|
}
|
|
86
|
+
return Object.fromEntries(
|
|
87
|
+
await Promise.all(
|
|
88
|
+
Object.entries(value).map(async ([key, value]) => [
|
|
89
|
+
key,
|
|
90
|
+
await deserialize(scope, value),
|
|
91
|
+
]),
|
|
92
|
+
),
|
|
93
|
+
);
|
|
86
94
|
}
|
|
87
95
|
return value;
|
|
88
96
|
}
|
package/src/stripe/price.ts
CHANGED
|
@@ -180,7 +180,7 @@ export const Price = Resource(
|
|
|
180
180
|
async function (
|
|
181
181
|
this: Context<Price>,
|
|
182
182
|
id: string,
|
|
183
|
-
props: PriceProps
|
|
183
|
+
props: PriceProps,
|
|
184
184
|
): Promise<Price> {
|
|
185
185
|
// Get Stripe API key from context or environment
|
|
186
186
|
const apiKey = process.env.STRIPE_API_KEY;
|
|
@@ -203,87 +203,84 @@ export const Price = Resource(
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
return this.destroy();
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (this.phase === "update" && this.output?.id) {
|
|
211
|
-
// Update existing price (limited properties can be updated)
|
|
212
|
-
price = await stripe.prices.update(this.output.id, {
|
|
213
|
-
active: props.active,
|
|
214
|
-
metadata: props.metadata,
|
|
215
|
-
nickname: props.nickname,
|
|
216
|
-
});
|
|
217
|
-
} else {
|
|
218
|
-
// Create new price
|
|
219
|
-
const createParams: Stripe.PriceCreateParams = {
|
|
220
|
-
currency: props.currency,
|
|
221
|
-
product: props.product,
|
|
222
|
-
active: props.active,
|
|
223
|
-
billing_scheme: props.billingScheme,
|
|
224
|
-
nickname: props.nickname,
|
|
225
|
-
metadata: props.metadata,
|
|
226
|
-
tax_behavior: props.taxBehavior,
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Add unit amount fields
|
|
230
|
-
if (props.unitAmount !== undefined) {
|
|
231
|
-
createParams.unit_amount = props.unitAmount;
|
|
232
|
-
} else if (props.unitAmountDecimal !== undefined) {
|
|
233
|
-
createParams.unit_amount_decimal = props.unitAmountDecimal;
|
|
234
|
-
}
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
let price: Stripe.Price;
|
|
235
209
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
210
|
+
if (this.phase === "update" && this.output?.id) {
|
|
211
|
+
// Update existing price (limited properties can be updated)
|
|
212
|
+
price = await stripe.prices.update(this.output.id, {
|
|
213
|
+
active: props.active,
|
|
214
|
+
metadata: props.metadata,
|
|
215
|
+
nickname: props.nickname,
|
|
216
|
+
});
|
|
217
|
+
} else {
|
|
218
|
+
// Create new price
|
|
219
|
+
const createParams: Stripe.PriceCreateParams = {
|
|
220
|
+
currency: props.currency,
|
|
221
|
+
product: props.product,
|
|
222
|
+
active: props.active,
|
|
223
|
+
billing_scheme: props.billingScheme,
|
|
224
|
+
nickname: props.nickname,
|
|
225
|
+
metadata: props.metadata,
|
|
226
|
+
tax_behavior: props.taxBehavior,
|
|
227
|
+
};
|
|
245
228
|
|
|
246
|
-
|
|
229
|
+
// Add unit amount fields
|
|
230
|
+
if (props.unitAmount !== undefined) {
|
|
231
|
+
createParams.unit_amount = props.unitAmount;
|
|
232
|
+
} else if (props.unitAmountDecimal !== undefined) {
|
|
233
|
+
createParams.unit_amount_decimal = props.unitAmountDecimal;
|
|
247
234
|
}
|
|
248
235
|
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
.aggregate_usage as Stripe.PriceCreateParams.Recurring.AggregateUsage,
|
|
259
|
-
}
|
|
260
|
-
: undefined;
|
|
236
|
+
// Add recurring parameters if present
|
|
237
|
+
if (props.recurring) {
|
|
238
|
+
createParams.recurring = {
|
|
239
|
+
interval: props.recurring.interval,
|
|
240
|
+
interval_count: props.recurring.intervalCount,
|
|
241
|
+
usage_type: props.recurring.usageType,
|
|
242
|
+
aggregate_usage: props.recurring.aggregateUsage,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
261
245
|
|
|
262
|
-
|
|
263
|
-
return this({
|
|
264
|
-
id: price.id,
|
|
265
|
-
product:
|
|
266
|
-
typeof price.product === "string"
|
|
267
|
-
? price.product
|
|
268
|
-
: price.product.id,
|
|
269
|
-
currency: price.currency,
|
|
270
|
-
unitAmount: price.unit_amount || undefined,
|
|
271
|
-
unitAmountDecimal: price.unit_amount_decimal || undefined,
|
|
272
|
-
active: price.active,
|
|
273
|
-
billingScheme: price.billing_scheme as BillingScheme,
|
|
274
|
-
nickname: price.nickname || undefined,
|
|
275
|
-
recurring: recurring,
|
|
276
|
-
metadata: price.metadata || undefined,
|
|
277
|
-
taxBehavior: price.tax_behavior as TaxBehavior,
|
|
278
|
-
createdAt: price.created,
|
|
279
|
-
livemode: price.livemode,
|
|
280
|
-
type: price.type as Stripe.Price.Type,
|
|
281
|
-
lookupKey: price.lookup_key || undefined,
|
|
282
|
-
});
|
|
283
|
-
} catch (error) {
|
|
284
|
-
console.error("Error creating/updating price:", error);
|
|
285
|
-
throw error;
|
|
246
|
+
price = await stripe.prices.create(createParams);
|
|
286
247
|
}
|
|
248
|
+
|
|
249
|
+
// Transform Stripe recurring object to our format
|
|
250
|
+
const recurring = price.recurring
|
|
251
|
+
? {
|
|
252
|
+
interval: price.recurring
|
|
253
|
+
.interval as Stripe.PriceCreateParams.Recurring.Interval,
|
|
254
|
+
intervalCount: price.recurring.interval_count,
|
|
255
|
+
usageType: price.recurring
|
|
256
|
+
.usage_type as Stripe.PriceCreateParams.Recurring.UsageType,
|
|
257
|
+
aggregateUsage: price.recurring
|
|
258
|
+
.aggregate_usage as Stripe.PriceCreateParams.Recurring.AggregateUsage,
|
|
259
|
+
}
|
|
260
|
+
: undefined;
|
|
261
|
+
|
|
262
|
+
// Map Stripe API response to our output format
|
|
263
|
+
return this({
|
|
264
|
+
id: price.id,
|
|
265
|
+
product:
|
|
266
|
+
typeof price.product === "string" ? price.product : price.product.id,
|
|
267
|
+
currency: price.currency,
|
|
268
|
+
unitAmount: price.unit_amount || undefined,
|
|
269
|
+
unitAmountDecimal: price.unit_amount_decimal || undefined,
|
|
270
|
+
active: price.active,
|
|
271
|
+
billingScheme: price.billing_scheme as BillingScheme,
|
|
272
|
+
nickname: price.nickname || undefined,
|
|
273
|
+
recurring: recurring,
|
|
274
|
+
metadata: price.metadata || undefined,
|
|
275
|
+
taxBehavior: price.tax_behavior as TaxBehavior,
|
|
276
|
+
createdAt: price.created,
|
|
277
|
+
livemode: price.livemode,
|
|
278
|
+
type: price.type as Stripe.Price.Type,
|
|
279
|
+
lookupKey: price.lookup_key || undefined,
|
|
280
|
+
});
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error("Error creating/updating price:", error);
|
|
283
|
+
throw error;
|
|
287
284
|
}
|
|
288
|
-
}
|
|
285
|
+
},
|
|
289
286
|
);
|