@effect/platform-cloudflare 0.0.0 → 4.1.3

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 (237) hide show
  1. package/AGENTS.md +434 -0
  2. package/CLAUDE.md +434 -0
  3. package/LICENSE +21 -0
  4. package/README.md +506 -0
  5. package/ai-docs/README.md +44 -0
  6. package/ai-docs/package.json +36 -0
  7. package/ai-docs/src/01_effect/01_basics/01_effect-gen.ts +30 -0
  8. package/ai-docs/src/01_effect/01_basics/02_effect-fn.ts +52 -0
  9. package/ai-docs/src/01_effect/01_basics/10_creating-effects.ts +74 -0
  10. package/ai-docs/src/01_effect/01_basics/index.md +7 -0
  11. package/ai-docs/src/01_effect/02_schema/10_schema-basics.ts +43 -0
  12. package/ai-docs/src/01_effect/02_schema/index.md +7 -0
  13. package/ai-docs/src/01_effect/03_services/01_service.ts +45 -0
  14. package/ai-docs/src/01_effect/03_services/10_reference.ts +10 -0
  15. package/ai-docs/src/01_effect/03_services/20_layer-composition.ts +70 -0
  16. package/ai-docs/src/01_effect/03_services/20_layer-unwrap.ts +66 -0
  17. package/ai-docs/src/01_effect/03_services/index.md +5 -0
  18. package/ai-docs/src/01_effect/04_errors/01_error-handling.ts +30 -0
  19. package/ai-docs/src/01_effect/04_errors/10_catch-tags.ts +24 -0
  20. package/ai-docs/src/01_effect/04_errors/20_reason-errors.ts +64 -0
  21. package/ai-docs/src/01_effect/04_errors/index.md +1 -0
  22. package/ai-docs/src/01_effect/05_resources/10_acquire-release.ts +105 -0
  23. package/ai-docs/src/01_effect/05_resources/20_layer-side-effects.ts +31 -0
  24. package/ai-docs/src/01_effect/05_resources/30_layer-map.ts +86 -0
  25. package/ai-docs/src/01_effect/05_resources/index.md +3 -0
  26. package/ai-docs/src/01_effect/06_running/10_run-main.ts +30 -0
  27. package/ai-docs/src/01_effect/06_running/20_layer-launch.ts +27 -0
  28. package/ai-docs/src/01_effect/06_running/index.md +1 -0
  29. package/ai-docs/src/01_effect/07_pubsub/10_pubsub.ts +56 -0
  30. package/ai-docs/src/01_effect/07_pubsub/index.md +3 -0
  31. package/ai-docs/src/03_stream/10_creating-streams.ts +103 -0
  32. package/ai-docs/src/03_stream/20_consuming-streams.ts +137 -0
  33. package/ai-docs/src/03_stream/30_encoding.ts +163 -0
  34. package/ai-docs/src/03_stream/index.md +4 -0
  35. package/ai-docs/src/04_integration/10_managed-runtime.ts +129 -0
  36. package/ai-docs/src/04_integration/index.md +5 -0
  37. package/ai-docs/src/05_batching/10_request-resolver.ts +89 -0
  38. package/ai-docs/src/05_batching/index.md +3 -0
  39. package/ai-docs/src/06_schedule/10_schedules.ts +111 -0
  40. package/ai-docs/src/06_schedule/index.md +3 -0
  41. package/ai-docs/src/07_datetime/10_creating-and-formatting.ts +30 -0
  42. package/ai-docs/src/07_datetime/20_time-zones.ts +44 -0
  43. package/ai-docs/src/07_datetime/index.md +5 -0
  44. package/ai-docs/src/08_observability/10_logging.ts +66 -0
  45. package/ai-docs/src/08_observability/20_otlp-tracing.ts +95 -0
  46. package/ai-docs/src/08_observability/index.md +7 -0
  47. package/ai-docs/src/09_testing/10_effect-tests.ts +55 -0
  48. package/ai-docs/src/09_testing/20_layer-tests.ts +138 -0
  49. package/ai-docs/src/09_testing/index.md +1 -0
  50. package/ai-docs/src/10_predicate/01_basics.ts +14 -0
  51. package/ai-docs/src/10_predicate/index.md +9 -0
  52. package/ai-docs/src/40_sql/10_basics.ts +175 -0
  53. package/ai-docs/src/40_sql/index.md +6 -0
  54. package/ai-docs/src/50_http-client/10_basics.ts +102 -0
  55. package/ai-docs/src/50_http-client/index.md +3 -0
  56. package/ai-docs/src/51_http-server/10_basics.ts +118 -0
  57. package/ai-docs/src/51_http-server/20_testing.ts +100 -0
  58. package/ai-docs/src/51_http-server/fixtures/api/Api.ts +14 -0
  59. package/ai-docs/src/51_http-server/fixtures/api/Authorization.ts +36 -0
  60. package/ai-docs/src/51_http-server/fixtures/api/System.ts +10 -0
  61. package/ai-docs/src/51_http-server/fixtures/api/Users.ts +107 -0
  62. package/ai-docs/src/51_http-server/fixtures/domain/User.ts +21 -0
  63. package/ai-docs/src/51_http-server/fixtures/domain/UserErrors.ts +22 -0
  64. package/ai-docs/src/51_http-server/fixtures/server/Authorization.ts +37 -0
  65. package/ai-docs/src/51_http-server/fixtures/server/Users/http.ts +80 -0
  66. package/ai-docs/src/51_http-server/fixtures/server/Users.ts +183 -0
  67. package/ai-docs/src/51_http-server/index.md +3 -0
  68. package/ai-docs/src/52_graphql/10_basics.ts +117 -0
  69. package/ai-docs/src/52_graphql/20_testing.ts +45 -0
  70. package/ai-docs/src/52_graphql/30_batching.ts +118 -0
  71. package/ai-docs/src/52_graphql/40_aliased-batching.ts +124 -0
  72. package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
  73. package/ai-docs/src/52_graphql/index.md +6 -0
  74. package/ai-docs/src/60_child-process/10_working-with-child-processes.ts +117 -0
  75. package/ai-docs/src/60_child-process/index.md +3 -0
  76. package/ai-docs/src/70_cli/10_basics.ts +162 -0
  77. package/ai-docs/src/70_cli/index.md +5 -0
  78. package/ai-docs/src/71_ai/10_language-model.ts +156 -0
  79. package/ai-docs/src/71_ai/20_tools.ts +226 -0
  80. package/ai-docs/src/71_ai/30_chat.ts +158 -0
  81. package/ai-docs/src/71_ai/fixtures/domain/LaunchPlan.ts +9 -0
  82. package/ai-docs/src/71_ai/index.md +5 -0
  83. package/ai-docs/src/80_cluster/10_entities.ts +97 -0
  84. package/ai-docs/src/80_cluster/index.md +4 -0
  85. package/ai-docs/src/index.md +10 -0
  86. package/ai-docs/tsconfig.json +28 -0
  87. package/dist/AlchemyCloudflareCluster.d.ts +134 -0
  88. package/dist/AlchemyCloudflareCluster.d.ts.map +1 -0
  89. package/dist/AlchemyCloudflareCluster.js +111 -0
  90. package/dist/AlchemyCloudflareCluster.js.map +1 -0
  91. package/dist/CloudflareCluster.d.ts +196 -0
  92. package/dist/CloudflareCluster.d.ts.map +1 -0
  93. package/dist/CloudflareCluster.js +533 -0
  94. package/dist/CloudflareCluster.js.map +1 -0
  95. package/dist/CloudflareDurableObjectPrograms.d.ts +279 -0
  96. package/dist/CloudflareDurableObjectPrograms.d.ts.map +1 -0
  97. package/dist/CloudflareDurableObjectPrograms.js +295 -0
  98. package/dist/CloudflareDurableObjectPrograms.js.map +1 -0
  99. package/dist/CloudflareDurableObjects.d.ts +149 -0
  100. package/dist/CloudflareDurableObjects.d.ts.map +1 -0
  101. package/dist/CloudflareDurableObjects.js +337 -0
  102. package/dist/CloudflareDurableObjects.js.map +1 -0
  103. package/dist/CloudflarePersistedQueue.d.ts +36 -0
  104. package/dist/CloudflarePersistedQueue.d.ts.map +1 -0
  105. package/dist/CloudflarePersistedQueue.js +122 -0
  106. package/dist/CloudflarePersistedQueue.js.map +1 -0
  107. package/dist/CloudflareWorkflowEngine.d.ts +72 -0
  108. package/dist/CloudflareWorkflowEngine.d.ts.map +1 -0
  109. package/dist/CloudflareWorkflowEngine.js +187 -0
  110. package/dist/CloudflareWorkflowEngine.js.map +1 -0
  111. package/dist/index.d.ts +29 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +33 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/internal/alchemyCluster.d.ts +2 -0
  116. package/dist/internal/alchemyCluster.d.ts.map +1 -0
  117. package/dist/internal/alchemyCluster.js +75 -0
  118. package/dist/internal/alchemyCluster.js.map +1 -0
  119. package/dist/internal/boundedMap.d.ts +3 -0
  120. package/dist/internal/boundedMap.d.ts.map +1 -0
  121. package/dist/internal/boundedMap.js +15 -0
  122. package/dist/internal/boundedMap.js.map +1 -0
  123. package/dist/internal/cluster.d.ts +57 -0
  124. package/dist/internal/cluster.d.ts.map +1 -0
  125. package/dist/internal/cluster.js +2 -0
  126. package/dist/internal/cluster.js.map +1 -0
  127. package/dist/internal/clusterName.d.ts +2 -0
  128. package/dist/internal/clusterName.d.ts.map +1 -0
  129. package/dist/internal/clusterName.js +16 -0
  130. package/dist/internal/clusterName.js.map +1 -0
  131. package/dist/internal/entityKeepAlive.d.ts +2 -0
  132. package/dist/internal/entityKeepAlive.d.ts.map +1 -0
  133. package/dist/internal/entityKeepAlive.js +73 -0
  134. package/dist/internal/entityKeepAlive.js.map +1 -0
  135. package/dist/internal/entityMailbox.d.ts +2 -0
  136. package/dist/internal/entityMailbox.d.ts.map +1 -0
  137. package/dist/internal/entityMailbox.js +160 -0
  138. package/dist/internal/entityMailbox.js.map +1 -0
  139. package/dist/internal/entityRegistry.d.ts +16 -0
  140. package/dist/internal/entityRegistry.d.ts.map +1 -0
  141. package/dist/internal/entityRegistry.js +22 -0
  142. package/dist/internal/entityRegistry.js.map +1 -0
  143. package/dist/internal/entityReply.d.ts +2 -0
  144. package/dist/internal/entityReply.d.ts.map +1 -0
  145. package/dist/internal/entityReply.js +35 -0
  146. package/dist/internal/entityReply.js.map +1 -0
  147. package/dist/internal/entityRuntime.d.ts +2 -0
  148. package/dist/internal/entityRuntime.d.ts.map +1 -0
  149. package/dist/internal/entityRuntime.js +823 -0
  150. package/dist/internal/entityRuntime.js.map +1 -0
  151. package/dist/internal/entitySql.d.ts +2 -0
  152. package/dist/internal/entitySql.d.ts.map +1 -0
  153. package/dist/internal/entitySql.js +29 -0
  154. package/dist/internal/entitySql.js.map +1 -0
  155. package/dist/internal/entityStorage.d.ts +2 -0
  156. package/dist/internal/entityStorage.d.ts.map +1 -0
  157. package/dist/internal/entityStorage.js +98 -0
  158. package/dist/internal/entityStorage.js.map +1 -0
  159. package/dist/internal/entityWire.d.ts +2 -0
  160. package/dist/internal/entityWire.d.ts.map +1 -0
  161. package/dist/internal/entityWire.js +146 -0
  162. package/dist/internal/entityWire.js.map +1 -0
  163. package/dist/internal/queueRuntime.d.ts +2 -0
  164. package/dist/internal/queueRuntime.d.ts.map +1 -0
  165. package/dist/internal/queueRuntime.js +88 -0
  166. package/dist/internal/queueRuntime.js.map +1 -0
  167. package/dist/internal/queueStorage.d.ts +2 -0
  168. package/dist/internal/queueStorage.d.ts.map +1 -0
  169. package/dist/internal/queueStorage.js +129 -0
  170. package/dist/internal/queueStorage.js.map +1 -0
  171. package/dist/internal/registry.d.ts +10 -0
  172. package/dist/internal/registry.d.ts.map +1 -0
  173. package/dist/internal/registry.js +24 -0
  174. package/dist/internal/registry.js.map +1 -0
  175. package/dist/internal/singletonRegistry.d.ts +2 -0
  176. package/dist/internal/singletonRegistry.d.ts.map +1 -0
  177. package/dist/internal/singletonRegistry.js +9 -0
  178. package/dist/internal/singletonRegistry.js.map +1 -0
  179. package/dist/internal/singletonRuntime.d.ts +2 -0
  180. package/dist/internal/singletonRuntime.d.ts.map +1 -0
  181. package/dist/internal/singletonRuntime.js +42 -0
  182. package/dist/internal/singletonRuntime.js.map +1 -0
  183. package/dist/internal/singletonStorage.d.ts +2 -0
  184. package/dist/internal/singletonStorage.d.ts.map +1 -0
  185. package/dist/internal/singletonStorage.js +34 -0
  186. package/dist/internal/singletonStorage.js.map +1 -0
  187. package/dist/internal/transport.d.ts +2 -0
  188. package/dist/internal/transport.d.ts.map +1 -0
  189. package/dist/internal/transport.js +49 -0
  190. package/dist/internal/transport.js.map +1 -0
  191. package/dist/internal/workflowRegistry.d.ts +2 -0
  192. package/dist/internal/workflowRegistry.d.ts.map +1 -0
  193. package/dist/internal/workflowRegistry.js +29 -0
  194. package/dist/internal/workflowRegistry.js.map +1 -0
  195. package/dist/internal/workflowRuntime.d.ts +2 -0
  196. package/dist/internal/workflowRuntime.d.ts.map +1 -0
  197. package/dist/internal/workflowRuntime.js +322 -0
  198. package/dist/internal/workflowRuntime.js.map +1 -0
  199. package/dist/internal/workflowStorage.d.ts +2 -0
  200. package/dist/internal/workflowStorage.d.ts.map +1 -0
  201. package/dist/internal/workflowStorage.js +113 -0
  202. package/dist/internal/workflowStorage.js.map +1 -0
  203. package/dist/internal/workflowWire.d.ts +2 -0
  204. package/dist/internal/workflowWire.d.ts.map +1 -0
  205. package/dist/internal/workflowWire.js +52 -0
  206. package/dist/internal/workflowWire.js.map +1 -0
  207. package/package.json +77 -1
  208. package/src/AlchemyCloudflareCluster.ts +200 -0
  209. package/src/CloudflareCluster.ts +704 -0
  210. package/src/CloudflareDurableObjectPrograms.ts +543 -0
  211. package/src/CloudflareDurableObjects.ts +430 -0
  212. package/src/CloudflarePersistedQueue.ts +189 -0
  213. package/src/CloudflareWorkflowEngine.ts +220 -0
  214. package/src/index.ts +38 -0
  215. package/src/internal/alchemyCluster.ts +120 -0
  216. package/src/internal/boundedMap.ts +15 -0
  217. package/src/internal/cluster.ts +57 -0
  218. package/src/internal/clusterName.ts +19 -0
  219. package/src/internal/entityKeepAlive.ts +121 -0
  220. package/src/internal/entityMailbox.ts +324 -0
  221. package/src/internal/entityRegistry.ts +46 -0
  222. package/src/internal/entityReply.ts +54 -0
  223. package/src/internal/entityRuntime.ts +1318 -0
  224. package/src/internal/entitySql.ts +60 -0
  225. package/src/internal/entityStorage.ts +168 -0
  226. package/src/internal/entityWire.ts +204 -0
  227. package/src/internal/queueRuntime.ts +174 -0
  228. package/src/internal/queueStorage.ts +255 -0
  229. package/src/internal/registry.ts +31 -0
  230. package/src/internal/singletonRegistry.ts +21 -0
  231. package/src/internal/singletonRuntime.ts +88 -0
  232. package/src/internal/singletonStorage.ts +64 -0
  233. package/src/internal/transport.ts +67 -0
  234. package/src/internal/workflowRegistry.ts +78 -0
  235. package/src/internal/workflowRuntime.ts +443 -0
  236. package/src/internal/workflowStorage.ts +234 -0
  237. package/src/internal/workflowWire.ts +123 -0
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "../tsconfig.base.json",
4
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
5
+ "compilerOptions": {
6
+ "rootDir": ".",
7
+ "noEmit": true,
8
+ "declaration": false,
9
+ "declarationMap": false,
10
+ "composite": false,
11
+ "incremental": false,
12
+ "types": ["node"],
13
+ "plugins": [
14
+ {
15
+ "name": "@effect/language-service",
16
+ "effectFn": ["untraced"],
17
+ "namespaceImportPackages": [],
18
+ "diagnosticSeverity": {
19
+ "floatingEffect": "off",
20
+ "globalJson": "off",
21
+ "globalJsonInEffect": "off",
22
+ "unstableApiUsage": "off",
23
+ "effectFnOpportunity": "error"
24
+ }
25
+ }
26
+ ]
27
+ }
28
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Deploys the Cloudflare cluster with Alchemy v2 ("Infrastructure as
3
+ * Effects").
4
+ *
5
+ * `make` runs inside an Effect-native `Cloudflare.Worker` init program. It
6
+ * registers the four cluster Durable Object classes on the hosting Worker
7
+ * (bindings, class exports, and SQLite migrations are all owned by Alchemy),
8
+ * builds the cluster layer together with the user's handler layer into the
9
+ * isolate-lifetime scope, and returns a handle for wiring the built context
10
+ * into the Worker's handlers. The user never declares or re-exports Durable
11
+ * Object classes.
12
+ *
13
+ * The Wrangler path (`CloudflareDurableObjects` + `CloudflareCluster.layer`)
14
+ * never imports this module; `@alchemy.run/cloudflare` is an optional peer
15
+ * dependency needed only here.
16
+ *
17
+ * **Example** (Declaring a cluster)
18
+ *
19
+ * ```ts
20
+ * import * as Cloudflare from "@alchemy.run/cloudflare"
21
+ * import { Effect, Layer } from "effect"
22
+ * import { make } from "@effect/platform-cloudflare/AlchemyCloudflareCluster"
23
+ * import { CloudflareCluster } from "@effect/platform-cloudflare"
24
+ *
25
+ * export default Cloudflare.Worker("MyApp", {
26
+ * main: import.meta.url
27
+ * }, Effect.gen(function*() {
28
+ * const cluster = yield* make({
29
+ * entities: [Counter],
30
+ * layer: Layer.mergeAll(CounterLayer, MaintenanceLayer, NightlyReport)
31
+ * })
32
+ *
33
+ * yield* Cloudflare.Workers.cron("0 * * * *", cluster.wake("hourly-maintenance"))
34
+ * // A ClusterCron fires only after a Cron Trigger wakes its seed singleton
35
+ * yield* Cloudflare.Workers.cron(
36
+ * "30 * * * *",
37
+ * cluster.wake(CloudflareCluster.clusterCronSingletonName("nightly-report"))
38
+ * )
39
+ *
40
+ * return {
41
+ * fetch: cluster.provide(handler)
42
+ * }
43
+ * }).pipe(Effect.provide(Cloudflare.Workers.CronEventSourceLive)))
44
+ * ```
45
+ *
46
+ * @stability experimental
47
+ * @since 4.1.3
48
+ */
49
+ import * as Cloudflare from "@alchemy.run/cloudflare";
50
+ import type * as Entity from "effect/cluster/Entity";
51
+ import type { Sharding } from "effect/cluster/Sharding";
52
+ import type * as Duration from "effect/Duration";
53
+ import * as Effect from "effect/Effect";
54
+ import type * as Layer from "effect/Layer";
55
+ import type { PersistedQueueFactory } from "effect/persistence/PersistedQueue";
56
+ import type { WorkflowEngine } from "effect/workflow/WorkflowEngine";
57
+ import type { Cluster } from "./internal/cluster.ts";
58
+ declare global {
59
+ var __ALCHEMY_RUNTIME__: boolean | undefined;
60
+ }
61
+ /**
62
+ * The services `make` builds on top of the user layer: the cluster `Sharding`
63
+ * service, the workflow engine, and the persisted queue factory.
64
+ *
65
+ * @stability experimental
66
+ * @category models
67
+ * @since 4.1.3
68
+ */
69
+ export type ClusterServices = Sharding | WorkflowEngine | PersistedQueueFactory;
70
+ export type {
71
+ /**
72
+ * The handle returned by {@link make}.
73
+ *
74
+ * **Gotchas**
75
+ *
76
+ * Namespace bindings and `context` are available only in the deployed runtime.
77
+ * Reading them during plan evaluation throws a diagnostic; `provide` and
78
+ * `wake` remain inert during planning.
79
+ *
80
+ * @stability experimental
81
+ * @category models
82
+ * @since 4.1.3
83
+ */
84
+ Cluster } from "./internal/cluster.ts";
85
+ /**
86
+ * The entity definitions and handler layer the cluster is built from.
87
+ *
88
+ * @stability experimental
89
+ * @category models
90
+ * @since 4.1.3
91
+ */
92
+ export interface MakeOptions<ROut, E, RIn> {
93
+ /**
94
+ * The complete set of entity definitions the Worker serves, as in
95
+ * `CloudflareCluster.LayerOptions`.
96
+ */
97
+ readonly entities: ReadonlyArray<Entity.Entity<any, any>>;
98
+ /**
99
+ * The user's merged handler layers: entity handlers, singletons, workflow
100
+ * handlers, and any services they need.
101
+ */
102
+ readonly layer: Layer.Layer<ROut, E, RIn>;
103
+ /**
104
+ * Interval of the entity keep-alive heartbeat alarm, as in
105
+ * `CloudflareCluster.LayerOptions`. Defaults to 30 seconds.
106
+ */
107
+ readonly keepAliveHeartbeat?: Duration.Input | undefined;
108
+ }
109
+ /**
110
+ * Builds the Cloudflare cluster inside an Effect-native Alchemy Worker.
111
+ *
112
+ * **Details**
113
+ *
114
+ * Always registers all four Durable Object classes (entity, workflow, durable
115
+ * queue, singleton) on the hosting Worker, so Alchemy owns their bindings and
116
+ * SQLite migrations across deploys. At runtime it builds
117
+ * `CloudflareCluster.layer` merged with the user's handler layer into the
118
+ * isolate-lifetime Scope — the Scope is never closed, so do not wrap the
119
+ * Worker init program in `Effect.provide(layer)` for cluster services.
120
+ *
121
+ * The returned handle exposes `provide` for the Worker's handlers, `wake` for
122
+ * user-declared Cron Triggers
123
+ * (`Cloudflare.Workers.cron(expr, cluster.wake("name"))`), and the four
124
+ * native namespace bindings as escape hatches.
125
+ *
126
+ * A `ClusterCron` fires only after a Cron Trigger wakes its seed singleton.
127
+ * Pass `CloudflareCluster.clusterCronSingletonName(name)` to `wake` for it.
128
+ *
129
+ * @stability experimental
130
+ * @category constructors
131
+ * @since 4.1.3
132
+ */
133
+ export declare const make: <ROut, E, RIn>(options: MakeOptions<ROut, E, RIn>) => Effect.Effect<Cluster<ROut | ClusterServices>, E, Cloudflare.Worker | Exclude<RIn, ClusterServices>>;
134
+ //# sourceMappingURL=AlchemyCloudflareCluster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlchemyCloudflareCluster.d.ts","sourceRoot":"","sources":["../src/AlchemyCloudflareCluster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,KAAK,UAAU,MAAM,yBAAyB,CAAA;AACrD,OAAO,KAAK,KAAK,MAAM,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AASpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAEpD,OAAO,CAAC,MAAM,CAAC;IAGb,IAAI,mBAAmB,EAAE,OAAO,GAAG,SAAS,CAAA;CAC7C;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,cAAc,GAAG,qBAAqB,CAAA;AAE/E,YAAY;AACV;;;;;;;;;;;;GAYG;AACH,OAAO,EACR,MAAM,uBAAuB,CAAA;AAE9B;;;;;;GAMG;AACH,MAAM,WAAW,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG;IACvC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IACzD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;IACzC;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAA;CACzD;AA+CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAC9B,OAAO,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,KAC/B,MAAM,CAAC,MAAM,CAChB,OAAO,CAAC,IAAI,GAAG,eAAe,CAAC,EAC/B,CAAC,EACD,UAAU,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAC9B,CAAA"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Deploys the Cloudflare cluster with Alchemy v2 ("Infrastructure as
3
+ * Effects").
4
+ *
5
+ * `make` runs inside an Effect-native `Cloudflare.Worker` init program. It
6
+ * registers the four cluster Durable Object classes on the hosting Worker
7
+ * (bindings, class exports, and SQLite migrations are all owned by Alchemy),
8
+ * builds the cluster layer together with the user's handler layer into the
9
+ * isolate-lifetime scope, and returns a handle for wiring the built context
10
+ * into the Worker's handlers. The user never declares or re-exports Durable
11
+ * Object classes.
12
+ *
13
+ * The Wrangler path (`CloudflareDurableObjects` + `CloudflareCluster.layer`)
14
+ * never imports this module; `@alchemy.run/cloudflare` is an optional peer
15
+ * dependency needed only here.
16
+ *
17
+ * **Example** (Declaring a cluster)
18
+ *
19
+ * ```ts
20
+ * import * as Cloudflare from "@alchemy.run/cloudflare"
21
+ * import { Effect, Layer } from "effect"
22
+ * import { make } from "@effect/platform-cloudflare/AlchemyCloudflareCluster"
23
+ * import { CloudflareCluster } from "@effect/platform-cloudflare"
24
+ *
25
+ * export default Cloudflare.Worker("MyApp", {
26
+ * main: import.meta.url
27
+ * }, Effect.gen(function*() {
28
+ * const cluster = yield* make({
29
+ * entities: [Counter],
30
+ * layer: Layer.mergeAll(CounterLayer, MaintenanceLayer, NightlyReport)
31
+ * })
32
+ *
33
+ * yield* Cloudflare.Workers.cron("0 * * * *", cluster.wake("hourly-maintenance"))
34
+ * // A ClusterCron fires only after a Cron Trigger wakes its seed singleton
35
+ * yield* Cloudflare.Workers.cron(
36
+ * "30 * * * *",
37
+ * cluster.wake(CloudflareCluster.clusterCronSingletonName("nightly-report"))
38
+ * )
39
+ *
40
+ * return {
41
+ * fetch: cluster.provide(handler)
42
+ * }
43
+ * }).pipe(Effect.provide(Cloudflare.Workers.CronEventSourceLive)))
44
+ * ```
45
+ *
46
+ * @stability experimental
47
+ * @since 4.1.3
48
+ */
49
+ import * as Cloudflare from "@alchemy.run/cloudflare";
50
+ import * as Effect from "effect/Effect";
51
+ import { makeClusterDurableQueueProgram, makeClusterEntityProgram, makeClusterSingletonProgram, makeClusterWorkflowProgram } from "./CloudflareDurableObjectPrograms.js";
52
+ import { inertClusterHandle, makeClusterHandle } from "./internal/alchemyCluster.js";
53
+ // One alchemy Durable Object class per cluster program. The outer effect
54
+ // resolves the instance state; the inner effect runs per activation, under
55
+ // alchemy's blockConcurrencyWhile, once the shared isolate init (and so the
56
+ // user's handler registrations) has completed. The program handle is the RPC
57
+ // shape: every method returns an Effect and `alarm` matches alchemy's hook.
58
+ // `fetch` is omitted on purpose: cluster RPCs use the native bindings, so
59
+ // alchemy's default 404 stands in for the Wrangler classes' rejection.
60
+ const durableObject = (className, program) => Cloudflare.DurableObject()(className, Effect.map(Cloudflare.DurableObjectState, state => program(state.raw)));
61
+ const ClusterEntity = /*#__PURE__*/durableObject("ClusterEntity", makeClusterEntityProgram);
62
+ const ClusterWorkflow = /*#__PURE__*/durableObject("ClusterWorkflow", makeClusterWorkflowProgram);
63
+ const ClusterDurableQueue = /*#__PURE__*/durableObject("ClusterDurableQueue", makeClusterDurableQueueProgram);
64
+ const ClusterSingleton = /*#__PURE__*/durableObject("ClusterSingleton", makeClusterSingletonProgram);
65
+ const makeUnsafe = /*#__PURE__*/Effect.fnUntraced(function* (options) {
66
+ // Register the four Durable Object classes on the hosting Worker. At plan
67
+ // time this declares the bindings and class exports Alchemy deploys; at
68
+ // runtime it resolves the same bindings in every isolate, Worker and
69
+ // Durable Object alike.
70
+ yield* ClusterEntity;
71
+ yield* ClusterWorkflow;
72
+ yield* ClusterDurableQueue;
73
+ yield* ClusterSingleton;
74
+ if (!globalThis.__ALCHEMY_RUNTIME__) {
75
+ // Plan/deploy evaluation only discovers the declarations above.
76
+ return inertClusterHandle();
77
+ }
78
+ const env = yield* Cloudflare.WorkerEnvironment;
79
+ return yield* makeClusterHandle({
80
+ entities: options.entities,
81
+ layer: options.layer,
82
+ env,
83
+ keepAliveHeartbeat: options.keepAliveHeartbeat
84
+ });
85
+ });
86
+ /**
87
+ * Builds the Cloudflare cluster inside an Effect-native Alchemy Worker.
88
+ *
89
+ * **Details**
90
+ *
91
+ * Always registers all four Durable Object classes (entity, workflow, durable
92
+ * queue, singleton) on the hosting Worker, so Alchemy owns their bindings and
93
+ * SQLite migrations across deploys. At runtime it builds
94
+ * `CloudflareCluster.layer` merged with the user's handler layer into the
95
+ * isolate-lifetime Scope — the Scope is never closed, so do not wrap the
96
+ * Worker init program in `Effect.provide(layer)` for cluster services.
97
+ *
98
+ * The returned handle exposes `provide` for the Worker's handlers, `wake` for
99
+ * user-declared Cron Triggers
100
+ * (`Cloudflare.Workers.cron(expr, cluster.wake("name"))`), and the four
101
+ * native namespace bindings as escape hatches.
102
+ *
103
+ * A `ClusterCron` fires only after a Cron Trigger wakes its seed singleton.
104
+ * Pass `CloudflareCluster.clusterCronSingletonName(name)` to `wake` for it.
105
+ *
106
+ * @stability experimental
107
+ * @category constructors
108
+ * @since 4.1.3
109
+ */
110
+ export const make = makeUnsafe;
111
+ //# sourceMappingURL=AlchemyCloudflareCluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AlchemyCloudflareCluster.js","names":[],"sources":["../src/AlchemyCloudflareCluster.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,OAAO,KAAK,UAAU,MAAM,yBAAyB;AAIrD,OAAO,KAAK,MAAM,MAAM,eAAe;AAIvC,SAEE,8BAA8B,EAC9B,wBAAwB,EACxB,2BAA2B,EAC3B,0BAA0B,QACrB,sCAAsC;AAC7C,SAAS,kBAAkB,EAAE,iBAAiB,QAAQ,8BAA8B;AA6DpF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,aAAa,GAAG,CACpB,SAAiB,EACjB,OAAmE,KAEnE,UAAU,CAAC,aAAa,EAAS,CAC/B,SAAS,EACT,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,kBAAkB,EAAG,KAAK,IAAK,OAAO,CAAC,KAAK,CAAC,GAAgC,CAAC,CAAC,CACtG;AAEH,MAAM,aAAa,gBAAG,aAAa,CAAC,eAAe,EAAE,wBAAwB,CAAC;AAC9E,MAAM,eAAe,gBAAG,aAAa,CAAC,iBAAiB,EAAE,0BAA0B,CAAC;AACpF,MAAM,mBAAmB,gBAAG,aAAa,CAAC,qBAAqB,EAAE,8BAA8B,CAAC;AAChG,MAAM,gBAAgB,gBAAG,aAAa,CAAC,kBAAkB,EAAE,2BAA2B,CAAC;AAEvF,MAAM,UAAU,gBAAG,MAAM,CAAC,UAAU,CAAC,WAAU,OAAmC;EAChF;EACA;EACA;EACA;EACA,OAAO,aAAa;EACpB,OAAO,eAAe;EACtB,OAAO,mBAAmB;EAC1B,OAAO,gBAAgB;EAEvB,IAAI,CAAC,UAAU,CAAC,mBAAmB,EAAE;IACnC;IACA,OAAO,kBAAkB,EAAE;EAC7B;EAEA,MAAM,GAAG,GAAG,OAAO,UAAU,CAAC,iBAAiB;EAC/C,OAAO,OAAO,iBAAiB,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;IAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,GAAG;IACH,kBAAkB,EAAE,OAAO,CAAC;GAC7B,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,OAAO,MAAM,IAAI,GAMb,UAAiB","ignoreList":[]}
@@ -0,0 +1,196 @@
1
+ import type * as Entity from "effect/cluster/Entity";
2
+ import { Sharding } from "effect/cluster/Sharding";
3
+ import * as Context from "effect/Context";
4
+ import * as Duration from "effect/Duration";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Layer from "effect/Layer";
7
+ import type { PersistedQueueFactory } from "effect/persistence/PersistedQueue";
8
+ import type * as SqlClient from "effect/sql/SqlClient";
9
+ import type { WorkflowEngine } from "effect/workflow/WorkflowEngine";
10
+ /**
11
+ * A Durable Object name decoded back into its entity address parts.
12
+ *
13
+ * @stability experimental
14
+ * @category models
15
+ * @since 4.1.3
16
+ */
17
+ export interface ClusterName {
18
+ readonly type: string;
19
+ readonly id: string;
20
+ }
21
+ /**
22
+ * Encodes an entity address into the Durable Object name used with
23
+ * `getByName`.
24
+ *
25
+ * **Details**
26
+ *
27
+ * The name is the entity type length-prefixed as `` `${type.length}:${type}${id}` ``,
28
+ * which keeps `(type, id)` pairs collision-free without restricting the
29
+ * characters an entity id may contain. Workflow, queue, and singleton names use
30
+ * the same scheme on their own namespaces.
31
+ *
32
+ * @stability experimental
33
+ * @category encoding
34
+ * @since 4.1.3
35
+ */
36
+ export declare const encodeName: (type: string, id: string) => string;
37
+ /**
38
+ * Decodes a Durable Object name produced by {@link encodeName} back into its
39
+ * entity address parts.
40
+ *
41
+ * **Details**
42
+ *
43
+ * Returns `undefined` for names that were not produced by {@link encodeName},
44
+ * including non-canonical length prefixes. A Durable Object uses this to
45
+ * recover its own address from `ctx.id.name`.
46
+ *
47
+ * @stability experimental
48
+ * @category decoding
49
+ * @since 4.1.3
50
+ */
51
+ export declare const decodeName: (name: string) => ClusterName | undefined;
52
+ /**
53
+ * Returns the singleton name of the seed singleton that `ClusterCron.make`
54
+ * registers for the cron job with the given name.
55
+ *
56
+ * **When to use**
57
+ *
58
+ * Use to wake a `ClusterCron` from a Worker Cron Trigger, by passing the
59
+ * result to `cluster.wake` with Alchemy or by prefixing it with `Singleton/`
60
+ * for `getByName` on the singleton namespace with Wrangler.
61
+ *
62
+ * **Details**
63
+ *
64
+ * The result is `"ClusterCron/<name>"`, the singleton name without the
65
+ * `Singleton/` Durable Object name prefix.
66
+ *
67
+ * **Gotchas**
68
+ *
69
+ * A singleton runs only when its Durable Object is woken, so a `ClusterCron`
70
+ * never schedules its first run until a Cron Trigger wakes this singleton.
71
+ * Each later run is scheduled through a destination alarm.
72
+ *
73
+ * @stability experimental
74
+ * @category encoding
75
+ * @since 4.1.3
76
+ */
77
+ export declare const clusterCronSingletonName: (name: string) => string;
78
+ /**
79
+ * The Durable Object namespace bindings and entity definitions the cluster
80
+ * layer is built from.
81
+ *
82
+ * **Details**
83
+ *
84
+ * `entities` is the complete set of entity definitions the Worker serves.
85
+ * Handlers are attached per entity type with `Entity.toLayer`; a client or
86
+ * handler registration for an entity type outside this set fails at the
87
+ * Worker, before any Durable Object is contacted.
88
+ *
89
+ * `keepAliveHeartbeat` (default 30 seconds) is the interval of the alarm an
90
+ * entity Durable Object keeps armed while it holds `Entity.keepAlive` or runs
91
+ * a persisted request. Cloudflare never re-creates an evicted object on its
92
+ * own, so this alarm is what rebuilds a kept-alive entity and replays an
93
+ * interrupted request after a restart or deploy. It bounds recovery latency;
94
+ * it is not a liveness check.
95
+ *
96
+ * @stability experimental
97
+ * @category layers
98
+ * @since 4.1.3
99
+ */
100
+ export interface LayerOptions {
101
+ readonly entities: ReadonlyArray<Entity.Entity<any, any>>;
102
+ readonly entityNamespace: DurableObjectNamespace;
103
+ readonly workflowNamespace: DurableObjectNamespace;
104
+ readonly queueNamespace: DurableObjectNamespace;
105
+ readonly singletonNamespace: DurableObjectNamespace;
106
+ readonly keepAliveHeartbeat?: Duration.Input | undefined;
107
+ }
108
+ /**
109
+ * Builds the cluster on Cloudflare Durable Objects.
110
+ *
111
+ * **Details**
112
+ *
113
+ * Provides the cluster `Sharding` service on top of the four same-Worker
114
+ * Durable Object namespace bindings, plus the `WorkflowEngine` backed by the
115
+ * workflow class and the `PersistedQueueFactory` backed by the queue class, so
116
+ * the `DurableQueue` user API works out of the box. `Entity.client` resolves
117
+ * an entity to its Durable Object by
118
+ * encoding `(type, id)` with {@link encodeName} and calling `getByName`; an
119
+ * unknown entity type or a bad encode fails at the Worker before any Durable
120
+ * Object is contacted. Entity handlers registered with `Entity.toLayer` are
121
+ * recorded per `EntityType` at Worker init and built once per Durable Object
122
+ * wake; workflow handlers registered with `Workflow.toLayer` follow the same
123
+ * pattern on the workflow class.
124
+ *
125
+ * @stability experimental
126
+ * @category layers
127
+ * @since 4.1.3
128
+ */
129
+ export declare const layer: (options: LayerOptions) => Layer.Layer<Sharding | WorkflowEngine | PersistedQueueFactory>;
130
+ declare const DurableObjectSqlClient_base: Context.ServiceClass<DurableObjectSqlClient, "@effect/platform-cloudflare/CloudflareCluster/DurableObjectSqlClient", SqlClient.SqlClient>;
131
+ /**
132
+ * An `SqlClient` on the current entity Durable Object's own SQLite storage.
133
+ *
134
+ * **When to use**
135
+ *
136
+ * Use to keep rows next to the entity's mailbox from an entity handler build
137
+ * or handler, such as state that must survive eviction and commit atomically
138
+ * with each step. Register the handlers with `Entity.toLayer` and read the
139
+ * client with {@link DurableObjectSqlClient.orDie} in the build.
140
+ *
141
+ * **Details**
142
+ *
143
+ * Each entity Durable Object provides this service, an `@effect/sql-sqlite-do`
144
+ * client, to its handler build and handlers, including handlers registered
145
+ * with `Entity.toLayerQueue`. Yielding the tag directly leaves it as a layer
146
+ * requirement; `orDie` has none. It is a separate tag, so any
147
+ * `SqlClient` from the Worker layer (a D1 database, say) stays reachable inside
148
+ * the handlers. To run code written against `SqlClient.SqlClient`, provide this
149
+ * client as that service locally.
150
+ *
151
+ * `withTransaction` uses the object's storage transaction, so several rows
152
+ * commit or roll back together. Rows live and die with the entity's Durable
153
+ * Object and cannot be queried from outside it.
154
+ *
155
+ * **Gotchas**
156
+ *
157
+ * - Outside an entity Durable Object, `orDie` dies on the first handler build.
158
+ * In tests, provide this service from any SQL layer.
159
+ * - Table names starting with `cluster_` are reserved for the entity mailbox.
160
+ * - A row, like an encoded message, cannot exceed 2 MB.
161
+ * - A storage transaction closes the Durable Object's input gate until it
162
+ * finishes, so timers and I/O awaited inside `withTransaction` never
163
+ * resume. Keep transactions to SQL work.
164
+ * - Mailbox writes wait for an open transaction to finish, so a rollback never
165
+ * takes another handler's stored reply with it.
166
+ *
167
+ * **Example** (Storing rows from entity handlers)
168
+ *
169
+ * ```ts
170
+ * import { CloudflareCluster } from "@effect/platform-cloudflare"
171
+ * import { Effect } from "effect"
172
+ *
173
+ * const JournalLayer = Journal.toLayer(Effect.gen(function*() {
174
+ * const sql = yield* CloudflareCluster.DurableObjectSqlClient.orDie
175
+ * yield* sql`CREATE TABLE IF NOT EXISTS journal (entry TEXT NOT NULL)`.pipe(Effect.orDie)
176
+ * return Journal.of({
177
+ * Append: ({ payload }) => sql`INSERT INTO journal (entry) VALUES (${payload.entry})`.pipe(Effect.orDie)
178
+ * })
179
+ * }))
180
+ * ```
181
+ *
182
+ * @stability experimental
183
+ * @category services
184
+ * @since 4.1.3
185
+ */
186
+ export declare class DurableObjectSqlClient extends DurableObjectSqlClient_base {
187
+ /**
188
+ * Gets the current entity's SQL client, or dies if no client is provided.
189
+ *
190
+ * @stability experimental
191
+ * @since 4.1.3
192
+ */
193
+ static readonly orDie: Effect.Effect<SqlClient.SqlClient>;
194
+ }
195
+ export {};
196
+ //# sourceMappingURL=CloudflareCluster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareCluster.d.ts","sourceRoot":"","sources":["../src/CloudflareCluster.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,KAAK,MAAM,MAAM,uBAAuB,CAAA;AAKpD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AAEzC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AAO9E,OAAO,KAAK,KAAK,SAAS,MAAM,sBAAsB,CAAA;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAepE;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAA4B,CAAA;AAEnF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,WAAW,GAAG,SAA+B,CAAA;AAExF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,wBAAwB,SAAU,MAAM,KAAG,MAA+B,CAAA;AAEvF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IACzD,QAAQ,CAAC,eAAe,EAAE,sBAAsB,CAAA;IAChD,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB,CAAA;IAClD,QAAQ,CAAC,cAAc,EAAE,sBAAsB,CAAA;IAC/C,QAAQ,CAAC,kBAAkB,EAAE,sBAAsB,CAAA;IACnD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,SAAS,CAAA;CACzD;AAybD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,KAAK,YACP,YAAY,KACpB,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,cAAc,GAAG,qBAAqB,CAK7D,CAAA;;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,qBAAa,sBAAuB,SAAQ,2BAE3C;IACC;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAWxD;CACF"}