alchemy 2.0.0-beta.50 → 2.0.0-beta.52

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 (254) hide show
  1. package/bin/exec.js +133 -135
  2. package/bin/exec.js.map +1 -1
  3. package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
  4. package/lib/AWS/Lambda/BucketEventSource.js +13 -2
  5. package/lib/AWS/Lambda/BucketEventSource.js.map +1 -1
  6. package/lib/AWS/S3/Bucket.d.ts.map +1 -1
  7. package/lib/AWS/S3/Bucket.js +49 -0
  8. package/lib/AWS/S3/Bucket.js.map +1 -1
  9. package/lib/AWS/S3/BucketNotifications.d.ts +10 -0
  10. package/lib/AWS/S3/BucketNotifications.d.ts.map +1 -1
  11. package/lib/AWS/S3/BucketNotifications.js.map +1 -1
  12. package/lib/Apply.js +15 -3
  13. package/lib/Apply.js.map +1 -1
  14. package/lib/Auth/AuthProvider.js +1 -1
  15. package/lib/Auth/AuthProvider.js.map +1 -1
  16. package/lib/Binding.d.ts.map +1 -1
  17. package/lib/Binding.js +12 -8
  18. package/lib/Binding.js.map +1 -1
  19. package/lib/Build/DevServer.d.ts +113 -0
  20. package/lib/Build/DevServer.d.ts.map +1 -0
  21. package/lib/Build/DevServer.js +212 -0
  22. package/lib/Build/DevServer.js.map +1 -0
  23. package/lib/Build/Local.d.ts +2 -0
  24. package/lib/Build/Local.d.ts.map +1 -0
  25. package/lib/Build/Local.js +4 -0
  26. package/lib/Build/Local.js.map +1 -0
  27. package/lib/Bundle/Bundle.js +1 -1
  28. package/lib/Bundle/Bundle.js.map +1 -1
  29. package/lib/Cli/commands/cloudflare.d.ts +1 -1
  30. package/lib/Cloudflare/Access.js +1 -1
  31. package/lib/Cloudflare/Access.js.map +1 -1
  32. package/lib/Cloudflare/AiGateway/AiGateway.d.ts +1 -1
  33. package/lib/Cloudflare/AiGateway/AiGateway.js +1 -1
  34. package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts +1 -1
  35. package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts.map +1 -1
  36. package/lib/Cloudflare/AiGateway/AiGatewayBinding.js +6 -2
  37. package/lib/Cloudflare/AiGateway/AiGatewayBinding.js.map +1 -1
  38. package/lib/Cloudflare/Browser/Browser.d.ts +84 -12
  39. package/lib/Cloudflare/Browser/Browser.d.ts.map +1 -1
  40. package/lib/Cloudflare/Browser/Browser.js +80 -9
  41. package/lib/Cloudflare/Browser/Browser.js.map +1 -1
  42. package/lib/Cloudflare/Browser/BrowserBinding.d.ts +90 -16
  43. package/lib/Cloudflare/Browser/BrowserBinding.d.ts.map +1 -1
  44. package/lib/Cloudflare/Browser/BrowserBinding.js +41 -6
  45. package/lib/Cloudflare/Browser/BrowserBinding.js.map +1 -1
  46. package/lib/Cloudflare/Browser/index.d.ts +1 -1
  47. package/lib/Cloudflare/Browser/index.d.ts.map +1 -1
  48. package/lib/Cloudflare/Browser/index.js.map +1 -1
  49. package/lib/Cloudflare/Container/ContainerBinding.d.ts.map +1 -1
  50. package/lib/Cloudflare/Container/ContainerBinding.js +2 -4
  51. package/lib/Cloudflare/Container/ContainerBinding.js.map +1 -1
  52. package/lib/Cloudflare/Images/Images.d.ts +4 -3
  53. package/lib/Cloudflare/Images/Images.d.ts.map +1 -1
  54. package/lib/Cloudflare/Images/Images.js.map +1 -1
  55. package/lib/Cloudflare/Providers.d.ts +1 -1
  56. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  57. package/lib/Cloudflare/Providers.js +6 -1
  58. package/lib/Cloudflare/Providers.js.map +1 -1
  59. package/lib/Cloudflare/RateLimit/RateLimit.d.ts +4 -3
  60. package/lib/Cloudflare/RateLimit/RateLimit.d.ts.map +1 -1
  61. package/lib/Cloudflare/RateLimit/RateLimit.js.map +1 -1
  62. package/lib/Cloudflare/Ruleset/Ruleset.d.ts +82 -0
  63. package/lib/Cloudflare/Ruleset/Ruleset.d.ts.map +1 -0
  64. package/lib/Cloudflare/Ruleset/Ruleset.js +126 -0
  65. package/lib/Cloudflare/Ruleset/Ruleset.js.map +1 -0
  66. package/lib/Cloudflare/Ruleset/index.d.ts +2 -0
  67. package/lib/Cloudflare/Ruleset/index.d.ts.map +1 -0
  68. package/lib/Cloudflare/Ruleset/index.js +2 -0
  69. package/lib/Cloudflare/Ruleset/index.js.map +1 -0
  70. package/lib/Cloudflare/SecretsStore/Secret.d.ts +1 -0
  71. package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
  72. package/lib/Cloudflare/SecretsStore/Secret.js +2 -0
  73. package/lib/Cloudflare/SecretsStore/Secret.js.map +1 -1
  74. package/lib/Cloudflare/StateStore/State.d.ts +1 -1
  75. package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
  76. package/lib/Cloudflare/Website/StaticSite.d.ts +64 -12
  77. package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
  78. package/lib/Cloudflare/Website/StaticSite.js +80 -30
  79. package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
  80. package/lib/Cloudflare/Website/Vite.d.ts +36 -7
  81. package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
  82. package/lib/Cloudflare/Website/Vite.js +26 -8
  83. package/lib/Cloudflare/Website/Vite.js.map +1 -1
  84. package/lib/Cloudflare/Workers/Assets.d.ts +10 -4
  85. package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
  86. package/lib/Cloudflare/Workers/Assets.js +6 -6
  87. package/lib/Cloudflare/Workers/Assets.js.map +1 -1
  88. package/lib/Cloudflare/Workers/DurableObjectChatPersistence.d.ts +69 -0
  89. package/lib/Cloudflare/Workers/DurableObjectChatPersistence.d.ts.map +1 -0
  90. package/lib/Cloudflare/Workers/DurableObjectChatPersistence.js +104 -0
  91. package/lib/Cloudflare/Workers/DurableObjectChatPersistence.js.map +1 -0
  92. package/lib/Cloudflare/Workers/DynamicWorkerLoader.d.ts +4 -4
  93. package/lib/Cloudflare/Workers/InferEnv.d.ts +1 -1
  94. package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
  95. package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
  96. package/lib/Cloudflare/Workers/LocalWorkerProvider.js +45 -11
  97. package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
  98. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.d.ts +5 -4
  99. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.d.ts.map +1 -1
  100. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.js +4 -2
  101. package/lib/Cloudflare/Workers/RpcDurableObjectNamespace.js.map +1 -1
  102. package/lib/Cloudflare/Workers/RpcWorker.d.ts +3 -2
  103. package/lib/Cloudflare/Workers/RpcWorker.d.ts.map +1 -1
  104. package/lib/Cloudflare/Workers/RpcWorker.js +5 -3
  105. package/lib/Cloudflare/Workers/RpcWorker.js.map +1 -1
  106. package/lib/Cloudflare/Workers/VersionMetadata.d.ts +99 -0
  107. package/lib/Cloudflare/Workers/VersionMetadata.d.ts.map +1 -0
  108. package/lib/Cloudflare/Workers/VersionMetadata.js +65 -0
  109. package/lib/Cloudflare/Workers/VersionMetadata.js.map +1 -0
  110. package/lib/Cloudflare/Workers/VersionMetadataBinding.d.ts +35 -0
  111. package/lib/Cloudflare/Workers/VersionMetadataBinding.d.ts.map +1 -0
  112. package/lib/Cloudflare/Workers/VersionMetadataBinding.js +31 -0
  113. package/lib/Cloudflare/Workers/VersionMetadataBinding.js.map +1 -0
  114. package/lib/Cloudflare/Workers/Worker.d.ts +20 -19
  115. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  116. package/lib/Cloudflare/Workers/Worker.js +23 -37
  117. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  118. package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
  119. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +16 -0
  120. package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
  121. package/lib/Cloudflare/Workers/WorkerBinding.d.ts +3 -1
  122. package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
  123. package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
  124. package/lib/Cloudflare/Workers/Workflow.d.ts +8 -1
  125. package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
  126. package/lib/Cloudflare/Workers/Workflow.js +1 -0
  127. package/lib/Cloudflare/Workers/Workflow.js.map +1 -1
  128. package/lib/Cloudflare/Workers/WorkflowBridge.d.ts.map +1 -1
  129. package/lib/Cloudflare/Workers/WorkflowBridge.js +31 -2
  130. package/lib/Cloudflare/Workers/WorkflowBridge.js.map +1 -1
  131. package/lib/Cloudflare/Workers/index.d.ts +3 -0
  132. package/lib/Cloudflare/Workers/index.d.ts.map +1 -1
  133. package/lib/Cloudflare/Workers/index.js +3 -0
  134. package/lib/Cloudflare/Workers/index.js.map +1 -1
  135. package/lib/Cloudflare/Zaraz/ZarazConfig.d.ts.map +1 -1
  136. package/lib/Cloudflare/index.d.ts +1 -0
  137. package/lib/Cloudflare/index.d.ts.map +1 -1
  138. package/lib/Cloudflare/index.js +1 -0
  139. package/lib/Cloudflare/index.js.map +1 -1
  140. package/lib/Deploy.d.ts +3 -2
  141. package/lib/Deploy.d.ts.map +1 -1
  142. package/lib/Deploy.js.map +1 -1
  143. package/lib/Destroy.d.ts +3 -2
  144. package/lib/Destroy.d.ts.map +1 -1
  145. package/lib/Destroy.js.map +1 -1
  146. package/lib/Diff.d.ts.map +1 -1
  147. package/lib/Diff.js +7 -0
  148. package/lib/Diff.js.map +1 -1
  149. package/lib/Drizzle/Postgres.d.ts +13 -12
  150. package/lib/Drizzle/Postgres.d.ts.map +1 -1
  151. package/lib/Drizzle/Postgres.js +13 -12
  152. package/lib/Drizzle/Postgres.js.map +1 -1
  153. package/lib/Drizzle/Schema.d.ts +1 -1
  154. package/lib/Drizzle/Schema.d.ts.map +1 -1
  155. package/lib/Drizzle/Schema.js +20 -9
  156. package/lib/Drizzle/Schema.js.map +1 -1
  157. package/lib/Local/RpcSerialization.js +5 -2
  158. package/lib/Local/RpcSerialization.js.map +1 -1
  159. package/lib/Output.d.ts.map +1 -1
  160. package/lib/Output.js +11 -10
  161. package/lib/Output.js.map +1 -1
  162. package/lib/Plan.d.ts.map +1 -1
  163. package/lib/Plan.js +5 -1
  164. package/lib/Plan.js.map +1 -1
  165. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts +0 -2
  166. package/lib/Planetscale/MySQL/MySQLDatabase.d.ts.map +1 -1
  167. package/lib/Planetscale/MySQL/MySQLDatabase.js +1 -3
  168. package/lib/Planetscale/MySQL/MySQLDatabase.js.map +1 -1
  169. package/lib/Planetscale/Postgres/PostgresDatabase.d.ts +0 -2
  170. package/lib/Planetscale/Postgres/PostgresDatabase.d.ts.map +1 -1
  171. package/lib/Planetscale/Postgres/PostgresDatabase.js +1 -3
  172. package/lib/Planetscale/Postgres/PostgresDatabase.js.map +1 -1
  173. package/lib/Platform.d.ts +4 -4
  174. package/lib/Platform.d.ts.map +1 -1
  175. package/lib/Platform.js +27 -25
  176. package/lib/Platform.js.map +1 -1
  177. package/lib/Resource.d.ts +7 -1
  178. package/lib/Resource.d.ts.map +1 -1
  179. package/lib/Resource.js +11 -12
  180. package/lib/Resource.js.map +1 -1
  181. package/lib/Runtime.d.ts +1 -1
  182. package/lib/Runtime.d.ts.map +1 -1
  183. package/lib/Runtime.js +3 -4
  184. package/lib/Runtime.js.map +1 -1
  185. package/lib/Stack.d.ts +5 -4
  186. package/lib/Stack.d.ts.map +1 -1
  187. package/lib/Stack.js.map +1 -1
  188. package/lib/Test/Core.d.ts +2 -2
  189. package/lib/Test/Core.d.ts.map +1 -1
  190. package/lib/Util/effect.d.ts.map +1 -1
  191. package/lib/Util/effect.js +13 -15
  192. package/lib/Util/effect.js.map +1 -1
  193. package/lib/Util/poll.d.ts +0 -1
  194. package/lib/Util/poll.d.ts.map +1 -1
  195. package/lib/Util/poll.js +2 -2
  196. package/lib/Util/poll.js.map +1 -1
  197. package/lib/tsconfig.test.tsbuildinfo +1 -1
  198. package/package.json +20 -20
  199. package/src/AWS/Lambda/BucketEventSource.ts +16 -1
  200. package/src/AWS/S3/Bucket.ts +78 -0
  201. package/src/AWS/S3/BucketNotifications.ts +10 -0
  202. package/src/Apply.ts +22 -3
  203. package/src/Auth/AuthProvider.ts +1 -1
  204. package/src/Binding.ts +45 -38
  205. package/src/Build/DevServer.ts +330 -0
  206. package/src/Build/Local.ts +4 -0
  207. package/src/Bundle/Bundle.ts +1 -1
  208. package/src/Cloudflare/Access.ts +1 -1
  209. package/src/Cloudflare/AiGateway/AiGateway.ts +1 -1
  210. package/src/Cloudflare/AiGateway/AiGatewayBinding.ts +8 -3
  211. package/src/Cloudflare/Browser/Browser.ts +84 -12
  212. package/src/Cloudflare/Browser/BrowserBinding.ts +205 -33
  213. package/src/Cloudflare/Browser/index.ts +15 -2
  214. package/src/Cloudflare/Container/ContainerBinding.ts +5 -10
  215. package/src/Cloudflare/Images/Images.ts +4 -3
  216. package/src/Cloudflare/Providers.ts +8 -0
  217. package/src/Cloudflare/RateLimit/RateLimit.ts +4 -3
  218. package/src/Cloudflare/Ruleset/Ruleset.ts +223 -0
  219. package/src/Cloudflare/Ruleset/index.ts +1 -0
  220. package/src/Cloudflare/SecretsStore/Secret.ts +4 -0
  221. package/src/Cloudflare/Website/StaticSite.ts +154 -41
  222. package/src/Cloudflare/Website/Vite.ts +73 -25
  223. package/src/Cloudflare/Workers/Assets.ts +10 -8
  224. package/src/Cloudflare/Workers/DurableObjectChatPersistence.ts +131 -0
  225. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +4 -4
  226. package/src/Cloudflare/Workers/InferEnv.ts +14 -12
  227. package/src/Cloudflare/Workers/LocalWorkerProvider.ts +54 -13
  228. package/src/Cloudflare/Workers/RpcDurableObjectNamespace.ts +11 -8
  229. package/src/Cloudflare/Workers/RpcWorker.ts +8 -7
  230. package/src/Cloudflare/Workers/VersionMetadata.ts +129 -0
  231. package/src/Cloudflare/Workers/VersionMetadataBinding.ts +82 -0
  232. package/src/Cloudflare/Workers/Worker.ts +66 -82
  233. package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +14 -0
  234. package/src/Cloudflare/Workers/WorkerBinding.ts +4 -0
  235. package/src/Cloudflare/Workers/Workflow.ts +12 -1
  236. package/src/Cloudflare/Workers/WorkflowBridge.ts +47 -6
  237. package/src/Cloudflare/Workers/index.ts +3 -0
  238. package/src/Cloudflare/index.ts +1 -0
  239. package/src/Deploy.ts +2 -1
  240. package/src/Destroy.ts +2 -1
  241. package/src/Diff.ts +7 -0
  242. package/src/Drizzle/Postgres.ts +13 -12
  243. package/src/Drizzle/Schema.ts +27 -9
  244. package/src/Local/RpcSerialization.ts +5 -2
  245. package/src/Output.ts +12 -10
  246. package/src/Plan.ts +5 -1
  247. package/src/Planetscale/MySQL/MySQLDatabase.ts +1 -6
  248. package/src/Planetscale/Postgres/PostgresDatabase.ts +1 -5
  249. package/src/Platform.ts +48 -36
  250. package/src/Resource.ts +22 -13
  251. package/src/Runtime.ts +3 -7
  252. package/src/Stack.ts +11 -10
  253. package/src/Util/effect.ts +13 -17
  254. package/src/Util/poll.ts +3 -3
@@ -1,12 +1,14 @@
1
1
  import type * as cf from "@cloudflare/workers-types";
2
2
  import * as workers from "@distilled.cloud/cloudflare/workers";
3
3
  import * as zones from "@distilled.cloud/cloudflare/zones";
4
+ import type { ConfigError } from "effect/Config";
4
5
  import * as Config from "effect/Config";
5
6
  import * as Context from "effect/Context";
6
7
  import * as Data from "effect/Data";
7
8
  import * as Effect from "effect/Effect";
8
9
  import * as Layer from "effect/Layer";
9
10
  import * as Path from "effect/Path";
11
+ import * as Predicate from "effect/Predicate";
10
12
  import * as Redacted from "effect/Redacted";
11
13
  import * as Schedule from "effect/Schedule";
12
14
  import * as crypto from "node:crypto";
@@ -30,7 +32,6 @@ import {
30
32
  readAssets,
31
33
  uploadAssets,
32
34
  type Assets,
33
- type AssetsConfig,
34
35
  type AssetsProps,
35
36
  } from "./Assets.ts";
36
37
  import { getCompatibility } from "./Compatibility.ts";
@@ -98,20 +99,12 @@ export const isWorkerEvent = (value: any): value is WorkerEvent =>
98
99
  * When hash is provided, it's used directly for diffing instead of computing from directory contents.
99
100
  * This is useful when integrating with Build resources that produce a deterministic hash.
100
101
  */
101
- export interface AssetsWithHash {
102
- /**
103
- * Path to the assets directory.
104
- */
105
- path: string;
102
+ export interface AssetsWithHash extends AssetsProps {
106
103
  /**
107
104
  * Pre-computed hash of the assets. When provided, this hash is used for diffing
108
105
  * to determine if the worker needs to be redeployed.
109
106
  */
110
107
  hash: string;
111
- /**
112
- * Optional assets configuration.
113
- */
114
- config?: AssetsConfig;
115
108
  }
116
109
 
117
110
  export interface WorkerObservability extends Exclude<
@@ -165,7 +158,7 @@ export type WorkerBindingProps = {
165
158
  | Effect.Effect<WorkerBindingResource, any, any>;
166
159
  };
167
160
 
168
- type NormalizedBindings<
161
+ export type NormalizedBindings<
169
162
  Bindings extends WorkerBindingProps = {},
170
163
  AssetsConfig extends WorkerAssetsConfig | undefined = undefined,
171
164
  > = {
@@ -286,26 +279,33 @@ export interface WorkerProps<
286
279
  /**
287
280
  * Options for the local dev server that runs this Worker under `alchemy dev`.
288
281
  * Each Worker is served on its own port.
282
+ *
283
+ * Set to `false` to skip starting a local Worker entirely — useful when an
284
+ * external dev server (e.g. one spawned via `Build.DevCommand`) is
285
+ * serving the content this Worker would otherwise host.
289
286
  */
290
- dev?: {
291
- /**
292
- * Host the local dev server binds to.
293
- * @default "localhost"
294
- */
295
- host?: string;
296
- /**
297
- * Port the local dev server listens on. If the port is unavailable, the
298
- * next free port is used unless {@link strictPort} is `true`.
299
- * @default 1337
300
- */
301
- port?: number;
302
- /**
303
- * When `true`, fail instead of falling back to another port if {@link port}
304
- * is already in use.
305
- * @default false
306
- */
307
- strictPort?: boolean;
308
- };
287
+ dev?:
288
+ | false
289
+ | string
290
+ | {
291
+ /**
292
+ * Host the local dev server binds to.
293
+ * @default "localhost"
294
+ */
295
+ host?: string;
296
+ /**
297
+ * Port the local dev server listens on. If the port is unavailable,
298
+ * the next free port is used unless {@link strictPort} is `true`.
299
+ * @default 1337
300
+ */
301
+ port?: number;
302
+ /**
303
+ * When `true`, fail instead of falling back to another port if
304
+ * {@link port} is already in use.
305
+ * @default false
306
+ */
307
+ strictPort?: boolean;
308
+ };
309
309
  }
310
310
 
311
311
  export type Worker<Bindings extends WorkerBindings = any> = Resource<
@@ -708,7 +708,11 @@ export const Worker: Platform<
708
708
  id: string,
709
709
  props:
710
710
  | InputProps<WorkerProps<Bindings, Assets>>
711
- | Effect.Effect<InputProps<WorkerProps<Bindings, Assets>>, never, Req>,
711
+ | Effect.Effect<
712
+ InputProps<WorkerProps<Bindings, Assets>>,
713
+ ConfigError,
714
+ Req
715
+ >,
712
716
  ): Effect.Effect<
713
717
  Worker<{
714
718
  [binding in keyof NormalizedBindings<
@@ -1124,15 +1128,9 @@ export const LiveWorkerProvider = () =>
1124
1128
  return undefined;
1125
1129
  }
1126
1130
 
1127
- if (
1128
- typeof assets === "object" &&
1129
- "path" in assets &&
1130
- "hash" in assets
1131
- ) {
1132
- return yield* readAssets({
1133
- directory: assets.path as string,
1134
- config: assets.config,
1135
- });
1131
+ if (typeof assets === "object" && "hash" in assets) {
1132
+ const { hash: _, ...config } = assets;
1133
+ return yield* readAssets(config);
1136
1134
  }
1137
1135
 
1138
1136
  // Handle string path or AssetsProps
@@ -1181,7 +1179,7 @@ export const LiveWorkerProvider = () =>
1181
1179
  typeof Redacted.value(value) === "string"
1182
1180
  ? Redacted.value(value)
1183
1181
  : Config.isConfig(value) || Effect.isEffect(value)
1184
- ? yield* value
1182
+ ? yield* value as Effect.Effect<any>
1185
1183
  : undefined,
1186
1184
  ];
1187
1185
  }),
@@ -1203,11 +1201,10 @@ export const LiveWorkerProvider = () =>
1203
1201
  [
1204
1202
  assetsDirectory
1205
1203
  ? readAssets({
1204
+ ...(props.assets && typeof props.assets !== "string"
1205
+ ? props.assets
1206
+ : undefined),
1206
1207
  directory: assetsDirectory,
1207
- config:
1208
- typeof props.assets === "object" && "config" in props.assets
1209
- ? props.assets.config
1210
- : undefined,
1211
1208
  })
1212
1209
  : Effect.succeed(undefined),
1213
1210
  serverBundle
@@ -1293,6 +1290,15 @@ export const LiveWorkerProvider = () =>
1293
1290
  })),
1294
1291
  );
1295
1292
 
1293
+ const normalizePrebuiltAssets = (
1294
+ assets: WorkerProps["assets"],
1295
+ output: Worker["Attributes"] | undefined,
1296
+ ) => {
1297
+ if (!Predicate.hasProperty(assets, "hash")) return undefined;
1298
+ const { directory: _, hash, ...config } = assets;
1299
+ return { config, hash, skip: hash === output?.hash?.assets };
1300
+ };
1301
+
1296
1302
  const putWorker = Effect.fnUntraced(function* (
1297
1303
  id: string,
1298
1304
  news: WorkerProps,
@@ -1311,28 +1317,14 @@ export const LiveWorkerProvider = () =>
1311
1317
  // entirely and tell Cloudflare to keep the assets it already
1312
1318
  // has bound to this script. The disk read is the expensive
1313
1319
  // part; the script PUT happens either way.
1314
- const previousAssetsHash = output?.hash?.assets;
1315
- const precomputedAssetsHash =
1316
- news.assets &&
1317
- typeof news.assets === "object" &&
1318
- "path" in news.assets &&
1319
- "hash" in news.assets
1320
- ? (news.assets.hash as string)
1321
- : undefined;
1322
- const assetsConfigFromProps =
1323
- news.assets &&
1324
- typeof news.assets === "object" &&
1325
- "config" in news.assets
1326
- ? news.assets.config
1327
- : undefined;
1328
- const skipAssetsRead =
1329
- precomputedAssetsHash !== undefined &&
1330
- precomputedAssetsHash === previousAssetsHash;
1320
+ const prebuiltAssets = normalizePrebuiltAssets(news.assets, output);
1331
1321
  const {
1332
1322
  assets,
1333
1323
  bundle,
1334
1324
  hash: preparedHash,
1335
- } = yield* prepareAssetsAndBundle(id, news, { skipAssetsRead });
1325
+ } = yield* prepareAssetsAndBundle(id, news, {
1326
+ skipAssetsRead: prebuiltAssets?.skip,
1327
+ });
1336
1328
  // When the caller supplied a precomputed hash (e.g. via
1337
1329
  // `Build.Command`), store *that* hash in output state so the
1338
1330
  // next diff can short-circuit by comparing it directly. The
@@ -1341,7 +1333,7 @@ export const LiveWorkerProvider = () =>
1341
1333
  // build-input hash and will never match it on the next pass.
1342
1334
  const hash = {
1343
1335
  ...preparedHash,
1344
- assets: precomputedAssetsHash ?? preparedHash.assets,
1336
+ assets: prebuiltAssets?.hash ?? preparedHash.assets,
1345
1337
  } satisfies Worker["Attributes"]["hash"];
1346
1338
  const metadataBindings = bindings.flatMap((b) => b.data.bindings ?? []);
1347
1339
  const expectedDurableObjectClassNames =
@@ -1350,16 +1342,14 @@ export const LiveWorkerProvider = () =>
1350
1342
  | workers.PutScriptRequest["metadata"]["assets"]
1351
1343
  | undefined;
1352
1344
  let keepAssets = false;
1353
- if (skipAssetsRead) {
1345
+ if (prebuiltAssets?.skip) {
1354
1346
  // Hash matched what's already on Cloudflare: keep the
1355
1347
  // existing asset manifest and skip the upload session.
1356
1348
  yield* Effect.logInfo(
1357
1349
  `Cloudflare Worker update: assets unchanged for ${name}, keeping existing`,
1358
1350
  );
1359
1351
  keepAssets = true;
1360
- metadataAssets = assetsConfigFromProps
1361
- ? { config: assetsConfigFromProps }
1362
- : undefined;
1352
+ metadataAssets = { config: prebuiltAssets.config };
1363
1353
  metadataBindings.push({
1364
1354
  type: "assets",
1365
1355
  name: "ASSETS",
@@ -1372,7 +1362,7 @@ export const LiveWorkerProvider = () =>
1372
1362
  // disk). In that case still keep the existing manifest and
1373
1363
  // skip the upload session — Cloudflare's content-addressed
1374
1364
  // session would no-op on every byte anyway.
1375
- if (assets.hash === previousAssetsHash) {
1365
+ if (assets.hash === prebuiltAssets?.hash) {
1376
1366
  yield* Effect.logInfo(
1377
1367
  `Cloudflare Worker update: assets unchanged for ${name}, keeping existing`,
1378
1368
  );
@@ -1759,12 +1749,9 @@ export const LiveWorkerProvider = () =>
1759
1749
  if (!props.assets) {
1760
1750
  return false;
1761
1751
  }
1762
- const assetsHash =
1763
- typeof props.assets === "object" &&
1764
- "path" in props.assets &&
1765
- "hash" in props.assets
1766
- ? (props.assets.hash as string)
1767
- : undefined;
1752
+ const assetsHash = Predicate.hasProperty(props.assets, "hash")
1753
+ ? props.assets.hash
1754
+ : undefined;
1768
1755
  if (assetsHash === undefined) {
1769
1756
  return true;
1770
1757
  }
@@ -1802,12 +1789,9 @@ export const LiveWorkerProvider = () =>
1802
1789
  // assume the assets changed; `putWorker` will read once,
1803
1790
  // hash, and use `keepAssets` if it turns out nothing actually
1804
1791
  // changed.
1805
- const assetsHash =
1806
- typeof props.assets === "object" &&
1807
- "path" in props.assets &&
1808
- "hash" in props.assets
1809
- ? (props.assets.hash as string)
1810
- : undefined;
1792
+ const assetsHash = Predicate.hasProperty(props.assets, "hash")
1793
+ ? props.assets.hash
1794
+ : undefined;
1811
1795
  if (assetsHash === undefined) {
1812
1796
  return true;
1813
1797
  }
@@ -1976,7 +1960,7 @@ export const LiveWorkerProvider = () =>
1976
1960
  // @distilled.cloud/cloudflare versions that haven't picked
1977
1961
  // up the patch yet.
1978
1962
  Effect.retry({
1979
- while: (e: any) =>
1963
+ while: (e) =>
1980
1964
  e._tag === "InternalServerError" ||
1981
1965
  e._tag === "UnknownCloudflareError",
1982
1966
  schedule: Schedule.exponential(1000).pipe(
@@ -20,10 +20,12 @@ import { isKVNamespace } from "../KV/KVNamespace.ts";
20
20
  import { isQueue } from "../Queue/Queue.ts";
21
21
  import { isR2Bucket } from "../R2/R2Bucket.ts";
22
22
  import { isRateLimit } from "../RateLimit/RateLimit.ts";
23
+ import { isSecret } from "../SecretsStore/Secret.ts";
23
24
  import { isVectorizeIndex } from "../Vectorize/VectorizeIndex.ts";
24
25
  import { isAssets } from "./Assets.ts";
25
26
  import { isDurableObjectNamespaceLike } from "./DurableObjectNamespace.ts";
26
27
  import { isDynamicWorkerLoader } from "./DynamicWorkerLoader.ts";
28
+ import { isVersionMetadata } from "./VersionMetadata.ts";
27
29
  import type { WorkerBindingProps } from "./Worker.ts";
28
30
  import { isWorker, type Worker, type WorkerProps } from "./Worker.ts";
29
31
  import type { WorkerBinding, WorkerBindingResource } from "./WorkerBinding.ts";
@@ -210,6 +212,18 @@ const toBinding = (
210
212
  name: bindingName,
211
213
  indexName: binding.indexName,
212
214
  };
215
+ } else if (isSecret(binding)) {
216
+ return {
217
+ type: "secrets_store_secret",
218
+ name: bindingName,
219
+ secretName: binding.secretName,
220
+ storeId: binding.storeId,
221
+ };
222
+ } else if (isVersionMetadata(binding)) {
223
+ return {
224
+ type: "version_metadata",
225
+ name: bindingName,
226
+ };
213
227
  } else if (isDynamicWorkerLoader(binding)) {
214
228
  return {
215
229
  type: "worker_loader",
@@ -18,10 +18,12 @@ import type { KVNamespace } from "../KV/KVNamespace.ts";
18
18
  import type { Queue } from "../Queue/Queue.ts";
19
19
  import type { R2Bucket } from "../R2/R2Bucket.ts";
20
20
  import type { RateLimit } from "../RateLimit/RateLimit.ts";
21
+ import type { Secret } from "../SecretsStore/Secret.ts";
21
22
  import type { VectorizeIndex } from "../Vectorize/VectorizeIndex.ts";
22
23
  import type { Assets } from "./Assets.ts";
23
24
  import type { DurableObjectNamespaceLike } from "./DurableObjectNamespace.ts";
24
25
  import type { DynamicWorkerLoader } from "./DynamicWorkerLoader.ts";
26
+ import type { VersionMetadata } from "./VersionMetadata.ts";
25
27
  import { makeRpcStub } from "./Rpc.ts";
26
28
  import { isWorker, Worker, WorkerEnvironment } from "./Worker.ts";
27
29
 
@@ -55,8 +57,10 @@ export type WorkerBindingResource =
55
57
  | Images
56
58
  | Hyperdrive
57
59
  | VectorizeIndex
60
+ | Secret
58
61
  | Worker
59
62
  | DynamicWorkerLoader
63
+ | VersionMetadata
60
64
  | DurableObjectNamespaceLike<any>;
61
65
 
62
66
  export type WorkerBindings = {
@@ -3,6 +3,7 @@ import type { ConfigError } from "effect/Config";
3
3
  import * as Context from "effect/Context";
4
4
  import * as Effect from "effect/Effect";
5
5
  import { AlchemyContext } from "../../AlchemyContext.ts";
6
+ import { ExecutionContext } from "../../ExecutionContext.ts";
6
7
  import { ALCHEMY_PHASE } from "../../Phase.ts";
7
8
  import type { PlatformServices } from "../../Platform.ts";
8
9
  import * as Provider from "../../Provider.ts";
@@ -100,8 +101,18 @@ export const sleepUntil = (
100
101
  * from inside workflow steps via `yield* WorkerEnvironment` — the type must
101
102
  * reflect that or `yield* WorkerEnvironment` fails to type-check inside a
102
103
  * body even though it succeeds at runtime.
104
+ *
105
+ * `ExecutionContext` (scope + cache) is provided per run-invocation by
106
+ * `WorkflowBridge.run` and threaded into every `task` via the surrounding
107
+ * body context, so `@binding` helpers that need it (e.g. `Drizzle.postgres`)
108
+ * resolve their per-run resources inside workflow steps just as they do in a
109
+ * Worker `fetch`/`queue` handler.
103
110
  */
104
- export type WorkflowRunServices = WorkflowEvent | WorkflowStep | WorkerServices;
111
+ export type WorkflowRunServices =
112
+ | WorkflowEvent
113
+ | WorkflowStep
114
+ | WorkerServices
115
+ | ExecutionContext;
105
116
 
106
117
  export type WorkflowServices = WorkflowRunServices | PlatformServices;
107
118
 
@@ -1,11 +1,17 @@
1
+ import * as Cause from "effect/Cause";
1
2
  import * as Effect from "effect/Effect";
3
+ import * as Exit from "effect/Exit";
4
+ import * as Layer from "effect/Layer";
5
+ import * as Scope from "effect/Scope";
6
+ import { ExecutionContext } from "../../ExecutionContext.ts";
7
+ import { isScopeEjected } from "./HttpServer.ts";
8
+ import { getWorkerExport } from "./WorkerBridge.ts";
2
9
  import {
3
10
  WorkflowEvent as WorkflowEventService,
4
11
  type WorkflowExport,
5
12
  type WorkflowImpl,
6
13
  WorkflowStep,
7
14
  } from "./Workflow.ts";
8
- import { getWorkerExport } from "./WorkerBridge.ts";
9
15
 
10
16
  /**
11
17
  * Create a WorkflowBridge class that extends `WorkflowEntrypoint` and
@@ -52,15 +58,50 @@ export const makeWorkflowBridge =
52
58
 
53
59
  async run(event: any, step: any): Promise<unknown> {
54
60
  const fn = await this.fn;
55
- return Effect.runPromise(
61
+ // Each run-invocation gets a fresh ExecutionContext (scope + cache),
62
+ // following the same per-invocation-scope pattern as
63
+ // `WorkerBridge.processEvent`. `task` threads this into every step via
64
+ // the surrounding body context, so `@binding` helpers that need it
65
+ // (e.g. `Drizzle.postgres`) resolve their per-run resources inside
66
+ // workflow steps. The same scope is also provided as the ambient
67
+ // `Scope` service, matching the Worker and Durable Object bridges.
68
+ const scope = Scope.makeUnsafe();
69
+ const exit = await Effect.runPromiseExit(
56
70
  fn(event.payload).pipe(
57
- Effect.provideService(
58
- WorkflowEventService,
59
- wrapWorkflowEvent(event),
71
+ Effect.provide(
72
+ Layer.succeed(
73
+ WorkflowEventService,
74
+ wrapWorkflowEvent(event),
75
+ ).pipe(
76
+ Layer.provideMerge(
77
+ Layer.succeed(WorkflowStep, wrapWorkflowStep(step)),
78
+ ),
79
+ Layer.provideMerge(
80
+ Layer.succeed(ExecutionContext, { scope, cache: {} }),
81
+ ),
82
+ Layer.provideMerge(Layer.succeed(Scope.Scope, scope)),
83
+ ),
60
84
  ),
61
- Effect.provideService(WorkflowStep, wrapWorkflowStep(step)),
62
85
  ) as Effect.Effect<unknown>,
63
86
  );
87
+ // Settle the run's resources with its real exit, unless a binding
88
+ // ejected the scope to outlive the invocation. The workflow runtime has
89
+ // no `waitUntil` to detach cleanup to, so close inline — a failing
90
+ // finalizer (e.g. a pg pool `end()` on a dropped connection) is logged
91
+ // and ignored so it can't mask the run's outcome.
92
+ if (!isScopeEjected(scope)) {
93
+ await Scope.close(scope, exit).pipe(
94
+ Effect.ignoreCause({
95
+ log: "Warn",
96
+ message: "Workflow run scope close failed",
97
+ }),
98
+ Effect.runPromise,
99
+ );
100
+ }
101
+ if (Exit.isSuccess(exit)) {
102
+ return exit.value;
103
+ }
104
+ throw Cause.squash(exit.cause);
64
105
  }
65
106
  };
66
107
 
@@ -2,6 +2,7 @@ export * from "./Assets.ts";
2
2
  export * from "./ConfigProvider.ts";
3
3
  export * from "./CronEventSource.ts";
4
4
  export * from "./DurableObjectBridge.ts";
5
+ export * from "./DurableObjectChatPersistence.ts";
5
6
  export * from "./DurableObjectNamespace.ts";
6
7
  export * from "./DurableObjectState.ts";
7
8
  export * from "./DurableObjectStorage.ts";
@@ -15,6 +16,8 @@ export * from "./Rpc.ts";
15
16
  export * from "./RpcDurableObjectNamespace.ts";
16
17
  export * from "./RpcWorker.ts";
17
18
  export * from "./ScheduledEvents.ts";
19
+ export * from "./VersionMetadata.ts";
20
+ export * from "./VersionMetadataBinding.ts";
18
21
  export * from "./WebSocket.ts";
19
22
  export * from "./Worker.ts";
20
23
  export * from "./WorkerBinding.ts";
@@ -17,6 +17,7 @@ export * from "./Providers.ts";
17
17
  export * from "./Queue/index.ts";
18
18
  export * from "./R2/index.ts";
19
19
  export * from "./RateLimit/index.ts";
20
+ export * from "./Ruleset/index.ts";
20
21
  export * from "./SecretsStore/index.ts";
21
22
  export * from "./StateStore/index.ts";
22
23
  export * from "./Tunnel/index.ts";
package/src/Deploy.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ConfigError } from "effect/Config";
1
2
  import * as Effect from "effect/Effect";
2
3
  import type * as Scope from "effect/Scope";
3
4
  import { AlchemyContext } from "./AlchemyContext.ts";
@@ -14,7 +15,7 @@ export const deploy = <A>({
14
15
  scope,
15
16
  force,
16
17
  }: {
17
- stack: StackEffect<CompiledStack<A>, Stage | AlchemyContext>;
18
+ stack: StackEffect<CompiledStack<A>, ConfigError, Stage | AlchemyContext>;
18
19
  stage: string;
19
20
  dev?: boolean;
20
21
  /** See {@link evalStack} — when set, scoped resources outlive `deploy`. */
package/src/Destroy.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ConfigError } from "effect/Config";
1
2
  import { Effect } from "effect";
2
3
  import type * as Scope from "effect/Scope";
3
4
  import type { AlchemyContext } from "./AlchemyContext.ts";
@@ -13,7 +14,7 @@ export const destroy = ({
13
14
  dev,
14
15
  scope,
15
16
  }: {
16
- stack: StackEffect<CompiledStack, Stage | AlchemyContext>;
17
+ stack: StackEffect<CompiledStack, ConfigError, Stage | AlchemyContext>;
17
18
  stage: string;
18
19
  dev?: boolean;
19
20
  /** See {@link evalStack} — when set, scoped resources outlive `destroy`. */
package/src/Diff.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as Effect from "effect/Effect";
2
+ import * as Redacted from "effect/Redacted";
2
3
  import type { Input } from "./Input.ts";
3
4
  import * as Output from "./Output.ts";
4
5
  import type { BindingNode } from "./Plan.ts";
@@ -119,6 +120,12 @@ export const deepEqual = (
119
120
 
120
121
  const canonicalize = (value: unknown, stripNullish: boolean): unknown => {
121
122
  if (stripNullish && value == null) return undefined;
123
+ if (Redacted.isRedacted(value)) {
124
+ return {
125
+ _tag: "Redacted",
126
+ value: Redacted.value(value),
127
+ };
128
+ }
122
129
  if (Array.isArray(value)) {
123
130
  return value.map((v) => canonicalize(v, stripNullish));
124
131
  }
@@ -25,19 +25,20 @@ import { proxyChain } from "../Util/proxy-chain.ts";
25
25
  * });
26
26
  * ```
27
27
  *
28
- * Behind the scenes the actual connect work is wrapped in `Effect.cached`,
29
- * so the pool is built at most once per JS realm. Yielding the
30
- * connection string is also deferred until first query, so deploy /
31
- * plan-time invocations (where `WorkerEnvironment` isn't provided)
32
- * never trigger a real connection attempt.
28
+ * The connect work is deferred until the first query and memoized on the
29
+ * current `ExecutionContext` (`ctx.cache`), so the pool is built at most
30
+ * once per execution — a Worker `fetch`/`queue`/`scheduled` event or a
31
+ * Workflow run — and reused across every query and `task` step in that
32
+ * execution. Yielding the connection string is likewise deferred, so
33
+ * deploy / plan-time invocations (where `WorkerEnvironment` isn't
34
+ * provided) never trigger a real connection attempt.
33
35
  *
34
- * The PgClient pool is built against an isolated, never-closing `Scope`
35
- * so it outlives whatever scope this helper is yielded under. In a
36
- * Cloudflare Worker the surrounding `Cloudflare.Worker` runs init
37
- * inside `Effect.scoped`, which closes after returning the exports
38
- * object — without an isolated scope, the pool's `end` finalizer
39
- * would fire there and every subsequent request would see "Cannot
40
- * use a pool after end".
36
+ * The pool is built against the execution's `Scope` (`ctx.scope`), so its
37
+ * `end` finalizer fires when that scope closes — when the request / run
38
+ * settles, not when the `Cloudflare.Worker` init scope closes. Init runs
39
+ * inside `Effect.scoped` and closes after returning the exports object;
40
+ * building lazily against `ctx.scope` (rather than that init scope) is
41
+ * what keeps later requests from seeing "Cannot use a pool after end".
41
42
  *
42
43
  * @binding
43
44
  */
@@ -1,7 +1,7 @@
1
- import * as crypto from "node:crypto";
2
1
  import * as Effect from "effect/Effect";
3
2
  import * as FileSystem from "effect/FileSystem";
4
3
  import * as Path from "effect/Path";
4
+ import * as crypto from "node:crypto";
5
5
  import { isResolved } from "../Diff.ts";
6
6
  import * as Provider from "../Provider.ts";
7
7
  import { Resource } from "../Resource.ts";
@@ -9,6 +9,10 @@ import type { Providers } from "./Providers.ts";
9
9
 
10
10
  export type Dialect = "postgres" | "mysql" | "sqlite";
11
11
 
12
+ type DrizzleSnapshot = {
13
+ id?: string;
14
+ };
15
+
12
16
  export type SchemaProps = {
13
17
  /**
14
18
  * Path to the schema module, relative to the current working directory.
@@ -41,7 +45,7 @@ export type Schema = Resource<
41
45
  "Drizzle.Schema",
42
46
  SchemaProps,
43
47
  {
44
- /** Absolute path to the migrations directory. */
48
+ /** Path to the migrations directory, relative to the current working directory. */
45
49
  out: string;
46
50
  /**
47
51
  * sha256 of the latest snapshot.json. Stable across deploys when the
@@ -113,6 +117,12 @@ export const SchemaProvider = () =>
113
117
  const resolveOut = (p: SchemaProps) =>
114
118
  path.resolve(process.cwd(), p.out ?? "./migrations");
115
119
 
120
+ // The `out` attribute is exposed as a path relative to the current
121
+ // working directory so that persisted state stays portable across
122
+ // machines/checkouts. Internal filesystem ops always use the absolute
123
+ // `resolveOut` form.
124
+ const relativeOut = (abs: string) => path.relative(process.cwd(), abs);
125
+
116
126
  const resolveSchema = (p: SchemaProps) =>
117
127
  path.resolve(process.cwd(), p.schema);
118
128
 
@@ -164,7 +174,10 @@ export const SchemaProvider = () =>
164
174
  const exists = yield* fs.exists(snapshotPath);
165
175
  if (!exists) continue;
166
176
  const text = yield* fs.readFileString(snapshotPath);
167
- return { snapshot: JSON.parse(text) as unknown, hash: sha(text) };
177
+ return {
178
+ snapshot: JSON.parse(text) as DrizzleSnapshot,
179
+ hash: sha(text),
180
+ };
168
181
  }
169
182
  return undefined;
170
183
  });
@@ -181,13 +194,14 @@ export const SchemaProvider = () =>
181
194
  const dialect = props.dialect ?? "postgres";
182
195
  const kit = yield* loadKit(dialect);
183
196
  const schemaModule = yield* loadSchemaModule(props);
197
+ const prevEntry = yield* readLatestSnapshot(out);
184
198
  const cur = yield* Effect.tryPromise({
185
- try: () => kit.generateDrizzleJson(schemaModule),
199
+ try: () =>
200
+ kit.generateDrizzleJson(schemaModule, prevEntry?.snapshot.id),
186
201
  catch: (cause) =>
187
202
  new Error(`drizzle-kit generateDrizzleJson failed: ${cause}`),
188
203
  });
189
204
 
190
- const prevEntry = yield* readLatestSnapshot(out);
191
205
  // For the initial migration, drizzle-kit needs an *empty* snapshot
192
206
  // produced by `generateDrizzleJson({})`, not a bare `{}` — the
193
207
  // snapshot has internal fields (`ddl`, etc.) that the differ reads.
@@ -233,7 +247,7 @@ export const SchemaProvider = () =>
233
247
  const migrations = yield* listMigrationDirs(out);
234
248
  const latest = yield* readLatestSnapshot(out);
235
249
  return {
236
- out,
250
+ out: relativeOut(out),
237
251
  snapshotHash: latest?.hash ?? sha(JSON.stringify(cur)),
238
252
  migrations,
239
253
  };
@@ -248,8 +262,12 @@ export const SchemaProvider = () =>
248
262
  // `schema.out` as an unresolved Output during plan and cascade
249
263
  // into spurious updates of their own.
250
264
  const { sqlStatements } = yield* detectDrift(news);
251
- if (sqlStatements.length === 0) return undefined;
252
- return { action: "update" } as const;
265
+ // Originally `output.out` was an absolute path, which is not portable.
266
+ // So, we trigger an update to migrate existing resources.
267
+ // This is safe because `regenerate` is idempotent.
268
+ return sqlStatements.length > 0 || path.isAbsolute(output.out)
269
+ ? { action: "update" }
270
+ : undefined;
253
271
  }),
254
272
  read: Effect.fn(function* ({ olds, output }) {
255
273
  if (!output) return undefined;
@@ -259,7 +277,7 @@ export const SchemaProvider = () =>
259
277
  const latest = yield* readLatestSnapshot(out);
260
278
  const migrations = yield* listMigrationDirs(out);
261
279
  return {
262
- out,
280
+ out: relativeOut(out),
263
281
  snapshotHash: latest?.hash ?? output.snapshotHash,
264
282
  migrations,
265
283
  };