alchemy 0.44.4 → 0.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/drizzle/0000_busy_mauler.sql +19 -0
  2. package/drizzle/0001_concerned_iceman.sql +20 -0
  3. package/drizzle/meta/0000_snapshot.json +137 -0
  4. package/drizzle/meta/0001_snapshot.json +106 -0
  5. package/drizzle/meta/_journal.json +20 -0
  6. package/lib/alchemy.d.ts +3 -3
  7. package/lib/alchemy.d.ts.map +1 -1
  8. package/lib/alchemy.js +5 -2
  9. package/lib/alchemy.js.map +1 -1
  10. package/lib/build-date.d.ts +1 -1
  11. package/lib/build-date.js +1 -1
  12. package/lib/cloudflare/auth.js +1 -1
  13. package/lib/cloudflare/auth.js.map +1 -1
  14. package/lib/cloudflare/bundle/esbuild.d.ts +16 -0
  15. package/lib/cloudflare/bundle/esbuild.d.ts.map +1 -0
  16. package/lib/cloudflare/bundle/esbuild.js +159 -0
  17. package/lib/cloudflare/bundle/esbuild.js.map +1 -0
  18. package/lib/cloudflare/bundle/fs.d.ts +18 -0
  19. package/lib/cloudflare/bundle/fs.d.ts.map +1 -0
  20. package/lib/cloudflare/bundle/fs.js +75 -0
  21. package/lib/cloudflare/bundle/fs.js.map +1 -0
  22. package/lib/cloudflare/bundle/index.d.ts +3 -0
  23. package/lib/cloudflare/bundle/index.d.ts.map +1 -0
  24. package/lib/cloudflare/bundle/index.js +2 -0
  25. package/lib/cloudflare/bundle/index.js.map +1 -0
  26. package/lib/cloudflare/bundle/inline.d.ts +12 -0
  27. package/lib/cloudflare/bundle/inline.d.ts.map +1 -0
  28. package/lib/cloudflare/bundle/inline.js +32 -0
  29. package/lib/cloudflare/bundle/inline.js.map +1 -0
  30. package/lib/cloudflare/bundle/normalize.d.ts +21 -0
  31. package/lib/cloudflare/bundle/normalize.d.ts.map +1 -0
  32. package/lib/cloudflare/bundle/normalize.js +49 -0
  33. package/lib/cloudflare/bundle/normalize.js.map +1 -0
  34. package/lib/cloudflare/bundle/{nodejs-import-warning-plugin.d.ts → plugin-compat-warning.d.ts} +2 -2
  35. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts.map +1 -0
  36. package/lib/cloudflare/bundle/{nodejs-import-warning-plugin.js → plugin-compat-warning.js} +3 -3
  37. package/lib/cloudflare/bundle/plugin-compat-warning.js.map +1 -0
  38. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts +8 -0
  39. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts.map +1 -0
  40. package/lib/cloudflare/bundle/plugin-hot-reload.js +19 -0
  41. package/lib/cloudflare/bundle/plugin-hot-reload.js.map +1 -0
  42. package/lib/cloudflare/bundle/{nodejs-compat.d.ts → plugin-hybrid-node-compat.d.ts} +2 -2
  43. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts.map +1 -0
  44. package/lib/cloudflare/bundle/{nodejs-compat.js → plugin-hybrid-node-compat.js} +10 -10
  45. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -0
  46. package/lib/cloudflare/bundle/plugin-wasm.d.ts +3 -0
  47. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -0
  48. package/lib/cloudflare/bundle/plugin-wasm.js +55 -0
  49. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -0
  50. package/lib/cloudflare/bundle/shared.d.ts +30 -0
  51. package/lib/cloudflare/bundle/shared.d.ts.map +1 -0
  52. package/lib/cloudflare/bundle/shared.js +34 -0
  53. package/lib/cloudflare/bundle/shared.js.map +1 -0
  54. package/lib/cloudflare/bundle/{nodejs-compat-mode.d.ts → validate-node-compat.d.ts} +8 -4
  55. package/lib/cloudflare/bundle/validate-node-compat.d.ts.map +1 -0
  56. package/lib/cloudflare/bundle/{nodejs-compat-mode.js → validate-node-compat.js} +27 -7
  57. package/lib/cloudflare/bundle/validate-node-compat.js.map +1 -0
  58. package/lib/cloudflare/container.d.ts +231 -0
  59. package/lib/cloudflare/container.d.ts.map +1 -1
  60. package/lib/cloudflare/container.js +7 -0
  61. package/lib/cloudflare/container.js.map +1 -1
  62. package/lib/cloudflare/d1-migrations.d.ts +1 -0
  63. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  64. package/lib/cloudflare/d1-migrations.js +18 -6
  65. package/lib/cloudflare/d1-migrations.js.map +1 -1
  66. package/lib/cloudflare/d1-state-store.d.ts +10 -0
  67. package/lib/cloudflare/d1-state-store.d.ts.map +1 -0
  68. package/lib/cloudflare/d1-state-store.js +70 -0
  69. package/lib/cloudflare/d1-state-store.js.map +1 -0
  70. package/lib/cloudflare/do-state-store/internal.js +1 -1
  71. package/lib/cloudflare/do-state-store/internal.js.map +1 -1
  72. package/lib/cloudflare/do-state-store/store.js +1 -1
  73. package/lib/cloudflare/do-state-store/store.js.map +1 -1
  74. package/lib/cloudflare/index.d.ts +3 -2
  75. package/lib/cloudflare/index.d.ts.map +1 -1
  76. package/lib/cloudflare/index.js +3 -2
  77. package/lib/cloudflare/index.js.map +1 -1
  78. package/lib/cloudflare/local-dev-cloudflare-shim.d.ts.map +1 -0
  79. package/lib/cloudflare/{bundle/local-dev-cloudflare-shim.js → local-dev-cloudflare-shim.js} +1 -1
  80. package/lib/cloudflare/local-dev-cloudflare-shim.js.map +1 -0
  81. package/lib/cloudflare/miniflare/http-server.d.ts.map +1 -0
  82. package/lib/cloudflare/miniflare/http-server.js.map +1 -0
  83. package/lib/cloudflare/{worker → miniflare}/miniflare-worker-options.d.ts +4 -3
  84. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -0
  85. package/lib/cloudflare/{worker → miniflare}/miniflare-worker-options.js +6 -3
  86. package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -0
  87. package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -0
  88. package/lib/cloudflare/{worker → miniflare}/miniflare.js +1 -1
  89. package/lib/cloudflare/miniflare/miniflare.js.map +1 -0
  90. package/lib/cloudflare/miniflare/mixed-mode.d.ts.map +1 -0
  91. package/lib/cloudflare/{worker → miniflare}/mixed-mode.js +1 -2
  92. package/lib/cloudflare/miniflare/mixed-mode.js.map +1 -0
  93. package/lib/cloudflare/user.js +1 -1
  94. package/lib/cloudflare/user.js.map +1 -1
  95. package/lib/cloudflare/worker/shared.d.ts +4 -0
  96. package/lib/cloudflare/worker/shared.d.ts.map +1 -0
  97. package/lib/cloudflare/worker/{get-worker-template.js → shared.js} +10 -1
  98. package/lib/cloudflare/worker/shared.js.map +1 -0
  99. package/lib/cloudflare/worker/tail.d.ts +18 -0
  100. package/lib/cloudflare/worker/tail.d.ts.map +1 -0
  101. package/lib/cloudflare/worker/tail.js +140 -0
  102. package/lib/cloudflare/worker/tail.js.map +1 -0
  103. package/lib/cloudflare/worker-metadata.d.ts +6 -2
  104. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  105. package/lib/cloudflare/worker-metadata.js +7 -23
  106. package/lib/cloudflare/worker-metadata.js.map +1 -1
  107. package/lib/cloudflare/worker-stub.d.ts.map +1 -1
  108. package/lib/cloudflare/worker-stub.js +8 -7
  109. package/lib/cloudflare/worker-stub.js.map +1 -1
  110. package/lib/cloudflare/worker-subdomain.d.ts +31 -0
  111. package/lib/cloudflare/worker-subdomain.d.ts.map +1 -0
  112. package/lib/cloudflare/worker-subdomain.js +55 -0
  113. package/lib/cloudflare/worker-subdomain.js.map +1 -0
  114. package/lib/cloudflare/worker.d.ts +30 -36
  115. package/lib/cloudflare/worker.d.ts.map +1 -1
  116. package/lib/cloudflare/worker.js +641 -569
  117. package/lib/cloudflare/worker.js.map +1 -1
  118. package/lib/fs/file-system-state-store.js +1 -1
  119. package/lib/fs/file-system-state-store.js.map +1 -1
  120. package/lib/scope.d.ts +2 -2
  121. package/lib/scope.d.ts.map +1 -1
  122. package/lib/scope.js +23 -8
  123. package/lib/scope.js.map +1 -1
  124. package/lib/serde.d.ts +1 -1
  125. package/lib/serde.d.ts.map +1 -1
  126. package/lib/serde.js +4 -4
  127. package/lib/serde.js.map +1 -1
  128. package/lib/sqlite/base-sqlite-state-store.d.ts +31 -0
  129. package/lib/sqlite/base-sqlite-state-store.d.ts.map +1 -0
  130. package/lib/sqlite/base-sqlite-state-store.js +124 -0
  131. package/lib/sqlite/base-sqlite-state-store.js.map +1 -0
  132. package/lib/sqlite/index.d.ts +2 -0
  133. package/lib/sqlite/index.d.ts.map +1 -0
  134. package/lib/sqlite/index.js +2 -0
  135. package/lib/sqlite/index.js.map +1 -0
  136. package/lib/sqlite/schema.d.ts +198 -0
  137. package/lib/sqlite/schema.d.ts.map +1 -0
  138. package/lib/sqlite/schema.js +23 -0
  139. package/lib/sqlite/schema.js.map +1 -0
  140. package/lib/sqlite/sqlite-state-store.d.ts +60 -0
  141. package/lib/sqlite/sqlite-state-store.d.ts.map +1 -0
  142. package/lib/sqlite/sqlite-state-store.js +83 -0
  143. package/lib/sqlite/sqlite-state-store.js.map +1 -0
  144. package/lib/test/bun.d.ts.map +1 -1
  145. package/lib/test/bun.js +0 -6
  146. package/lib/test/bun.js.map +1 -1
  147. package/lib/test/vitest.d.ts.map +1 -1
  148. package/lib/test/vitest.js +19 -5
  149. package/lib/test/vitest.js.map +1 -1
  150. package/lib/util/memoize.d.ts +1 -1
  151. package/lib/util/memoize.d.ts.map +1 -1
  152. package/lib/util/memoize.js +11 -1
  153. package/lib/util/memoize.js.map +1 -1
  154. package/package.json +13 -3
  155. package/src/alchemy.ts +8 -6
  156. package/src/build-date.ts +1 -1
  157. package/src/cloudflare/auth.ts +1 -1
  158. package/src/cloudflare/bundle/esbuild.ts +194 -0
  159. package/src/cloudflare/bundle/fs.ts +98 -0
  160. package/src/cloudflare/bundle/index.ts +6 -0
  161. package/src/cloudflare/bundle/inline.ts +42 -0
  162. package/src/cloudflare/bundle/normalize.ts +76 -0
  163. package/src/cloudflare/bundle/{nodejs-import-warning-plugin.ts → plugin-compat-warning.ts} +4 -2
  164. package/src/cloudflare/bundle/plugin-hot-reload.ts +27 -0
  165. package/src/cloudflare/bundle/{nodejs-compat.ts → plugin-hybrid-node-compat.ts} +10 -10
  166. package/src/cloudflare/bundle/plugin-wasm.ts +62 -0
  167. package/src/cloudflare/bundle/shared.ts +73 -0
  168. package/src/cloudflare/bundle/{nodejs-compat-mode.ts → validate-node-compat.ts} +41 -16
  169. package/src/cloudflare/container.ts +296 -0
  170. package/src/cloudflare/d1-migrations.ts +29 -16
  171. package/src/cloudflare/d1-state-store.ts +105 -0
  172. package/src/cloudflare/do-state-store/internal.ts +1 -1
  173. package/src/cloudflare/do-state-store/store.ts +1 -1
  174. package/src/cloudflare/index.ts +3 -2
  175. package/src/cloudflare/{bundle/local-dev-cloudflare-shim.ts → local-dev-cloudflare-shim.ts} +1 -1
  176. package/src/cloudflare/{worker → miniflare}/miniflare-worker-options.ts +14 -6
  177. package/src/cloudflare/{worker → miniflare}/miniflare.ts +1 -1
  178. package/src/cloudflare/{worker → miniflare}/mixed-mode.ts +1 -2
  179. package/src/cloudflare/user.ts +1 -1
  180. package/src/cloudflare/worker/{subdomain.ts → shared.ts} +14 -0
  181. package/src/cloudflare/worker/tail.ts +287 -0
  182. package/src/cloudflare/worker-metadata.ts +18 -26
  183. package/src/cloudflare/worker-stub.ts +9 -7
  184. package/src/cloudflare/worker-subdomain.ts +123 -0
  185. package/src/cloudflare/worker.ts +863 -760
  186. package/src/fs/file-system-state-store.ts +1 -1
  187. package/src/scope.ts +30 -11
  188. package/src/serde.ts +4 -4
  189. package/src/sqlite/base-sqlite-state-store.ts +199 -0
  190. package/src/sqlite/index.ts +1 -0
  191. package/src/sqlite/schema.ts +37 -0
  192. package/src/sqlite/sqlite-state-store.ts +174 -0
  193. package/src/test/bun.ts +0 -8
  194. package/src/test/vitest.ts +19 -8
  195. package/src/util/memoize.ts +13 -1
  196. package/templates/astro/types/env.d.ts +1 -1
  197. package/templates/nuxt/types/env.d.ts +1 -1
  198. package/templates/react-router/types/env.d.ts +1 -1
  199. package/templates/rwsdk/types/env.d.ts +1 -1
  200. package/templates/sveltekit/types/env.d.ts +1 -1
  201. package/templates/tanstack-start/types/env.d.ts +1 -1
  202. package/templates/typescript/types/env.d.ts +1 -1
  203. package/templates/vite/types/env.d.ts +1 -1
  204. package/lib/cloudflare/bundle/alias-plugin.d.ts +0 -6
  205. package/lib/cloudflare/bundle/alias-plugin.d.ts.map +0 -1
  206. package/lib/cloudflare/bundle/alias-plugin.js +0 -36
  207. package/lib/cloudflare/bundle/alias-plugin.js.map +0 -1
  208. package/lib/cloudflare/bundle/build-failures.d.ts +0 -12
  209. package/lib/cloudflare/bundle/build-failures.d.ts.map +0 -1
  210. package/lib/cloudflare/bundle/build-failures.js +0 -45
  211. package/lib/cloudflare/bundle/build-failures.js.map +0 -1
  212. package/lib/cloudflare/bundle/bundle-worker-dev.d.ts +0 -26
  213. package/lib/cloudflare/bundle/bundle-worker-dev.d.ts.map +0 -1
  214. package/lib/cloudflare/bundle/bundle-worker-dev.js +0 -88
  215. package/lib/cloudflare/bundle/bundle-worker-dev.js.map +0 -1
  216. package/lib/cloudflare/bundle/bundle-worker.d.ts +0 -13
  217. package/lib/cloudflare/bundle/bundle-worker.d.ts.map +0 -1
  218. package/lib/cloudflare/bundle/bundle-worker.js +0 -98
  219. package/lib/cloudflare/bundle/bundle-worker.js.map +0 -1
  220. package/lib/cloudflare/bundle/external.d.ts +0 -4
  221. package/lib/cloudflare/bundle/external.d.ts.map +0 -1
  222. package/lib/cloudflare/bundle/external.js +0 -40
  223. package/lib/cloudflare/bundle/external.js.map +0 -1
  224. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts.map +0 -1
  225. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js.map +0 -1
  226. package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts.map +0 -1
  227. package/lib/cloudflare/bundle/nodejs-compat-mode.js.map +0 -1
  228. package/lib/cloudflare/bundle/nodejs-compat.d.ts.map +0 -1
  229. package/lib/cloudflare/bundle/nodejs-compat.js.map +0 -1
  230. package/lib/cloudflare/bundle/nodejs-import-warning-plugin.d.ts.map +0 -1
  231. package/lib/cloudflare/bundle/nodejs-import-warning-plugin.js.map +0 -1
  232. package/lib/cloudflare/bundle/wasm-plugin.d.ts +0 -3
  233. package/lib/cloudflare/bundle/wasm-plugin.d.ts.map +0 -1
  234. package/lib/cloudflare/bundle/wasm-plugin.js +0 -53
  235. package/lib/cloudflare/bundle/wasm-plugin.js.map +0 -1
  236. package/lib/cloudflare/worker/get-worker-template.d.ts +0 -2
  237. package/lib/cloudflare/worker/get-worker-template.d.ts.map +0 -1
  238. package/lib/cloudflare/worker/get-worker-template.js.map +0 -1
  239. package/lib/cloudflare/worker/http-server.d.ts.map +0 -1
  240. package/lib/cloudflare/worker/http-server.js.map +0 -1
  241. package/lib/cloudflare/worker/miniflare-worker-options.d.ts.map +0 -1
  242. package/lib/cloudflare/worker/miniflare-worker-options.js.map +0 -1
  243. package/lib/cloudflare/worker/miniflare.d.ts.map +0 -1
  244. package/lib/cloudflare/worker/miniflare.js.map +0 -1
  245. package/lib/cloudflare/worker/mixed-mode.d.ts.map +0 -1
  246. package/lib/cloudflare/worker/mixed-mode.js.map +0 -1
  247. package/lib/cloudflare/worker/subdomain.d.ts +0 -3
  248. package/lib/cloudflare/worker/subdomain.d.ts.map +0 -1
  249. package/lib/cloudflare/worker/subdomain.js +0 -10
  250. package/lib/cloudflare/worker/subdomain.js.map +0 -1
  251. package/src/cloudflare/bundle/alias-plugin.ts +0 -48
  252. package/src/cloudflare/bundle/build-failures.ts +0 -55
  253. package/src/cloudflare/bundle/bundle-worker-dev.ts +0 -129
  254. package/src/cloudflare/bundle/bundle-worker.ts +0 -150
  255. package/src/cloudflare/bundle/external.ts +0 -41
  256. package/src/cloudflare/bundle/wasm-plugin.ts +0 -57
  257. package/src/cloudflare/worker/get-worker-template.ts +0 -14
  258. /package/lib/cloudflare/{bundle/local-dev-cloudflare-shim.d.ts → local-dev-cloudflare-shim.d.ts} +0 -0
  259. /package/lib/cloudflare/{worker → miniflare}/http-server.d.ts +0 -0
  260. /package/lib/cloudflare/{worker → miniflare}/http-server.js +0 -0
  261. /package/lib/cloudflare/{worker → miniflare}/miniflare.d.ts +0 -0
  262. /package/lib/cloudflare/{worker → miniflare}/mixed-mode.d.ts +0 -0
  263. /package/src/cloudflare/{worker → miniflare}/http-server.ts +0 -0
@@ -0,0 +1,105 @@
1
+ import type { RemoteCallback } from "drizzle-orm/sqlite-proxy";
2
+ import { drizzle } from "drizzle-orm/sqlite-proxy";
3
+ import assert from "node:assert";
4
+ import type { Scope } from "../scope.ts";
5
+ import {
6
+ BaseSQLiteStateStore,
7
+ resolveMigrationsPath,
8
+ } from "../sqlite/base-sqlite-state-store.ts";
9
+ import * as schema from "../sqlite/schema.ts";
10
+ import { memoize } from "../util/memoize.ts";
11
+ import type { CloudflareApi, CloudflareApiOptions } from "./api.ts";
12
+
13
+ export interface D1StateStoreOptions extends CloudflareApiOptions {
14
+ databaseName?: string;
15
+ }
16
+
17
+ type D1Response =
18
+ | {
19
+ success: true;
20
+ result: {
21
+ results: { columns: string[]; rows: any[][] };
22
+ }[];
23
+ }
24
+ | {
25
+ success: false;
26
+ errors: { code: number; message: string }[];
27
+ };
28
+
29
+ export class D1StateStore extends BaseSQLiteStateStore {
30
+ constructor(scope: Scope, options: D1StateStoreOptions = {}) {
31
+ super(scope, {
32
+ create: async () => createDatabaseClient(options),
33
+ });
34
+ }
35
+ }
36
+
37
+ const createDatabaseClient = memoize(async (options: D1StateStoreOptions) => {
38
+ const { createCloudflareApi } = await import("./api.ts");
39
+ const api = await createCloudflareApi(options);
40
+ const database = await upsertDatabase(
41
+ api,
42
+ options.databaseName ?? "alchemy-state",
43
+ );
44
+ const remoteCallback: RemoteCallback = async (sql, params) => {
45
+ const res = await api.post(
46
+ `/accounts/${api.accountId}/d1/database/${database.id}/raw`,
47
+ { sql, params },
48
+ {
49
+ headers: {
50
+ "Content-Type": "application/json",
51
+ },
52
+ },
53
+ );
54
+ const data = (await res.json()) as D1Response;
55
+ if (!data.success) {
56
+ throw new Error(
57
+ data.errors.map((it) => `${it.code}: ${it.message}`).join("\n"),
58
+ );
59
+ }
60
+ const [result] = data.result;
61
+ assert(result, "Missing result");
62
+ return {
63
+ rows: Object.values(result.results.rows),
64
+ };
65
+ };
66
+ return drizzle(remoteCallback, {
67
+ schema,
68
+ });
69
+ });
70
+
71
+ const upsertDatabase = async (api: CloudflareApi, databaseName: string) => {
72
+ const { listDatabases, createDatabase } = await import("./d1-database.ts");
73
+ const { applyMigrations, listMigrationsFiles } = await import(
74
+ "./d1-migrations.ts"
75
+ );
76
+ const databases = await listDatabases(api, databaseName);
77
+ if (databases[0]) {
78
+ await applyMigrations({
79
+ migrationsFiles: await listMigrationsFiles(resolveMigrationsPath()),
80
+ migrationsTable: "migrations",
81
+ accountId: api.accountId,
82
+ databaseId: databases[0].id,
83
+ api,
84
+ quiet: true,
85
+ });
86
+ return {
87
+ id: databases[0].id,
88
+ };
89
+ }
90
+ const res = await createDatabase(api, databaseName, {
91
+ readReplication: { mode: "disabled" },
92
+ });
93
+ assert(res.result.uuid, "Missing UUID for database");
94
+ await applyMigrations({
95
+ migrationsFiles: await listMigrationsFiles(resolveMigrationsPath()),
96
+ migrationsTable: "migrations",
97
+ accountId: api.accountId,
98
+ databaseId: res.result.uuid,
99
+ api,
100
+ quiet: true,
101
+ });
102
+ return {
103
+ id: res.result.uuid,
104
+ };
105
+ };
@@ -3,7 +3,7 @@ import { withExponentialBackoff } from "../../util/retry.ts";
3
3
  import { handleApiError } from "../api-error.ts";
4
4
  import type { CloudflareApi } from "../api.ts";
5
5
  import type { WorkerMetadata } from "../worker-metadata.ts";
6
- import { getWorkerTemplate } from "../worker/get-worker-template.ts";
6
+ import { getWorkerTemplate } from "../worker/shared.ts";
7
7
  import type { DOStateStoreAPI } from "./types.ts";
8
8
 
9
9
  interface DOStateStoreClientOptions {
@@ -5,7 +5,7 @@ import { serialize } from "../../serde.ts";
5
5
  import type { State, StateStore } from "../../state.ts";
6
6
  import { deserialize } from "../../serde.ts";
7
7
  import { createCloudflareApi, type CloudflareApiOptions } from "../api.ts";
8
- import { getAccountSubdomain } from "../worker/subdomain.ts";
8
+ import { getAccountSubdomain } from "../worker/shared.ts";
9
9
  import { DOStateStoreClient, upsertStateStoreWorker } from "./internal.ts";
10
10
 
11
11
  export interface DOStateStoreOptions extends CloudflareApiOptions {
@@ -11,8 +11,6 @@ export * from "./bindings.ts";
11
11
  export * from "./bound.ts";
12
12
  export * from "./browser-rendering.ts";
13
13
  export * from "./bucket.ts";
14
- export * from "./bundle/external.ts";
15
- export * from "./bundle/local-dev-cloudflare-shim.ts";
16
14
  export * from "./certificate-pack.ts";
17
15
  export * from "./container.ts";
18
16
  export * from "./custom-domain.ts";
@@ -20,6 +18,7 @@ export * from "./d1-clone.ts";
20
18
  export * from "./d1-database.ts";
21
19
  export * from "./d1-export.ts";
22
20
  export * from "./d1-import.ts";
21
+ export * from "./d1-state-store.ts";
23
22
  export * from "./dispatch-namespace.ts";
24
23
  export * from "./dns-records.ts";
25
24
  export * from "./do-state-store/index.ts";
@@ -31,6 +30,7 @@ export * from "./email-rule.ts";
31
30
  export * from "./hyperdrive.ts";
32
31
  export * from "./images.ts";
33
32
  export * from "./kv-namespace.ts";
33
+ export * from "./local-dev-cloudflare-shim.ts";
34
34
  export * from "./nuxt.ts";
35
35
  export * from "./orange.ts";
36
36
  export * from "./permission-groups.ts";
@@ -53,6 +53,7 @@ export * from "./version-metadata.ts";
53
53
  export * from "./vite.ts";
54
54
  export * from "./website.ts";
55
55
  export * from "./worker-stub.ts";
56
+ export * from "./worker-subdomain.ts";
56
57
  export * from "./worker.ts";
57
58
  export { Workflow } from "./workflow.ts";
58
59
  export * from "./wrangler.json.ts";
@@ -1,4 +1,4 @@
1
- import { dedent } from "../../util/dedent.ts";
1
+ import { dedent } from "../util/dedent.ts";
2
2
 
3
3
  /**
4
4
  * TanStackStart server functions and middleware run in Node.js intead of Miniflare when using `vite dev`.
@@ -7,6 +7,7 @@ import assert from "node:assert";
7
7
  import { assertNever } from "../../util/assert-never.ts";
8
8
  import { logger } from "../../util/logger.ts";
9
9
  import { Self, type Binding, type WorkerBindingSpec } from "../bindings.ts";
10
+ import type { WorkerBundle } from "../bundle/index.ts";
10
11
  import type { WorkerProps } from "../worker.ts";
11
12
 
12
13
  export type MiniflareWorkerOptions = Pick<
@@ -18,7 +19,7 @@ export type MiniflareWorkerOptions = Pick<
18
19
  | "format"
19
20
  > & {
20
21
  name: string;
21
- script: string;
22
+ bundle: WorkerBundle;
22
23
  port?: number;
23
24
  };
24
25
 
@@ -206,9 +207,9 @@ function buildRemoteBinding(
206
207
  }
207
208
  }
208
209
 
209
- export function buildMiniflareWorkerOptions({
210
+ export async function buildMiniflareWorkerOptions({
210
211
  name: workerName,
211
- script,
212
+ bundle,
212
213
  bindings,
213
214
  format,
214
215
  eventSources,
@@ -217,11 +218,18 @@ export function buildMiniflareWorkerOptions({
217
218
  remoteProxyConnectionString,
218
219
  }: MiniflareWorkerOptions & {
219
220
  remoteProxyConnectionString: RemoteProxyConnectionString | undefined;
220
- }): WorkerOptions {
221
+ }): Promise<WorkerOptions> {
221
222
  const options: WorkerOptions = {
222
223
  name: workerName,
223
- script,
224
- modules: format !== "cjs",
224
+ modules: await Promise.all(
225
+ bundle.files.map(async (file) => ({
226
+ type: (format === "cjs" ? "CommonJS" : "ESModule") as
227
+ | "CommonJS"
228
+ | "ESModule",
229
+ path: file.name,
230
+ contents: await file.text(),
231
+ })),
232
+ ),
225
233
  compatibilityDate,
226
234
  compatibilityFlags,
227
235
  unsafeDirectSockets: [{ entrypoint: undefined, proxy: true }],
@@ -60,7 +60,7 @@ class MiniflareServer {
60
60
  private async set(worker: MiniflareWorkerOptions) {
61
61
  this.workers.set(
62
62
  worker.name as string,
63
- buildMiniflareWorkerOptions({
63
+ await buildMiniflareWorkerOptions({
64
64
  ...worker,
65
65
  remoteProxyConnectionString:
66
66
  await this.maybeCreateMixedModeProxy(worker),
@@ -3,9 +3,8 @@ import { createCloudflareApi, type CloudflareApi } from "../api.ts";
3
3
  import type { WorkerBindingSpec } from "../bindings.ts";
4
4
  import type { CloudflareApiResponse } from "../types.ts";
5
5
  import type { WorkerMetadata } from "../worker-metadata.ts";
6
- import { getWorkerTemplate } from "./get-worker-template.ts";
6
+ import { getAccountSubdomain, getWorkerTemplate } from "../worker/shared.ts";
7
7
  import { HTTPServer } from "./http-server.ts";
8
- import { getAccountSubdomain } from "./subdomain.ts";
9
8
 
10
9
  type WranglerSessionConfig =
11
10
  | {
@@ -81,7 +81,7 @@ export async function getCloudflareAccounts(
81
81
  return (accountCache[cacheKey] ??= ((await accounts.json()) as any).result);
82
82
  } else {
83
83
  throw new CloudflareApiError(
84
- `Failed to get accounts for authorized user, please make sure you're authenticated (see: https://alchemy.run/docs/guides/cloudflare-auth.html) or explicitly set the Cloudflare Account ID (see: https://alchemy.run/docs/guides/cloudflare-auth.html#account-id)`,
84
+ `Failed to get accounts for authorized user, please make sure you're authenticated (see: https://alchemy.run/guides/cloudflare/) or explicitly set the Cloudflare Account ID (see: https://alchemy.run/guides/cloudflare/#account-id)`,
85
85
  accounts,
86
86
  );
87
87
  }
@@ -1,3 +1,6 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
1
4
  import { memoize } from "../../util/memoize.ts";
2
5
  import type { CloudflareApi } from "../api.ts";
3
6
 
@@ -14,3 +17,14 @@ export const getAccountSubdomain = memoize(
14
17
  },
15
18
  (api) => api.accountId,
16
19
  );
20
+
21
+ export async function getWorkerTemplate(
22
+ name: "do-state-store" | "mixed-mode-proxy-worker",
23
+ ) {
24
+ const dir = dirname(fileURLToPath(import.meta.url));
25
+ const path = join(dir, "..", "..", "..", "workers", `${name}.js`);
26
+ const template = await readFile(path, "utf8");
27
+ return new File([template], `${name}.js`, {
28
+ type: "application/javascript+module",
29
+ });
30
+ }
@@ -0,0 +1,287 @@
1
+ import kleur from "kleur";
2
+ import { WebSocket } from "ws";
3
+ import { logger } from "../../util/logger.ts";
4
+ import type { CloudflareApi } from "../api.ts";
5
+ import type { CloudflareApiResponse } from "../types.ts";
6
+
7
+ export const createTail = async (
8
+ api: CloudflareApi,
9
+ id: string,
10
+ scriptName: string,
11
+ ) => {
12
+ const tail = new TailClient(api, id, scriptName);
13
+ await tail.connect();
14
+ return tail;
15
+ };
16
+
17
+ interface Tail {
18
+ id: string;
19
+ url: string;
20
+ expires_at: Date;
21
+ }
22
+
23
+ const DEBUG = !!process.env.DEBUG;
24
+
25
+ class TailClient {
26
+ private api: CloudflareApi;
27
+ private id: string;
28
+ private scriptName: string;
29
+
30
+ private tail?: Tail;
31
+ private ws?: WebSocket;
32
+ private pingInterval?: NodeJS.Timeout;
33
+ private clean = false;
34
+
35
+ constructor(api: CloudflareApi, id: string, scriptName: string) {
36
+ this.api = api;
37
+ this.id = id;
38
+ this.scriptName = scriptName;
39
+ }
40
+
41
+ private async create() {
42
+ if (
43
+ this.tail &&
44
+ this.tail.expires_at > new Date(Date.now() + 1000 * 60 * 5)
45
+ ) {
46
+ return this.tail;
47
+ }
48
+ const res = await this.api.post(
49
+ `/accounts/${this.api.accountId}/workers/scripts/${this.scriptName}/tails`,
50
+ {},
51
+ );
52
+ const json = (await res.json()) as CloudflareApiResponse<Tail>;
53
+ if (!json.success) {
54
+ throw new Error(
55
+ `Failed to create tail for ${this.scriptName} (${res.status}): ${json.errors.map((e) => `${e.code} - ${e.message}`).join("\n")}`,
56
+ );
57
+ }
58
+ this.tail = {
59
+ id: json.result.id,
60
+ url: json.result.url,
61
+ expires_at: new Date(json.result.expires_at),
62
+ };
63
+ return this.tail;
64
+ }
65
+
66
+ async connect(attempt = 0) {
67
+ if (this.ws) {
68
+ throw new Error(`Tail already connected for ${this.scriptName}`);
69
+ }
70
+ if (attempt > 3) {
71
+ await this.close();
72
+ throw new Error(`Failed to connect to tail for ${this.scriptName}`);
73
+ }
74
+ const tail = await this.create();
75
+ const ws = new WebSocket(tail.url, {
76
+ headers: {
77
+ "Sec-WebSocket-Protocol": "trace-v1",
78
+ },
79
+ });
80
+ ws.on("open", () => {
81
+ this.ping(ws);
82
+ if (DEBUG) {
83
+ logger.log(`connected to tail for "${this.scriptName}"`);
84
+ }
85
+ });
86
+ ws.on("close", () => {
87
+ this.ws = undefined;
88
+ if (DEBUG) {
89
+ logger.log(`closed tail for "${this.scriptName}"`);
90
+ }
91
+ if (this.clean) {
92
+ return;
93
+ }
94
+ logger.log(`reconnecting to tail for "${this.scriptName}"`);
95
+ this.connect(attempt + 1);
96
+ });
97
+ ws.on("error", (event) => {
98
+ logger.error(`error on tail for "${this.scriptName}": ${event}`);
99
+ });
100
+ ws.on("message", (message) => {
101
+ const data: TailEventMessage = JSON.parse(message.toString());
102
+ const prefix = kleur.blue(`[${this.id}]`);
103
+ if (data.event && "request" in data.event) {
104
+ // TODO: handle other event types
105
+ const event = data.event;
106
+ const url = new URL(event.request.url);
107
+ const status = event.response?.status ?? 500;
108
+ // TODO: make this look nicer
109
+ logger.log(
110
+ `${prefix} ${kleur.gray(event.request.method)} ${url.pathname} ${kleur.dim(">")} ${status >= 200 && status < 300 ? kleur.green(status) : kleur.red(status)} ${kleur.gray(`(cpu: ${Math.round(data.cpuTime)}ms, wall: ${Math.round(data.wallTime)}ms)`)}`,
111
+ );
112
+ }
113
+ for (const log of data.logs) {
114
+ logger.log(
115
+ `${prefix} ${kleur.gray(log.level)} ${log.message.join(" ")}`,
116
+ );
117
+ }
118
+ for (const exception of data.exceptions) {
119
+ const start = `${prefix} ${kleur.red(exception.name)}`;
120
+ logger.log(
121
+ `${start} ${exception.message}\n${kleur.gray(exception.stack)}`,
122
+ );
123
+ }
124
+ });
125
+ }
126
+
127
+ private async ping(ws: WebSocket) {
128
+ let waitingForPong = false;
129
+
130
+ const cleanup = () => {
131
+ if (!this.pingInterval) {
132
+ return;
133
+ }
134
+ clearInterval(this.pingInterval);
135
+ this.pingInterval = undefined;
136
+ };
137
+
138
+ this.pingInterval = setInterval(() => {
139
+ if (ws.readyState !== WebSocket.OPEN) {
140
+ cleanup();
141
+ return;
142
+ }
143
+ if (waitingForPong) {
144
+ ws.close();
145
+ }
146
+ waitingForPong = true;
147
+ ws.ping();
148
+ }, 10_000);
149
+
150
+ ws.on("pong", () => {
151
+ waitingForPong = false;
152
+ });
153
+ }
154
+
155
+ async close() {
156
+ this.clean = true;
157
+ if (this.pingInterval) {
158
+ clearInterval(this.pingInterval);
159
+ this.pingInterval = undefined;
160
+ }
161
+ if (this.ws?.readyState === WebSocket.OPEN) {
162
+ this.ws.close();
163
+ this.ws = undefined;
164
+ }
165
+ if (this.tail) {
166
+ const res = await this.api.delete(
167
+ `/accounts/${this.api.accountId}/workers/scripts/${this.scriptName}/tails/${this.tail.id}`,
168
+ );
169
+ if (!res.ok) {
170
+ throw new Error(
171
+ `Failed to close tail for ${this.scriptName} (${res.status}): ${res.statusText}`,
172
+ );
173
+ }
174
+ this.tail = undefined;
175
+ if (DEBUG) {
176
+ logger.log(`closed tail for "${this.scriptName}": ${res.status}`);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ interface TailEventMessage {
182
+ wallTime: number;
183
+ cpuTime: number;
184
+ truncated: boolean;
185
+ executionModel: "stateless" | "durableObject";
186
+ outcome:
187
+ | "ok"
188
+ | "canceled"
189
+ | "exception"
190
+ | "exceededCpu"
191
+ | "exceededMemory"
192
+ | "unknown";
193
+ scriptTags: string[];
194
+ scriptVersion: { id: string };
195
+ scriptName: string;
196
+ diagnosticsChannelEvents: unknown[];
197
+ exceptions: {
198
+ name: string;
199
+ message: string;
200
+ stack: string;
201
+ timestamp: string;
202
+ }[];
203
+ logs: {
204
+ message: string[];
205
+ level: string;
206
+ timestamp: string;
207
+ }[];
208
+ event: TailEventMessage.Event;
209
+ }
210
+
211
+ namespace TailEventMessage {
212
+ export type Event =
213
+ | RequestEvent
214
+ | ScheduledEvent
215
+ | EmailEvent
216
+ | QueueEvent
217
+ | RpcEvent
218
+ | TailEvent
219
+ | TailInfoEvent
220
+ | null
221
+ | undefined;
222
+
223
+ export interface RequestEvent {
224
+ request: {
225
+ method: string;
226
+ url: string;
227
+ headers: Record<string, string>;
228
+ body: string;
229
+ };
230
+ response?: { status: number };
231
+ }
232
+
233
+ export interface ScheduledEvent {
234
+ cron: string;
235
+ scheduledTime: string;
236
+ }
237
+
238
+ export interface EmailEvent {
239
+ /**
240
+ * Who sent the email
241
+ */
242
+ mailFrom: string;
243
+
244
+ /**
245
+ * Who was the email sent to
246
+ */
247
+ rcptTo: string;
248
+
249
+ /**
250
+ * Size of the email in bytes
251
+ */
252
+ rawSize: number;
253
+ }
254
+
255
+ /**
256
+ * An event that was triggered for a tail receiving TailEventMessages
257
+ * Only seen when tailing a tail worker
258
+ */
259
+ export interface TailEvent {
260
+ /**
261
+ * A minimal representation of the TailEventMessages that were delivered to the tail handler
262
+ */
263
+ consumedEvents: {
264
+ /**
265
+ * The name of script being tailed
266
+ */
267
+ scriptName?: string;
268
+ }[];
269
+ }
270
+
271
+ /**
272
+ * Message from tail with information about the tail itself
273
+ */
274
+ export interface TailInfoEvent {
275
+ message: string;
276
+ type: string;
277
+ }
278
+
279
+ export interface QueueEvent {
280
+ queue: string;
281
+ batchSize: number;
282
+ }
283
+
284
+ export interface RpcEvent {
285
+ rpcMethod: string;
286
+ }
287
+ }
@@ -1,6 +1,6 @@
1
- import path from "node:path";
2
1
  import { assertNever } from "../util/assert-never.ts";
3
2
  import { logger } from "../util/logger.ts";
3
+ import { memoize } from "../util/memoize.ts";
4
4
  import type { CloudflareApi } from "./api.ts";
5
5
  import {
6
6
  Self,
@@ -13,7 +13,7 @@ import {
13
13
  isDurableObjectNamespace,
14
14
  type DurableObjectNamespace,
15
15
  } from "./durable-object-namespace.ts";
16
- import { createAssetConfig, type AssetUploadResult } from "./worker-assets.ts";
16
+ import { createAssetConfig } from "./worker-assets.ts";
17
17
  import type {
18
18
  MultiStepMigration,
19
19
  SingleStepMigration,
@@ -205,10 +205,17 @@ export async function prepareWorkerMetadata(
205
205
  compatibilityFlags: string[];
206
206
  workerName: string;
207
207
  migrationTag?: string;
208
- assetUploadResult?: AssetUploadResult;
208
+ assetUploadResult?: {
209
+ completionToken?: string;
210
+ keepAssets?: boolean;
211
+ assetConfig?: AssetsConfig;
212
+ };
213
+ unstable_cacheWorkerSettings?: boolean;
209
214
  },
210
215
  ): Promise<WorkerMetadata> {
211
- const oldSettings = await getWorkerSettings(api, props.workerName);
216
+ const oldSettings = await (props.unstable_cacheWorkerSettings
217
+ ? getWorkerSettingsWithCache
218
+ : getWorkerSettings)(api, props.workerName);
212
219
  const oldTags: string[] | undefined = Array.from(
213
220
  new Set([
214
221
  ...(oldSettings?.default_environment?.script?.tags ?? []),
@@ -333,15 +340,10 @@ export async function prepareWorkerMetadata(
333
340
  if (assetUploadResult) {
334
341
  meta.assets = {
335
342
  jwt: assetUploadResult.completionToken,
343
+ keep_assets: assetUploadResult.keepAssets,
344
+ config: assetUploadResult.assetConfig,
336
345
  };
337
346
 
338
- // Initialize config from assetUploadResult if it exists
339
- if (assetUploadResult.assetConfig) {
340
- meta.assets.config = {
341
- ...assetUploadResult.assetConfig,
342
- };
343
- }
344
-
345
347
  // If there's no config from assetUploadResult but we have props.assets,
346
348
  // we need to create the config ourselves (this handles the case when no assets were uploaded)
347
349
  if (!meta.assets.config && props.assets) {
@@ -617,21 +619,6 @@ export async function prepareWorkerMetadata(
617
619
  }
618
620
  }
619
621
 
620
- // Determine if we're using ESM or service worker format
621
- const isEsModule = props.format !== "cjs"; // Default to ESM unless CJS is specified
622
- const scriptName = props.noBundle
623
- ? path.basename(props.entrypoint!)
624
- : isEsModule
625
- ? "worker.js"
626
- : "script";
627
-
628
- if (isEsModule) {
629
- // For ES modules format
630
- meta.main_module = scriptName;
631
- } else {
632
- // For service worker format (CJS)
633
- meta.body_part = scriptName;
634
- }
635
622
  if (process.env.DEBUG) {
636
623
  logger.log(meta);
637
624
  }
@@ -656,6 +643,11 @@ interface WorkerSettings {
656
643
  [key: string]: any;
657
644
  }
658
645
 
646
+ const getWorkerSettingsWithCache = memoize(
647
+ getWorkerSettings,
648
+ (api, workerName) => `${api.accountId}:${workerName}`,
649
+ );
650
+
659
651
  async function getWorkerSettings(
660
652
  api: CloudflareApi,
661
653
  workerName: string,
@@ -7,7 +7,7 @@ import {
7
7
  type CloudflareApi,
8
8
  type CloudflareApiOptions,
9
9
  } from "./api.ts";
10
- import { configureURL } from "./worker.ts";
10
+ import { WorkerSubdomain } from "./worker-subdomain.ts";
11
11
 
12
12
  /**
13
13
  * Properties for creating a Worker stub
@@ -109,18 +109,20 @@ export const WorkerStub = Resource("cloudflare::WorkerStub", async function <
109
109
  }
110
110
 
111
111
  // Configure URL if requested (defaults to true)
112
- const workerUrl = await configureURL(this, api, {
113
- ...props,
114
- workerName: props.name!,
115
- url: props.url ?? true,
116
- });
112
+ const subdomain =
113
+ props.url !== false
114
+ ? await WorkerSubdomain("url", {
115
+ ...props,
116
+ scriptName: props.name,
117
+ })
118
+ : undefined;
117
119
 
118
120
  // Return the worker stub info
119
121
  return this({
120
122
  type: "service",
121
123
  __rpc__: props.rpc as unknown as RPC,
122
124
  ...props,
123
- url: workerUrl,
125
+ url: subdomain?.url,
124
126
  }) as WorkerStub<RPC>;
125
127
  });
126
128