alchemy 0.53.0 → 0.54.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.
Files changed (124) hide show
  1. package/bin/alchemy.js +196353 -0
  2. package/bin/alchemy.ts +2 -2
  3. package/bin/commands/init.ts +1190 -0
  4. package/bin/constants.ts +18 -0
  5. package/bin/services/execute-alchemy.ts +7 -2
  6. package/bin/services/package-manager.ts +1 -1
  7. package/bin/types.ts +14 -5
  8. package/lib/cloudflare/astro.d.ts.map +1 -1
  9. package/lib/cloudflare/astro.js +4 -3
  10. package/lib/cloudflare/astro.js.map +1 -1
  11. package/lib/cloudflare/bundle/externals.d.ts +3 -0
  12. package/lib/cloudflare/bundle/externals.d.ts.map +1 -0
  13. package/lib/cloudflare/bundle/externals.js +35 -0
  14. package/lib/cloudflare/bundle/externals.js.map +1 -0
  15. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts +8 -1
  16. package/lib/cloudflare/bundle/internal-worker-bundle.d.ts.map +1 -1
  17. package/lib/cloudflare/bundle/internal-worker-bundle.js +11 -4
  18. package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -1
  19. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts +4 -1
  20. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts.map +1 -1
  21. package/lib/cloudflare/bundle/plugin-hot-reload.js +27 -11
  22. package/lib/cloudflare/bundle/plugin-hot-reload.js.map +1 -1
  23. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +5 -1
  24. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  25. package/lib/cloudflare/bundle/plugin-wasm.d.ts +6 -2
  26. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -1
  27. package/lib/cloudflare/bundle/plugin-wasm.js +21 -47
  28. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
  29. package/lib/cloudflare/bundle/validate-node-compat.d.ts +2 -1
  30. package/lib/cloudflare/bundle/validate-node-compat.d.ts.map +1 -1
  31. package/lib/cloudflare/bundle/validate-node-compat.js +5 -4
  32. package/lib/cloudflare/bundle/validate-node-compat.js.map +1 -1
  33. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  34. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  35. package/lib/cloudflare/d1-database.js +1 -1
  36. package/lib/cloudflare/d1-database.js.map +1 -1
  37. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  38. package/lib/cloudflare/d1-migrations.js +8 -10
  39. package/lib/cloudflare/d1-migrations.js.map +1 -1
  40. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts +3 -3
  41. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -1
  42. package/lib/cloudflare/miniflare/miniflare-worker-options.js +23 -6
  43. package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -1
  44. package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -1
  45. package/lib/cloudflare/miniflare/miniflare.js +2 -4
  46. package/lib/cloudflare/miniflare/miniflare.js.map +1 -1
  47. package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
  48. package/lib/cloudflare/miniflare/remote-binding-proxy.js +4 -6
  49. package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
  50. package/lib/cloudflare/react-router.d.ts +4 -0
  51. package/lib/cloudflare/react-router.d.ts.map +1 -1
  52. package/lib/cloudflare/react-router.js +8 -5
  53. package/lib/cloudflare/react-router.js.map +1 -1
  54. package/lib/cloudflare/worker-bundle.d.ts +80 -0
  55. package/lib/cloudflare/worker-bundle.d.ts.map +1 -0
  56. package/lib/cloudflare/worker-bundle.js +266 -0
  57. package/lib/cloudflare/worker-bundle.js.map +1 -0
  58. package/lib/cloudflare/worker.d.ts +1 -1
  59. package/lib/cloudflare/worker.d.ts.map +1 -1
  60. package/lib/cloudflare/worker.js +81 -195
  61. package/lib/cloudflare/worker.js.map +1 -1
  62. package/lib/esbuild/bundle.d.ts +1 -0
  63. package/lib/esbuild/bundle.d.ts.map +1 -1
  64. package/lib/state/cloudflare-state-store.js +1 -5
  65. package/lib/state/cloudflare-state-store.js.map +1 -1
  66. package/lib/state/dofs-state-store/store.d.ts.map +1 -1
  67. package/lib/state/dofs-state-store/store.js +4 -4
  68. package/lib/state/dofs-state-store/store.js.map +1 -1
  69. package/package.json +10 -5
  70. package/src/cloudflare/astro.ts +4 -3
  71. package/src/cloudflare/bundle/externals.ts +36 -0
  72. package/src/cloudflare/bundle/internal-worker-bundle.ts +12 -3
  73. package/src/cloudflare/bundle/plugin-hot-reload.ts +32 -12
  74. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +6 -1
  75. package/src/cloudflare/bundle/plugin-wasm.ts +26 -54
  76. package/src/cloudflare/bundle/validate-node-compat.ts +6 -5
  77. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  78. package/src/cloudflare/d1-database.ts +1 -1
  79. package/src/cloudflare/d1-migrations.ts +19 -18
  80. package/src/cloudflare/miniflare/miniflare-worker-options.ts +27 -13
  81. package/src/cloudflare/miniflare/miniflare.ts +2 -4
  82. package/src/cloudflare/miniflare/remote-binding-proxy.ts +5 -7
  83. package/src/cloudflare/react-router.ts +14 -6
  84. package/src/cloudflare/worker-bundle.ts +364 -0
  85. package/src/cloudflare/worker.ts +104 -241
  86. package/src/state/cloudflare-state-store.ts +1 -5
  87. package/src/state/dofs-state-store/store.ts +4 -4
  88. package/templates/nuxt/package.json +1 -1
  89. package/templates/nuxt/types/env.d.ts +11 -0
  90. package/templates/react-router/workers/app.ts +2 -2
  91. package/templates/rwsdk/.cursor/rules/alchemy_cloudflare.mdc +385 -0
  92. package/templates/rwsdk/wrangler.jsonc +10 -7
  93. package/templates/typescript/tsconfig.json +1 -2
  94. package/bin/alchemy.mjs +0 -51766
  95. package/lib/cloudflare/bundle/esbuild.d.ts +0 -16
  96. package/lib/cloudflare/bundle/esbuild.d.ts.map +0 -1
  97. package/lib/cloudflare/bundle/esbuild.js +0 -159
  98. package/lib/cloudflare/bundle/esbuild.js.map +0 -1
  99. package/lib/cloudflare/bundle/fs.d.ts +0 -18
  100. package/lib/cloudflare/bundle/fs.d.ts.map +0 -1
  101. package/lib/cloudflare/bundle/fs.js +0 -78
  102. package/lib/cloudflare/bundle/fs.js.map +0 -1
  103. package/lib/cloudflare/bundle/index.d.ts +0 -3
  104. package/lib/cloudflare/bundle/index.d.ts.map +0 -1
  105. package/lib/cloudflare/bundle/index.js +0 -2
  106. package/lib/cloudflare/bundle/index.js.map +0 -1
  107. package/lib/cloudflare/bundle/inline.d.ts +0 -12
  108. package/lib/cloudflare/bundle/inline.d.ts.map +0 -1
  109. package/lib/cloudflare/bundle/inline.js +0 -32
  110. package/lib/cloudflare/bundle/inline.js.map +0 -1
  111. package/lib/cloudflare/bundle/normalize.d.ts +0 -21
  112. package/lib/cloudflare/bundle/normalize.d.ts.map +0 -1
  113. package/lib/cloudflare/bundle/normalize.js +0 -49
  114. package/lib/cloudflare/bundle/normalize.js.map +0 -1
  115. package/lib/cloudflare/bundle/shared.d.ts +0 -30
  116. package/lib/cloudflare/bundle/shared.d.ts.map +0 -1
  117. package/lib/cloudflare/bundle/shared.js +0 -34
  118. package/lib/cloudflare/bundle/shared.js.map +0 -1
  119. package/src/cloudflare/bundle/esbuild.ts +0 -194
  120. package/src/cloudflare/bundle/fs.ts +0 -103
  121. package/src/cloudflare/bundle/index.ts +0 -6
  122. package/src/cloudflare/bundle/inline.ts +0 -42
  123. package/src/cloudflare/bundle/normalize.ts +0 -76
  124. package/src/cloudflare/bundle/shared.ts +0 -73
@@ -0,0 +1,364 @@
1
+ import esbuild from "esbuild";
2
+ import { err, ok, type Result } from "neverthrow";
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { external, external_als } from "./bundle/externals.ts";
6
+ import { esbuildPluginCompatWarning } from "./bundle/plugin-compat-warning.ts";
7
+ import { createHotReloadPlugin } from "./bundle/plugin-hot-reload.ts";
8
+ import { esbuildPluginHybridNodeCompat } from "./bundle/plugin-hybrid-node-compat.ts";
9
+ import { createWasmPlugin } from "./bundle/plugin-wasm.ts";
10
+ import { validateNodeCompat } from "./bundle/validate-node-compat.ts";
11
+
12
+ export interface WorkerBundle {
13
+ entrypoint: string;
14
+ root?: string;
15
+ modules: WorkerBundle.Module[];
16
+ }
17
+
18
+ export function normalizeWorkerBundle(props: {
19
+ script: string | undefined;
20
+ entrypoint: string | undefined;
21
+ noBundle: boolean | undefined;
22
+ format: "cjs" | "esm" | undefined;
23
+ compatibilityDate: string;
24
+ compatibilityFlags: string[];
25
+ rules:
26
+ | {
27
+ globs: string[];
28
+ }[]
29
+ | undefined;
30
+ bundle?: Omit<
31
+ esbuild.BuildOptions,
32
+ "entryPoints" | "format" | "absWorkingDir"
33
+ >;
34
+ cwd: string;
35
+ outdir: string;
36
+ sourceMap: boolean | undefined;
37
+ }): Result<WorkerBundleSource, string> {
38
+ return validateNodeCompat({
39
+ compatibilityDate: props.compatibilityDate,
40
+ compatibilityFlags: props.compatibilityFlags,
41
+ noBundle: props.noBundle ?? false,
42
+ }).andThen((nodeCompat) => {
43
+ if (props.script) {
44
+ return ok(
45
+ new WorkerBundleSource.Inline({
46
+ content: props.script,
47
+ format: props.format ?? "esm",
48
+ nodeCompat,
49
+ }),
50
+ );
51
+ }
52
+ if (!props.entrypoint) {
53
+ return err(
54
+ "Either `script` or `entrypoint` must be provided for workers",
55
+ );
56
+ }
57
+ return ok(
58
+ props.noBundle
59
+ ? new WorkerBundleSource.FS({
60
+ entrypoint: props.entrypoint,
61
+ format: props.format ?? "esm",
62
+ nodeCompat,
63
+ cwd: props.cwd,
64
+ globs: props.rules?.flatMap((rule) => rule.globs),
65
+ sourcemaps: props.sourceMap !== false,
66
+ })
67
+ : new WorkerBundleSource.ESBuild({
68
+ entrypoint: props.entrypoint,
69
+ format: props.format ?? "esm",
70
+ nodeCompat,
71
+ cwd: props.cwd,
72
+ outdir: props.outdir,
73
+ sourcemap: props.sourceMap !== false ? "linked" : undefined,
74
+ ...props.bundle,
75
+ }),
76
+ );
77
+ });
78
+ }
79
+
80
+ export namespace WorkerBundle {
81
+ export interface Module {
82
+ type: "esm" | "cjs" | "text" | "data" | "wasm" | "sourcemap";
83
+ path: string;
84
+ content?: string | Uint8Array<ArrayBuffer>;
85
+ }
86
+
87
+ export const parseModules = (
88
+ paths: string[],
89
+ format: "esm" | "cjs",
90
+ ): WorkerBundle.Module[] => {
91
+ return paths.map((path) => {
92
+ const ext = path.split(".").pop();
93
+ switch (ext) {
94
+ case "js":
95
+ return { type: format, path };
96
+ case "mjs":
97
+ return { type: "esm", path };
98
+ case "cjs":
99
+ return { type: "cjs", path };
100
+ case "wasm":
101
+ return { type: "wasm", path };
102
+ case "map":
103
+ return { type: "sourcemap", path };
104
+ default:
105
+ return { type: "text", path };
106
+ }
107
+ });
108
+ };
109
+
110
+ export const toFormData = async (bundle: WorkerBundle) => {
111
+ const types = {
112
+ esm: "application/javascript+module",
113
+ cjs: "application/javascript",
114
+ text: "text/plain",
115
+ data: "application/octet-stream",
116
+ wasm: "application/wasm",
117
+ sourcemap: "application/source-map",
118
+ } as const;
119
+ const form = new FormData();
120
+ await Promise.all(
121
+ bundle.modules.map(async (module) => {
122
+ const content =
123
+ module.content ??
124
+ (await fs.readFile(path.join(bundle.root!, module.path)));
125
+ form.append(
126
+ module.path,
127
+ new Blob([content], {
128
+ type: types[module.type],
129
+ }),
130
+ module.path,
131
+ );
132
+ }),
133
+ );
134
+ return form;
135
+ };
136
+ }
137
+
138
+ export interface WorkerBundleSource {
139
+ create(): Promise<WorkerBundle>;
140
+ watch(signal: AbortSignal): AsyncIterable<WorkerBundle>;
141
+ delete?(): Promise<void>;
142
+ }
143
+
144
+ export namespace WorkerBundleSource {
145
+ export interface BaseProps {
146
+ format: "cjs" | "esm";
147
+ nodeCompat: "als" | "v2" | null;
148
+ }
149
+
150
+ export class Inline implements WorkerBundleSource {
151
+ constructor(
152
+ private props: WorkerBundleSource.BaseProps & { content: string },
153
+ ) {}
154
+
155
+ async create(): Promise<WorkerBundle> {
156
+ return {
157
+ entrypoint: "worker.js",
158
+ modules: [
159
+ {
160
+ path: "worker.js",
161
+ type: this.props.format,
162
+ content: this.props.content,
163
+ },
164
+ ],
165
+ };
166
+ }
167
+
168
+ async *watch(): AsyncIterable<WorkerBundle> {
169
+ yield await this.create();
170
+ }
171
+ }
172
+
173
+ export class FS implements WorkerBundleSource {
174
+ private root: string;
175
+ private entrypoint: string;
176
+ private globs: string[];
177
+ private format: "esm" | "cjs";
178
+
179
+ constructor(
180
+ props: WorkerBundleSource.BaseProps & {
181
+ entrypoint: string;
182
+ globs: string[] | undefined;
183
+ cwd: string;
184
+ sourcemaps: boolean;
185
+ },
186
+ ) {
187
+ const entrypoint = path.resolve(props.cwd, props.entrypoint);
188
+ this.root = path.dirname(entrypoint);
189
+ this.entrypoint = path.relative(this.root, entrypoint);
190
+ this.globs = props.globs ?? [
191
+ "**/*.js",
192
+ "**/*.mjs",
193
+ "**/*.wasm",
194
+ ...(props.sourcemaps ? ["**/*.js.map"] : []),
195
+ ];
196
+ this.format = props.format;
197
+ }
198
+
199
+ async create(): Promise<WorkerBundle> {
200
+ return {
201
+ entrypoint: this.entrypoint,
202
+ root: this.root,
203
+ modules: await this.readFiles(),
204
+ };
205
+ }
206
+
207
+ async *watch(signal: AbortSignal): AsyncIterable<WorkerBundle> {
208
+ const watcher = fs.watch(this.root, { recursive: true, signal });
209
+ for await (const event of watcher) {
210
+ if (event.eventType === "change") {
211
+ yield await this.create();
212
+ }
213
+ }
214
+ }
215
+
216
+ private async readFiles() {
217
+ const fileNames = new Set<string>();
218
+ await Promise.all(
219
+ this.globs.map(async (glob) => {
220
+ for await (const file of fs.glob(glob, { cwd: this.root })) {
221
+ fileNames.add(file);
222
+ }
223
+ }),
224
+ );
225
+ if (fileNames.size === 0) {
226
+ throw new Error(
227
+ `No files found matching ${this.globs.join(", ")} in ${this.root}`,
228
+ );
229
+ }
230
+ return WorkerBundle.parseModules(Array.from(fileNames), this.format);
231
+ }
232
+ }
233
+
234
+ export interface ESBuildProps
235
+ extends WorkerBundleSource.BaseProps,
236
+ Omit<
237
+ esbuild.BuildOptions,
238
+ "entryPoints" | "format" | "absWorkingDir" | "outdir"
239
+ > {
240
+ entrypoint: string;
241
+ cwd: string;
242
+ outdir: string;
243
+ }
244
+
245
+ export class ESBuild implements WorkerBundleSource {
246
+ constructor(private props: ESBuildProps) {
247
+ // Normalize paths to ensure consistency between esbuild inputs and outputs:
248
+ // - the `cwd` is absolute
249
+ // - the `entrypoint` and `outdir` are relative to the `cwd`
250
+ // This is so we can identify the entrypoint from the metafile.
251
+ this.props.cwd = path.resolve(this.props.cwd);
252
+ this.props.entrypoint = path.isAbsolute(props.entrypoint)
253
+ ? path.relative(this.props.cwd, props.entrypoint)
254
+ : path.normalize(props.entrypoint);
255
+ this.props.outdir = path.isAbsolute(props.outdir)
256
+ ? path.relative(this.props.cwd, props.outdir)
257
+ : path.normalize(props.outdir);
258
+ }
259
+
260
+ async create(): Promise<WorkerBundle> {
261
+ const wasmPlugin = createWasmPlugin();
262
+ const options = this.buildOptions([wasmPlugin.plugin]);
263
+ const result = await esbuild.build(options);
264
+ const { entrypoint, root, modules } = this.resolveBuildOutput(
265
+ result.metafile,
266
+ );
267
+ return {
268
+ entrypoint,
269
+ root,
270
+ modules: [...modules, ...wasmPlugin.modules.values()],
271
+ };
272
+ }
273
+
274
+ async *watch(signal: AbortSignal): AsyncIterable<WorkerBundle> {
275
+ const wasm = createWasmPlugin();
276
+ const hotReload = createHotReloadPlugin();
277
+ const options = this.buildOptions([wasm.plugin, hotReload.plugin]);
278
+
279
+ const context = await esbuild.context(options);
280
+ signal.addEventListener("abort", () => context.dispose());
281
+ await context.watch();
282
+
283
+ for await (const result of hotReload.iterator) {
284
+ const { entrypoint, root, modules } = this.resolveBuildOutput(
285
+ result.metafile!,
286
+ );
287
+ yield {
288
+ entrypoint,
289
+ root,
290
+ modules: [...modules, ...wasm.modules.values()],
291
+ };
292
+ }
293
+ }
294
+
295
+ async delete(): Promise<void> {
296
+ await fs.rm(path.join(this.props.cwd, this.props.outdir), {
297
+ recursive: true,
298
+ force: true,
299
+ });
300
+ }
301
+
302
+ private buildOptions(additionalPlugins: esbuild.Plugin[]) {
303
+ const { entrypoint, nodeCompat, cwd, format, ...props } = this.props;
304
+ return {
305
+ entryPoints: [entrypoint],
306
+ absWorkingDir: cwd,
307
+ format,
308
+ target: "esnext",
309
+ ...props,
310
+ metafile: true,
311
+ write: true,
312
+ bundle: true,
313
+ conditions: props.conditions ?? ["workerd", "worker", "browser"],
314
+ mainFields: props.mainFields,
315
+ loader: {
316
+ ".sql": "text",
317
+ ".json": "json",
318
+ ".wasm": "binary",
319
+ ...props.loader,
320
+ },
321
+ plugins: [
322
+ nodeCompat === "v2"
323
+ ? esbuildPluginHybridNodeCompat()
324
+ : esbuildPluginCompatWarning(nodeCompat ?? null),
325
+ ...(props.plugins ?? []),
326
+ ...additionalPlugins,
327
+ ],
328
+ alias: props.alias,
329
+ external: [
330
+ ...(nodeCompat === "als" ? external_als : external),
331
+ ...(props.external ?? []),
332
+ ],
333
+ } satisfies esbuild.BuildOptions;
334
+ }
335
+
336
+ private resolveBuildOutput(metafile: esbuild.Metafile): WorkerBundle {
337
+ const outdir = path.resolve(this.props.cwd, this.props.outdir);
338
+ const paths: string[] = [];
339
+ let entrypoint: string | undefined;
340
+ for (const [key, value] of Object.entries(metafile.outputs)) {
341
+ const name = path.relative(outdir, path.resolve(this.props.cwd, key));
342
+ paths.push(name);
343
+ if (value.entryPoint === this.props.entrypoint) {
344
+ entrypoint = name;
345
+ }
346
+ }
347
+ if (!entrypoint) {
348
+ throw new Error(
349
+ `Failed to find entrypoint in metafile - expected ${this.props.entrypoint} but found ${Object.values(
350
+ metafile.outputs,
351
+ )
352
+ .map((v) => v.entryPoint)
353
+ .filter((v) => v !== undefined)
354
+ .join(", ")}`,
355
+ );
356
+ }
357
+ return {
358
+ entrypoint,
359
+ root: outdir,
360
+ modules: WorkerBundle.parseModules(paths, this.props.format),
361
+ };
362
+ }
363
+ }
364
+ }