alchemy 0.43.0 → 0.43.2
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 +1 -1
- package/bin/alchemy.mjs +508 -316
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +8 -2
- package/lib/alchemy.js.map +1 -1
- package/lib/build-date.d.ts +1 -1
- package/lib/build-date.js +1 -1
- package/lib/cloudflare/astro.d.ts +1 -1
- package/lib/cloudflare/astro.d.ts.map +1 -1
- package/lib/cloudflare/astro.js +1 -1
- package/lib/cloudflare/astro.js.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker-dev.d.ts +1 -0
- package/lib/cloudflare/bundle/bundle-worker-dev.d.ts.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker-dev.js +2 -3
- package/lib/cloudflare/bundle/bundle-worker-dev.js.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker.d.ts +1 -0
- package/lib/cloudflare/bundle/bundle-worker.d.ts.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker.js +2 -3
- package/lib/cloudflare/bundle/bundle-worker.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +10 -22
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker/http-server.d.ts.map +1 -1
- package/lib/cloudflare/worker/http-server.js +2 -1
- package/lib/cloudflare/worker/http-server.js.map +1 -1
- package/lib/cloudflare/worker/miniflare.d.ts +1 -0
- package/lib/cloudflare/worker/miniflare.d.ts.map +1 -1
- package/lib/cloudflare/worker/miniflare.js +2 -1
- package/lib/cloudflare/worker/miniflare.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +11 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +30 -15
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +1 -1
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +28 -10
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/internal/docs/providers.js +2 -0
- package/lib/internal/docs/providers.js.map +1 -1
- package/lib/os/exec.d.ts +15 -1
- package/lib/os/exec.d.ts.map +1 -1
- package/lib/os/exec.js +30 -3
- package/lib/os/exec.js.map +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +4 -1
- package/lib/scope.js.map +1 -1
- package/lib/util/deferred-promise.d.ts.map +1 -1
- package/lib/util/deferred-promise.js +2 -1
- package/lib/util/deferred-promise.js.map +1 -1
- package/lib/util/promise-with-resolvers.d.ts +14 -0
- package/lib/util/promise-with-resolvers.d.ts.map +1 -0
- package/lib/util/promise-with-resolvers.js +19 -0
- package/lib/util/promise-with-resolvers.js.map +1 -0
- package/package.json +4 -3
- package/src/alchemy.ts +8 -2
- package/src/build-date.ts +1 -1
- package/src/cloudflare/astro.ts +1 -1
- package/src/cloudflare/bundle/bundle-worker-dev.ts +3 -4
- package/src/cloudflare/bundle/bundle-worker.ts +3 -4
- package/src/cloudflare/website.ts +13 -30
- package/src/cloudflare/worker/http-server.ts +2 -1
- package/src/cloudflare/worker/miniflare.ts +5 -1
- package/src/cloudflare/worker.ts +56 -27
- package/src/cloudflare/wrangler.json.ts +40 -11
- package/src/internal/docs/providers.ts +2 -0
- package/src/os/exec.ts +32 -4
- package/src/scope.ts +5 -1
- package/src/util/deferred-promise.ts +3 -1
- package/src/util/promise-with-resolvers.ts +30 -0
|
@@ -27,7 +27,7 @@ export interface WranglerJsonProps {
|
|
|
27
27
|
/**
|
|
28
28
|
* Path to write the wrangler.json file to
|
|
29
29
|
*
|
|
30
|
-
* @default cwd/wrangler.json
|
|
30
|
+
* @default worker.cwd/wrangler.json
|
|
31
31
|
*/
|
|
32
32
|
path?: string;
|
|
33
33
|
|
|
@@ -89,14 +89,27 @@ export const WranglerJson = Resource(
|
|
|
89
89
|
_id: string,
|
|
90
90
|
props: WranglerJsonProps,
|
|
91
91
|
): Promise<WranglerJson> {
|
|
92
|
-
// Default path is wrangler.json in current directory
|
|
93
|
-
const filePath = props.path || "wrangler.jsonc";
|
|
94
|
-
|
|
95
92
|
if (this.phase === "delete") {
|
|
96
93
|
return this.destroy();
|
|
97
94
|
}
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
const cwd = props.worker.cwd
|
|
97
|
+
? path.resolve(props.worker.cwd)
|
|
98
|
+
: process.cwd();
|
|
99
|
+
|
|
100
|
+
const toAbsolute = <T extends string | undefined>(input: T): T => {
|
|
101
|
+
return (input ? path.resolve(cwd, input) : undefined) as T;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const main = toAbsolute(props.main ?? props.worker.entrypoint);
|
|
105
|
+
let filePath = toAbsolute(props.path ?? cwd);
|
|
106
|
+
if (!path.basename(filePath).match(".json")) {
|
|
107
|
+
filePath = path.join(filePath, props.name ?? "wrangler.jsonc");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const dirname = path.dirname(filePath);
|
|
111
|
+
|
|
112
|
+
if (!main) {
|
|
100
113
|
throw new Error(
|
|
101
114
|
"Worker must have an entrypoint to generate a wrangler.json",
|
|
102
115
|
);
|
|
@@ -107,16 +120,27 @@ export const WranglerJson = Resource(
|
|
|
107
120
|
const spec: WranglerJsonSpec = {
|
|
108
121
|
name: worker.name,
|
|
109
122
|
// Use entrypoint as main if it exists
|
|
110
|
-
main:
|
|
123
|
+
main: path.relative(dirname, main),
|
|
111
124
|
// see: https://developers.cloudflare.com/workers/configuration/compatibility-dates/
|
|
112
125
|
compatibility_date: worker.compatibilityDate,
|
|
113
126
|
compatibility_flags: props.worker.compatibilityFlags,
|
|
114
|
-
assets: props.assets
|
|
127
|
+
assets: props.assets
|
|
128
|
+
? {
|
|
129
|
+
directory: toAbsolute(props.assets.directory),
|
|
130
|
+
binding: props.assets.binding,
|
|
131
|
+
}
|
|
132
|
+
: undefined,
|
|
115
133
|
};
|
|
116
134
|
|
|
117
135
|
// Process bindings if they exist
|
|
118
136
|
if (worker.bindings) {
|
|
119
|
-
processBindings(
|
|
137
|
+
processBindings(
|
|
138
|
+
spec,
|
|
139
|
+
worker.bindings,
|
|
140
|
+
worker.eventSources,
|
|
141
|
+
worker.name,
|
|
142
|
+
cwd,
|
|
143
|
+
);
|
|
120
144
|
}
|
|
121
145
|
|
|
122
146
|
// Add environment variables as vars
|
|
@@ -128,13 +152,17 @@ export const WranglerJson = Resource(
|
|
|
128
152
|
spec.triggers = { crons: worker.crons };
|
|
129
153
|
}
|
|
130
154
|
|
|
131
|
-
|
|
155
|
+
if (spec.assets) {
|
|
156
|
+
spec.assets.directory = path.relative(dirname, spec.assets.directory);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
await fs.mkdir(dirname, { recursive: true });
|
|
132
160
|
await fs.writeFile(filePath, await formatJson(spec));
|
|
133
161
|
|
|
134
162
|
// Return the resource
|
|
135
163
|
return this({
|
|
136
164
|
...props,
|
|
137
|
-
path: filePath,
|
|
165
|
+
path: path.relative(cwd, filePath),
|
|
138
166
|
spec,
|
|
139
167
|
createdAt: Date.now(),
|
|
140
168
|
updatedAt: Date.now(),
|
|
@@ -381,6 +409,7 @@ function processBindings(
|
|
|
381
409
|
bindings: Bindings,
|
|
382
410
|
eventSources: EventSource[] | undefined,
|
|
383
411
|
workerName: string,
|
|
412
|
+
workerCwd: string,
|
|
384
413
|
): void {
|
|
385
414
|
// Arrays to collect different binding types
|
|
386
415
|
const kvNamespaces: { binding: string; id: string; preview_id: string }[] =
|
|
@@ -527,7 +556,7 @@ function processBindings(
|
|
|
527
556
|
secrets.push(bindingName);
|
|
528
557
|
} else if (binding.type === "assets") {
|
|
529
558
|
spec.assets = {
|
|
530
|
-
directory: binding.path,
|
|
559
|
+
directory: path.resolve(workerCwd, binding.path),
|
|
531
560
|
binding: bindingName,
|
|
532
561
|
};
|
|
533
562
|
} else if (binding.type === "workflow") {
|
|
@@ -199,6 +199,8 @@ async function generateProviderDocs({
|
|
|
199
199
|
See ${alchemy.file("./README.md")} to understand the overview of Alchemy.
|
|
200
200
|
See ${alchemy.file("./.cursorrules")} to better understand the structure and convention of an Alchemy Resource.
|
|
201
201
|
|
|
202
|
+
Documentation should be written in the mdx format for an Astro Starlight documentation site.
|
|
203
|
+
|
|
202
204
|
Relevant files for the ${providerName} Service:
|
|
203
205
|
${alchemy.files(files)}
|
|
204
206
|
|
package/src/os/exec.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import type { Context } from "../context.ts";
|
|
5
5
|
import { Resource } from "../resource.ts";
|
|
6
|
+
import type { Secret } from "../secret.ts";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Properties for executing a shell command
|
|
@@ -47,7 +48,7 @@ export interface ExecProps {
|
|
|
47
48
|
/**
|
|
48
49
|
* Environment variables to set
|
|
49
50
|
*/
|
|
50
|
-
env?: Record<string, string
|
|
51
|
+
env?: Record<string, string | Secret<string>>;
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
54
|
* Whether to inherit stdio from parent process
|
|
@@ -123,6 +124,19 @@ export interface Exec extends Resource<"os::Exec">, ExecProps {
|
|
|
123
124
|
* });
|
|
124
125
|
*
|
|
125
126
|
* @example
|
|
127
|
+
* // Run a command with secrets in environment variables
|
|
128
|
+
* import alchemy from "alchemy";
|
|
129
|
+
*
|
|
130
|
+
* const deploy = await Exec("deploy-app", {
|
|
131
|
+
* command: "npm run deploy",
|
|
132
|
+
* env: {
|
|
133
|
+
* NODE_ENV: "production",
|
|
134
|
+
* API_KEY: alchemy.secret(process.env.API_KEY),
|
|
135
|
+
* DATABASE_URL: alchemy.secret(process.env.DATABASE_URL)
|
|
136
|
+
* }
|
|
137
|
+
* });
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
126
140
|
* // Run a memoized command that only re-executes when the command changes
|
|
127
141
|
* const memoizedCmd = await Exec("status-check", {
|
|
128
142
|
* command: "git status",
|
|
@@ -198,10 +212,23 @@ export const Exec = Resource(
|
|
|
198
212
|
// Parse the command into command and args
|
|
199
213
|
const [cmd, ...args] = props.command.split(/\s+/);
|
|
200
214
|
|
|
215
|
+
// Process environment variables, extracting values from secrets
|
|
216
|
+
const processedEnv: Record<string, string> = {};
|
|
217
|
+
if (props.env) {
|
|
218
|
+
for (const [key, value] of Object.entries(props.env)) {
|
|
219
|
+
if (typeof value === "string") {
|
|
220
|
+
processedEnv[key] = value;
|
|
221
|
+
} else {
|
|
222
|
+
// Handle Secret objects
|
|
223
|
+
processedEnv[key] = value.unencrypted;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
201
228
|
// Use spawn for better stdio control
|
|
202
229
|
const childProcess = spawn(cmd, args, {
|
|
203
230
|
cwd: props.cwd || process.cwd(),
|
|
204
|
-
env: { ...process.env, ...
|
|
231
|
+
env: { ...process.env, ...processedEnv },
|
|
205
232
|
shell: true, // Use shell to handle complex commands
|
|
206
233
|
stdio: inheritStdio ? "inherit" : "pipe", // Inherit stdio when requested
|
|
207
234
|
});
|
|
@@ -342,13 +369,14 @@ export async function exec(
|
|
|
342
369
|
}
|
|
343
370
|
|
|
344
371
|
async function hashInputs(cwd: string, patterns: string[]) {
|
|
345
|
-
const { glob
|
|
372
|
+
const { glob } = await import("glob");
|
|
373
|
+
const { readFile } = await import("node:fs/promises");
|
|
346
374
|
|
|
347
375
|
const hashes = new Map<string, string>();
|
|
348
376
|
|
|
349
377
|
await Promise.all(
|
|
350
378
|
patterns.flatMap(async (pattern) => {
|
|
351
|
-
const files = await
|
|
379
|
+
const files = await glob(pattern, { cwd });
|
|
352
380
|
return Promise.all(
|
|
353
381
|
files.map(async (file: string) => {
|
|
354
382
|
const path = join(cwd, file);
|
package/src/scope.ts
CHANGED
|
@@ -48,7 +48,11 @@ export type PendingDeletions = Array<{
|
|
|
48
48
|
}>;
|
|
49
49
|
|
|
50
50
|
// TODO: support browser
|
|
51
|
-
const DEFAULT_STAGE =
|
|
51
|
+
const DEFAULT_STAGE =
|
|
52
|
+
process.env.ALCHEMY_STAGE ??
|
|
53
|
+
process.env.USER ??
|
|
54
|
+
process.env.USERNAME ??
|
|
55
|
+
"dev";
|
|
52
56
|
|
|
53
57
|
declare global {
|
|
54
58
|
var __ALCHEMY_STORAGE__: AsyncLocalStorage<Scope>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for the return type of promiseWithResolvers
|
|
3
|
+
*/
|
|
4
|
+
export interface PromiseWithResolvers<T> {
|
|
5
|
+
promise: Promise<T>;
|
|
6
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
7
|
+
reject: (reason?: any) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Node 20+ compatible implementation of Promise.withResolvers()
|
|
12
|
+
* Falls back to native Promise.withResolvers when available (Node 22+)
|
|
13
|
+
*/
|
|
14
|
+
export function promiseWithResolvers<T>(): PromiseWithResolvers<T> {
|
|
15
|
+
// Use native implementation if available
|
|
16
|
+
if (typeof Promise.withResolvers === "function") {
|
|
17
|
+
return Promise.withResolvers<T>();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Fallback implementation for Node 20+
|
|
21
|
+
let resolve!: (value: T | PromiseLike<T>) => void;
|
|
22
|
+
let reject!: (reason?: any) => void;
|
|
23
|
+
|
|
24
|
+
const promise = new Promise<T>((res, rej) => {
|
|
25
|
+
resolve = res;
|
|
26
|
+
reject = rej;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return { promise, resolve, reject };
|
|
30
|
+
}
|