alchemy 0.74.2 → 0.75.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 (202) hide show
  1. package/README.md +1 -1
  2. package/bin/alchemy.js +1760 -1725
  3. package/bin/alchemy.ts +1 -1
  4. package/bin/commands/create.ts +5 -4
  5. package/bin/commands/init.ts +109 -65
  6. package/bin/constants.ts +2 -2
  7. package/bin/services/dependencies.ts +4 -4
  8. package/bin/services/execute-alchemy.ts +1 -1
  9. package/bin/services/find-workspace-root.ts +1 -1
  10. package/bin/services/get-package-version.ts +4 -4
  11. package/bin/services/github-workflow.ts +7 -7
  12. package/bin/services/template-manager.ts +20 -13
  13. package/bin/services/vibe-rules.ts +6 -5
  14. package/bin/trpc.ts +7 -0
  15. package/bin/types.ts +1 -0
  16. package/lib/alchemy.d.ts.map +1 -1
  17. package/lib/alchemy.js +29 -1
  18. package/lib/alchemy.js.map +1 -1
  19. package/lib/auth.js +1 -1
  20. package/lib/auth.js.map +1 -1
  21. package/lib/cloudflare/astro/astro.js +1 -1
  22. package/lib/cloudflare/astro/astro.js.map +1 -1
  23. package/lib/cloudflare/bun-spa/bun-spa.d.ts +1 -1
  24. package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
  25. package/lib/cloudflare/bun-spa/bun-spa.js +10 -11
  26. package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -1
  27. package/lib/cloudflare/bun-spa/get-bun-spa-backend.d.ts +11 -0
  28. package/lib/cloudflare/bun-spa/get-bun-spa-backend.d.ts.map +1 -0
  29. package/lib/cloudflare/bun-spa/get-bun-spa-backend.js +26 -0
  30. package/lib/cloudflare/bun-spa/get-bun-spa-backend.js.map +1 -0
  31. package/lib/cloudflare/bun-spa/index.d.ts +2 -0
  32. package/lib/cloudflare/bun-spa/index.d.ts.map +1 -0
  33. package/lib/cloudflare/bun-spa/index.js +2 -0
  34. package/lib/cloudflare/bun-spa/index.js.map +1 -0
  35. package/lib/cloudflare/bundle/internal-worker-bundle.js +1 -1
  36. package/lib/cloudflare/bundle/internal-worker-bundle.js.map +1 -1
  37. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +1 -1
  38. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  39. package/lib/cloudflare/bundle/plugin-node-compat.js +1 -1
  40. package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -1
  41. package/lib/cloudflare/bundle/plugin-wasm.js +1 -1
  42. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
  43. package/lib/cloudflare/d1-migrations.js +1 -1
  44. package/lib/cloudflare/d1-migrations.js.map +1 -1
  45. package/lib/cloudflare/kv-namespace.d.ts +5 -0
  46. package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
  47. package/lib/cloudflare/kv-namespace.js +44 -19
  48. package/lib/cloudflare/kv-namespace.js.map +1 -1
  49. package/lib/cloudflare/miniflare/miniflare-controller.js +1 -1
  50. package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
  51. package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
  52. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +34 -4
  53. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
  54. package/lib/cloudflare/miniflare/paths.d.ts.map +1 -1
  55. package/lib/cloudflare/miniflare/paths.js +2 -1
  56. package/lib/cloudflare/miniflare/paths.js.map +1 -1
  57. package/lib/cloudflare/miniflare/symlink-miniflare-state.js +1 -1
  58. package/lib/cloudflare/miniflare/symlink-miniflare-state.js.map +1 -1
  59. package/lib/cloudflare/next.js +1 -1
  60. package/lib/cloudflare/next.js.map +1 -1
  61. package/lib/cloudflare/nuxt/nuxt.js +1 -1
  62. package/lib/cloudflare/nuxt/nuxt.js.map +1 -1
  63. package/lib/cloudflare/react-router/plugin.d.ts.map +1 -1
  64. package/lib/cloudflare/react-router/plugin.js +2 -1
  65. package/lib/cloudflare/react-router/plugin.js.map +1 -1
  66. package/lib/cloudflare/react-router/react-router.js +1 -1
  67. package/lib/cloudflare/react-router/react-router.js.map +1 -1
  68. package/lib/cloudflare/redwood/redwood.js +1 -1
  69. package/lib/cloudflare/redwood/redwood.js.map +1 -1
  70. package/lib/cloudflare/sveltekit/sveltekit.js +1 -1
  71. package/lib/cloudflare/sveltekit/sveltekit.js.map +1 -1
  72. package/lib/cloudflare/tanstack-start/tanstack-start.js +1 -1
  73. package/lib/cloudflare/tanstack-start/tanstack-start.js.map +1 -1
  74. package/lib/cloudflare/tunnel.d.ts.map +1 -1
  75. package/lib/cloudflare/tunnel.js +25 -1
  76. package/lib/cloudflare/tunnel.js.map +1 -1
  77. package/lib/cloudflare/vite/plugin.js +1 -1
  78. package/lib/cloudflare/vite/plugin.js.map +1 -1
  79. package/lib/cloudflare/website.js +1 -1
  80. package/lib/cloudflare/website.js.map +1 -1
  81. package/lib/cloudflare/worker.d.ts.map +1 -1
  82. package/lib/cloudflare/worker.js +1 -1
  83. package/lib/cloudflare/worker.js.map +1 -1
  84. package/lib/cloudflare/wrangler.json.js +1 -1
  85. package/lib/cloudflare/wrangler.json.js.map +1 -1
  86. package/lib/coinbase/faucet.js +2 -2
  87. package/lib/coinbase/faucet.js.map +1 -1
  88. package/lib/docker/api.js +1 -1
  89. package/lib/docker/api.js.map +1 -1
  90. package/lib/docker/image.js +1 -1
  91. package/lib/docker/image.js.map +1 -1
  92. package/lib/esbuild/bundle.js +1 -1
  93. package/lib/esbuild/bundle.js.map +1 -1
  94. package/lib/fs/file.js +1 -1
  95. package/lib/fs/file.js.map +1 -1
  96. package/lib/llms.js +1 -1
  97. package/lib/llms.js.map +1 -1
  98. package/lib/os/exec.js +1 -1
  99. package/lib/os/exec.js.map +1 -1
  100. package/lib/scope.d.ts.map +1 -1
  101. package/lib/scope.js +1 -1
  102. package/lib/scope.js.map +1 -1
  103. package/lib/state/file-system-state-store.js +1 -1
  104. package/lib/state/file-system-state-store.js.map +1 -1
  105. package/lib/state/migrations.js +1 -1
  106. package/lib/state/migrations.js.map +1 -1
  107. package/lib/state/sqlite-state-store.js +1 -1
  108. package/lib/state/sqlite-state-store.js.map +1 -1
  109. package/lib/stripe/file.d.ts +3 -3
  110. package/lib/stripe/file.js +3 -3
  111. package/lib/test/bun.js +1 -1
  112. package/lib/test/bun.js.map +1 -1
  113. package/lib/test/prune.js +1 -1
  114. package/lib/test/prune.js.map +1 -1
  115. package/lib/test/vitest.js +1 -1
  116. package/lib/test/vitest.js.map +1 -1
  117. package/lib/util/content-type.js +1 -1
  118. package/lib/util/content-type.js.map +1 -1
  119. package/lib/util/idempotent-spawn.js +1 -1
  120. package/lib/util/idempotent-spawn.js.map +1 -1
  121. package/lib/util/lock.js +1 -1
  122. package/lib/util/lock.js.map +1 -1
  123. package/package.json +6 -1
  124. package/src/alchemy.ts +56 -1
  125. package/src/auth.ts +1 -1
  126. package/src/cloudflare/astro/astro.ts +1 -1
  127. package/src/cloudflare/bun-spa/bun-spa.ts +13 -13
  128. package/src/cloudflare/bun-spa/get-bun-spa-backend.ts +29 -0
  129. package/src/cloudflare/bun-spa/index.ts +1 -0
  130. package/src/cloudflare/bundle/internal-worker-bundle.ts +1 -1
  131. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +1 -1
  132. package/src/cloudflare/bundle/plugin-node-compat.ts +1 -1
  133. package/src/cloudflare/bundle/plugin-wasm.ts +1 -1
  134. package/src/cloudflare/d1-migrations.ts +1 -1
  135. package/src/cloudflare/kv-namespace.ts +52 -20
  136. package/src/cloudflare/miniflare/miniflare-controller.ts +1 -1
  137. package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +39 -4
  138. package/src/cloudflare/miniflare/paths.ts +2 -1
  139. package/src/cloudflare/miniflare/symlink-miniflare-state.ts +1 -1
  140. package/src/cloudflare/next.ts +1 -1
  141. package/src/cloudflare/nuxt/nuxt.ts +1 -1
  142. package/src/cloudflare/react-router/plugin.ts +2 -1
  143. package/src/cloudflare/react-router/react-router.ts +1 -1
  144. package/src/cloudflare/redwood/redwood.ts +1 -1
  145. package/src/cloudflare/sveltekit/sveltekit.ts +1 -1
  146. package/src/cloudflare/tanstack-start/tanstack-start.ts +1 -1
  147. package/src/cloudflare/tunnel.ts +36 -1
  148. package/src/cloudflare/vite/plugin.ts +1 -1
  149. package/src/cloudflare/website.ts +1 -1
  150. package/src/cloudflare/worker.ts +1 -3
  151. package/src/cloudflare/wrangler.json.ts +1 -1
  152. package/src/coinbase/faucet.ts +2 -2
  153. package/src/docker/api.ts +1 -1
  154. package/src/docker/image.ts +1 -1
  155. package/src/esbuild/bundle.ts +1 -1
  156. package/src/fs/file.ts +1 -1
  157. package/src/llms.ts +1 -1
  158. package/src/os/exec.ts +1 -1
  159. package/src/scope.ts +1 -1
  160. package/src/state/file-system-state-store.ts +1 -1
  161. package/src/state/migrations.ts +1 -1
  162. package/src/state/sqlite-state-store.ts +1 -1
  163. package/src/stripe/file.ts +3 -3
  164. package/src/test/bun.ts +1 -1
  165. package/src/test/prune.ts +1 -1
  166. package/src/test/vitest.ts +1 -1
  167. package/src/util/content-type.ts +1 -1
  168. package/src/util/idempotent-spawn.ts +1 -1
  169. package/src/util/lock.ts +1 -1
  170. package/templates/astro/alchemy.run.ts +0 -2
  171. package/templates/bun-spa/README.md +94 -0
  172. package/templates/bun-spa/_env +1 -0
  173. package/templates/bun-spa/_env.example +1 -0
  174. package/templates/bun-spa/_gitignore +29 -0
  175. package/templates/bun-spa/alchemy.run.ts +21 -0
  176. package/templates/bun-spa/bun-env.d.ts +26 -0
  177. package/templates/bun-spa/eslint.config.js +27 -0
  178. package/templates/bun-spa/package.json +30 -0
  179. package/templates/bun-spa/src/App.css +46 -0
  180. package/templates/bun-spa/src/App.tsx +54 -0
  181. package/templates/bun-spa/src/assets/logo.svg +1 -0
  182. package/templates/bun-spa/src/assets/react.svg +1 -0
  183. package/templates/bun-spa/src/frontend.tsx +15 -0
  184. package/templates/bun-spa/src/index.css +69 -0
  185. package/templates/bun-spa/src/index.html +14 -0
  186. package/templates/bun-spa/src/worker.ts +51 -0
  187. package/templates/bun-spa/tsconfig.json +24 -0
  188. package/templates/hono/alchemy.run.ts +0 -2
  189. package/templates/nextjs/alchemy.run.ts +0 -2
  190. package/templates/nuxt/alchemy.run.ts +0 -2
  191. package/templates/react-router/alchemy.run.ts +0 -2
  192. package/templates/react-router/package.json +1 -1
  193. package/templates/rwsdk/alchemy.run.ts +0 -1
  194. package/templates/rwsdk/package.json +1 -1
  195. package/templates/sveltekit/alchemy.run.ts +0 -2
  196. package/templates/sveltekit/package.json +1 -1
  197. package/templates/tanstack-start/alchemy.run.ts +0 -2
  198. package/templates/tanstack-start/package.json +1 -1
  199. package/templates/typescript/alchemy.run.ts +0 -2
  200. package/templates/vite/alchemy.run.ts +0 -2
  201. package/templates/vite/package.json +1 -1
  202. package/workers/tunnel-proxy.js +1 -1
@@ -1,6 +1,6 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs/promises";
3
- import path from "node:path";
3
+ import path from "pathe";
4
4
  import { memoize } from "../../util/memoize.ts";
5
5
  import type { WorkerBundle } from "../worker-bundle.ts";
6
6
 
@@ -5,7 +5,7 @@
5
5
  import type { Plugin, PluginBuild } from "esbuild";
6
6
  import assert from "node:assert";
7
7
  import { createRequire } from "node:module";
8
- import nodePath from "node:path";
8
+ import nodePath from "pathe";
9
9
  import { dedent } from "../../util/dedent.ts";
10
10
  import { NODEJS_MODULES_RE } from "./nodejs-builtin-modules.ts";
11
11
 
@@ -1,6 +1,6 @@
1
1
  import type { Plugin } from "esbuild";
2
2
  import type { NodeJSCompatMode } from "miniflare";
3
- import { relative } from "node:path";
3
+ import { relative } from "pathe";
4
4
  import pc from "picocolors";
5
5
  import { dedent } from "../../util/dedent.ts";
6
6
  import { logger } from "../../util/logger.ts";
@@ -1,7 +1,7 @@
1
1
  import type esbuild from "esbuild";
2
2
  import assert from "node:assert";
3
3
  import fs from "node:fs/promises";
4
- import path from "node:path";
4
+ import path from "pathe";
5
5
  import type { WorkerBundle } from "../worker-bundle.ts";
6
6
 
7
7
  export function createWasmPlugin() {
@@ -1,6 +1,6 @@
1
1
  import { glob } from "glob";
2
2
  import * as fs from "node:fs/promises";
3
- import * as path from "node:path";
3
+ import path from "pathe";
4
4
  import { logger } from "../util/logger.ts";
5
5
  import { handleApiError } from "./api-error.ts";
6
6
  import type { CloudflareApi } from "./api.ts";
@@ -13,6 +13,8 @@ import {
13
13
  type CloudflareApiOptions,
14
14
  } from "./api.ts";
15
15
  import { deleteMiniflareBinding } from "./miniflare/delete.ts";
16
+ import * as mf from "miniflare";
17
+ import { getDefaultPersistPath } from "./miniflare/paths.ts";
16
18
 
17
19
  /**
18
20
  * Properties for creating or updating a KV Namespace
@@ -220,6 +222,13 @@ const _KVNamespace = Resource(
220
222
  };
221
223
 
222
224
  if (local) {
225
+ if (props.values) {
226
+ await insertLocalKVRecords({
227
+ namespaceId: dev.id,
228
+ values: props.values,
229
+ rootDir: this.scope.rootDir,
230
+ });
231
+ }
223
232
  return {
224
233
  type: "kv_namespace",
225
234
  namespaceId: this.output?.namespaceId ?? "",
@@ -340,31 +349,18 @@ export async function insertKVRecords(
340
349
  const BATCH_SIZE = 10000;
341
350
 
342
351
  for (let i = 0; i < props.values.length; i += BATCH_SIZE) {
343
- const batch = props.values.slice(i, i + BATCH_SIZE);
344
-
345
- const bulkPayload = batch.map((entry) => {
346
- const item: any = {
352
+ const bulkPayload = props.values
353
+ .slice(i, i + BATCH_SIZE)
354
+ .map((entry) => ({
347
355
  key: entry.key,
348
356
  value:
349
357
  typeof entry.value === "string"
350
358
  ? entry.value
351
359
  : JSON.stringify(entry.value),
352
- };
353
-
354
- if (entry.expiration) {
355
- item.expiration = entry.expiration;
356
- }
357
-
358
- if (entry.expirationTtl) {
359
- item.expiration_ttl = entry.expirationTtl;
360
- }
361
-
362
- if (entry.metadata) {
363
- item.metadata = entry.metadata;
364
- }
365
-
366
- return item;
367
- });
360
+ expiration: entry.expiration,
361
+ expiration_ttl: entry.expirationTtl,
362
+ metadata: entry.metadata,
363
+ }));
368
364
 
369
365
  await withExponentialBackoff(
370
366
  async () => {
@@ -397,6 +393,42 @@ export async function insertKVRecords(
397
393
  }
398
394
  }
399
395
 
396
+ export async function insertLocalKVRecords(input: {
397
+ namespaceId: string;
398
+ values: KVPair[];
399
+ rootDir: string;
400
+ }) {
401
+ const miniflare = new mf.Miniflare({
402
+ script: "",
403
+ modules: true,
404
+ defaultPersistRoot: getDefaultPersistPath(input.rootDir),
405
+ kvNamespaces: { KV: input.namespaceId },
406
+ kvPersist: true,
407
+ log: process.env.DEBUG ? new mf.Log(mf.LogLevel.DEBUG) : undefined,
408
+ });
409
+ try {
410
+ await miniflare.ready;
411
+ const kv = await miniflare.getKVNamespace("KV");
412
+ await Promise.all(
413
+ input.values.map((record) =>
414
+ kv.put(
415
+ record.key,
416
+ typeof record.value === "string"
417
+ ? record.value
418
+ : JSON.stringify(record.value),
419
+ {
420
+ expiration: record.expiration,
421
+ expirationTtl: record.expirationTtl,
422
+ metadata: record.metadata,
423
+ },
424
+ ),
425
+ ),
426
+ );
427
+ } finally {
428
+ await miniflare.dispose();
429
+ }
430
+ }
431
+
400
432
  /**
401
433
  * Interface representing a KV namespace as returned by Cloudflare API
402
434
  */
@@ -1,6 +1,6 @@
1
1
  import * as miniflare from "miniflare";
2
2
  import assert from "node:assert";
3
- import path from "node:path";
3
+ import path from "pathe";
4
4
  import { Scope } from "../../scope.ts";
5
5
  import { findOpenPort } from "../../util/find-open-port.ts";
6
6
  import type { HTTPServer } from "../../util/http.ts";
@@ -1,9 +1,11 @@
1
1
  import * as miniflare from "miniflare";
2
+ import assert from "node:assert";
2
3
  import { once } from "node:events";
3
4
  import http from "node:http";
4
5
  import { Readable } from "node:stream";
5
6
  import { WebSocket, WebSocketServer } from "ws";
6
7
  import { createUpgradeHandler } from "../../util/http.ts";
8
+ import { logger } from "../../util/logger.ts";
7
9
 
8
10
  export interface MiniflareWorkerProxy {
9
11
  url: URL;
@@ -90,12 +92,10 @@ export async function createMiniflareWorkerProxy(options: {
90
92
  });
91
93
  };
92
94
 
93
- server.listen(options.port);
94
- await once(server, "listening");
95
- const url = new URL(`http://localhost:${options.port}`);
95
+ await listen(server, options.port);
96
96
 
97
97
  return {
98
- url,
98
+ url: getAddress(server),
99
99
  close: async () => {
100
100
  // If we await this while the `wss` server is open, the server will hang.
101
101
  server.close();
@@ -103,6 +103,41 @@ export async function createMiniflareWorkerProxy(options: {
103
103
  };
104
104
  }
105
105
 
106
+ /**
107
+ * Listens on the given port, retrying on the next port if the port is already in use.
108
+ */
109
+ async function listen(server: http.Server, port: number) {
110
+ try {
111
+ server.listen(port);
112
+ await once(server, "listening");
113
+ } catch (error) {
114
+ if (
115
+ typeof error === "object" &&
116
+ error !== null &&
117
+ "code" in error &&
118
+ error.code === "EADDRINUSE"
119
+ ) {
120
+ logger.warn(`Port ${port} is already in use, trying ${port + 1}...`);
121
+ await listen(server, port + 1);
122
+ } else {
123
+ throw error;
124
+ }
125
+ }
126
+ }
127
+
128
+ function getAddress(server: http.Server) {
129
+ const address = server.address();
130
+ assert(
131
+ address &&
132
+ typeof address === "object" &&
133
+ "address" in address &&
134
+ "port" in address,
135
+ "Server is not listening",
136
+ );
137
+ const hostname = address.address === "::" ? "localhost" : address.address;
138
+ return new URL(`http://${hostname}:${address.port}`);
139
+ }
140
+
106
141
  interface RequestInfo {
107
142
  method: string;
108
143
  url: URL;
@@ -1,6 +1,7 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ // biome-ignore lint/style/noRestrictedImports: I/O is acceptable here
2
3
  import { existsSync, readlinkSync, statSync } from "node:fs";
3
- import path from "node:path";
4
+ import path from "pathe";
4
5
  import { ALCHEMY_ROOT } from "../../util/root-dir.ts";
5
6
 
6
7
  const dynamicImportContext = new AsyncLocalStorage<boolean>();
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
  import { getDefaultPersistPath } from "./paths.ts";
4
4
 
5
5
  export async function writeMiniflareSymlink(rootDir: string, cwd: string) {
@@ -1,5 +1,5 @@
1
1
  import * as fs from "node:fs/promises";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
  import pc from "picocolors";
4
4
  import { getPackageManagerRunner } from "../util/detect-package-manager.ts";
5
5
  import { exists } from "../util/exists.ts";
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
  import { getPackageManagerRunner } from "../../util/detect-package-manager.ts";
3
3
  import type { Assets } from "../assets.ts";
4
4
  import type { Bindings } from "../bindings.ts";
@@ -1,6 +1,7 @@
1
1
  import type { PluginConfig } from "@cloudflare/vite-plugin";
2
+ // biome-ignore lint/style/noRestrictedImports: I/O is acceptable here
2
3
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
- import { dirname } from "node:path";
4
+ import { dirname } from "pathe";
4
5
  import { getDefaultConfigPath } from "../miniflare/paths.ts";
5
6
  import alchemyVite from "../vite/plugin.ts";
6
7
 
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
  import { getPackageManagerRunner } from "../../util/detect-package-manager.ts";
3
3
  import type { Assets } from "../assets.ts";
4
4
  import type { Bindings } from "../bindings.ts";
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
  import type { Assets } from "../assets.ts";
3
3
  import type { Bindings } from "../bindings.ts";
4
4
  import { Vite, type ViteProps } from "../vite/vite.ts";
@@ -1,4 +1,4 @@
1
- import { join } from "node:path";
1
+ import { join } from "pathe";
2
2
  import type { Assets } from "../assets.ts";
3
3
  import type { Bindings } from "../bindings.ts";
4
4
  import { Vite, type ViteProps } from "../vite/vite.ts";
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
  import { exists } from "../../util/exists.ts";
3
3
  import type { Assets } from "../assets.ts";
4
4
  import type { Bindings } from "../bindings.ts";
@@ -494,6 +494,9 @@ export const Tunnel = Resource(
494
494
  const name =
495
495
  props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
496
496
 
497
+ // Track if we're replacing the tunnel (don't fetch token in this case)
498
+ let isReplacing = false;
499
+
497
500
  if (this.phase === "update" && this.output.name !== name) {
498
501
  console.log("replacing tunnel", this.output.name, name);
499
502
  this.replace(true);
@@ -563,7 +566,6 @@ export const Tunnel = Resource(
563
566
  (error.message.includes("already have a tunnel with this name") ||
564
567
  error.message.includes("already exists"))
565
568
  ) {
566
- console.log(error);
567
569
  logger.log(`Tunnel '${name}' already exists, adopting it`);
568
570
 
569
571
  // Find the existing tunnel by name
@@ -596,6 +598,12 @@ export const Tunnel = Resource(
596
598
  }
597
599
  }
598
600
 
601
+ // Ensure tunnel token is available (unless we're replacing)
602
+ // Sometimes Cloudflare doesn't return the token in the initial response
603
+ if (!isReplacing && !tunnelData.token) {
604
+ tunnelData.token = await getTunnelToken(api, tunnelData.id);
605
+ }
606
+
599
607
  // Handle DNS records for ingress hostnames
600
608
  let dnsRecords = this.output?.dnsRecords || {};
601
609
 
@@ -918,3 +926,30 @@ export async function findTunnelByName(
918
926
  // No matching tunnel found
919
927
  return null;
920
928
  }
929
+
930
+ /**
931
+ * Get tunnel token
932
+ */
933
+ async function getTunnelToken(
934
+ api: CloudflareApi,
935
+ tunnelId: string,
936
+ ): Promise<string> {
937
+ const response = await api.get(
938
+ `/accounts/${api.accountId}/cfd_tunnel/${tunnelId}/token`,
939
+ );
940
+
941
+ if (!response.ok) {
942
+ logger.error(
943
+ `Failed to fetch token for tunnel ${tunnelId}: ${response.status} ${response.statusText}`,
944
+ );
945
+ await handleApiError(response, "get token", "tunnel", tunnelId);
946
+ }
947
+
948
+ const data = (await response.json()) as CloudflareApiResponse<string>;
949
+ if (!data.result) {
950
+ logger.warn(`Could not retrieve token for tunnel ${tunnelId}`);
951
+ } else {
952
+ logger.log(`Successfully fetched token for tunnel ${tunnelId}`);
953
+ }
954
+ return data.result;
955
+ }
@@ -1,5 +1,5 @@
1
1
  import { cloudflare, type PluginConfig } from "@cloudflare/vite-plugin";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
  import type { PluginOption } from "vite";
4
4
  import {
5
5
  getDefaultConfigPath,
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import fs from "node:fs/promises";
3
- import path from "node:path";
3
+ import path from "pathe";
4
4
  import { Exec } from "../os/index.ts";
5
5
  import { Scope } from "../scope.ts";
6
6
  import { dedent } from "../util/dedent.ts";
@@ -34,6 +34,7 @@ import {
34
34
  } from "./durable-object-namespace.ts";
35
35
  import { type EventSource, isQueueEventSource } from "./event-source.ts";
36
36
  import { deleteMiniflareWorkerData } from "./miniflare/delete.ts";
37
+ import { MiniflareController } from "./miniflare/miniflare-controller.ts";
37
38
  import {
38
39
  QueueConsumer,
39
40
  deleteQueueConsumer,
@@ -966,9 +967,6 @@ const _Worker = Resource(
966
967
  if (props.dev?.url) {
967
968
  url = props.dev.url;
968
969
  } else {
969
- const { MiniflareController } = await import(
970
- "./miniflare/miniflare-controller.js"
971
- );
972
970
  const controller = MiniflareController.singleton;
973
971
  url = await controller.add({
974
972
  api,
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
  import type { Context } from "../context.ts";
4
4
  import { Resource } from "../resource.ts";
5
5
  import { Scope } from "../scope.ts";
@@ -8,9 +8,9 @@
8
8
  * bun node_modules/alchemy/src/coinbase/faucet.ts backend/dev # Fund specific scope
9
9
  */
10
10
 
11
- import { readFile, access } from "node:fs/promises";
12
- import { join } from "node:path";
13
11
  import { glob } from "glob";
12
+ import { access, readFile } from "node:fs/promises";
13
+ import { join } from "pathe";
14
14
  import { createCdpClient } from "./client.ts";
15
15
  import type { EvmAccount } from "./evm-account.ts";
16
16
  import type { EvmSmartAccount } from "./evm-smart-account.ts";
package/src/docker/api.ts CHANGED
@@ -2,7 +2,7 @@ import { execa } from "execa";
2
2
  import { Buffer } from "node:buffer";
3
3
  import { spawn } from "node:child_process";
4
4
  import fs from "node:fs/promises";
5
- import path from "node:path";
5
+ import path from "pathe";
6
6
  import type { Duration, HealthcheckConfig } from "./container.ts";
7
7
 
8
8
  /**
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
- import path from "node:path";
3
+ import path from "pathe";
4
4
  import type { Context } from "../context.ts";
5
5
  import { Resource } from "../resource.ts";
6
6
  import type { Secret } from "../secret.ts";
@@ -1,7 +1,7 @@
1
1
  import type esbuild from "esbuild";
2
2
  import crypto from "node:crypto";
3
3
  import fs from "node:fs/promises";
4
- import path from "node:path";
4
+ import path from "pathe";
5
5
  import type { Context } from "../context.ts";
6
6
  import { Resource } from "../resource.ts";
7
7
  import { logger } from "../util/logger.ts";
package/src/fs/file.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
  import type { Context } from "../context.ts";
4
4
  import { Resource } from "../resource.ts";
5
5
  import { ignore } from "../util/ignore.ts";
package/src/llms.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
 
4
4
  export interface AlchemyRule {
5
5
  name: string;
package/src/os/exec.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { spawn, type SpawnOptions } from "node:child_process";
2
2
  import { createHash } from "node:crypto";
3
- import { join } from "node:path";
3
+ import { join } from "pathe";
4
4
  import type { Context } from "../context.ts";
5
5
  import { Resource } from "../resource.ts";
6
6
  import type { Secret } from "../secret.ts";
package/src/scope.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- import path from "node:path";
3
2
  import util from "node:util";
3
+ import path from "pathe";
4
4
  import pc from "picocolors";
5
5
  import type { Phase } from "./alchemy.ts";
6
6
  import { destroy, destroyAll, DestroyStrategy } from "./destroy.ts";
@@ -1,5 +1,5 @@
1
1
  import fs from "node:fs";
2
- import path from "node:path";
2
+ import path from "pathe";
3
3
  import { ResourceScope } from "../resource.ts";
4
4
  import type { Scope } from "../scope.ts";
5
5
  import { deserialize, serialize } from "../serde.ts";
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
 
3
3
  export const MIGRATIONS_DIRECTORY = path.join(
4
4
  import.meta.dirname,
@@ -1,6 +1,6 @@
1
1
  import type { Config as LibSQLConfig } from "@libsql/client";
2
2
  import fs from "node:fs/promises";
3
- import path from "node:path";
3
+ import path from "pathe";
4
4
  import type { Scope } from "../scope.ts";
5
5
  import { exists } from "../util/exists.ts";
6
6
  import { memoize } from "../util/memoize.ts";
@@ -121,21 +121,21 @@ export interface File {
121
121
  * @example
122
122
  * // Upload a dispute evidence file
123
123
  * const disputeEvidence = await File("dispute-evidence", {
124
- * file: fs.readFileSync("./evidence.pdf"),
124
+ * file: await fs.promises.readFile("./evidence.pdf"),
125
125
  * purpose: "dispute_evidence"
126
126
  * });
127
127
  *
128
128
  * @example
129
129
  * // Upload an identity document
130
130
  * const identityDocument = await File("identity-doc", {
131
- * file: fs.readFileSync("./passport.jpg"),
131
+ * file: await fs.promises.readFile("./passport.jpg"),
132
132
  * purpose: "identity_document"
133
133
  * });
134
134
  *
135
135
  * @example
136
136
  * // Upload a business logo with file link
137
137
  * const businessLogo = await File("business-logo", {
138
- * file: fs.readFileSync("./logo.png"),
138
+ * file: await fs.promises.readFile("./logo.png"),
139
139
  * purpose: "business_logo",
140
140
  * fileLink: {
141
141
  * create: true,
package/src/test/bun.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="bun" />
2
2
 
3
3
  import { afterAll, beforeAll, it } from "bun:test";
4
- import path from "node:path";
4
+ import path from "pathe";
5
5
  import { alchemy } from "../alchemy.ts";
6
6
  import { Scope } from "../scope.ts";
7
7
  import type { TestOptions } from "./options.ts";
package/src/test/prune.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as esbuild from "esbuild";
2
2
  import { glob } from "glob";
3
3
  import { exec, spawn } from "node:child_process";
4
- import path from "node:path";
5
4
  import { promisify } from "node:util";
5
+ import path from "pathe";
6
6
 
7
7
  const execAsync = promisify(exec);
8
8
 
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
  import { afterAll, beforeAll, it } from "vitest";
3
3
  import { alchemy } from "../alchemy.ts";
4
4
  import { Scope } from "../scope.ts";
@@ -1,4 +1,4 @@
1
- import path from "node:path";
1
+ import path from "pathe";
2
2
 
3
3
  // Common MIME types
4
4
  const mimeTypes: Record<string, string> = {
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import fs from "node:fs";
3
3
  import fsp from "node:fs/promises";
4
- import path from "node:path";
4
+ import path from "pathe";
5
5
  import { AsyncMutex } from "./mutex.ts";
6
6
 
7
7
  export interface IdempotentSpawnOptions {
package/src/util/lock.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
- import path from "node:path";
3
+ import path from "pathe";
4
4
  import lockfile from "proper-lockfile";
5
5
 
6
6
  const LOCK_DIR = path.join(os.homedir(), ".alchemy", "lock");
@@ -1,5 +1,3 @@
1
- /// <reference types="@types/node" />
2
-
3
1
  import alchemy from "alchemy";
4
2
  import { Astro } from "alchemy/cloudflare";
5
3