@alchemy.run/effect-cluster 0.0.0-stage → 2.0.0-beta.86

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 (233) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +105 -2
  3. package/lib/AWS/Database.d.ts +54 -0
  4. package/lib/AWS/Database.d.ts.map +1 -0
  5. package/lib/AWS/Database.js +96 -0
  6. package/lib/AWS/Database.js.map +1 -0
  7. package/lib/AWS/EcsPool.d.ts +56 -0
  8. package/lib/AWS/EcsPool.d.ts.map +1 -0
  9. package/lib/AWS/EcsPool.js +87 -0
  10. package/lib/AWS/EcsPool.js.map +1 -0
  11. package/lib/AWS/index.d.ts +7 -0
  12. package/lib/AWS/index.d.ts.map +1 -0
  13. package/lib/AWS/index.js +7 -0
  14. package/lib/AWS/index.js.map +1 -0
  15. package/lib/AWS/internal/Gateway.d.ts +23 -0
  16. package/lib/AWS/internal/Gateway.d.ts.map +1 -0
  17. package/lib/AWS/internal/Gateway.js +70 -0
  18. package/lib/AWS/internal/Gateway.js.map +1 -0
  19. package/lib/Cloudflare/DurableObjectPool.d.ts +42 -0
  20. package/lib/Cloudflare/DurableObjectPool.d.ts.map +1 -0
  21. package/lib/Cloudflare/DurableObjectPool.js +136 -0
  22. package/lib/Cloudflare/DurableObjectPool.js.map +1 -0
  23. package/lib/Cloudflare/PoolWorker.d.ts +186 -0
  24. package/lib/Cloudflare/PoolWorker.d.ts.map +1 -0
  25. package/lib/Cloudflare/PoolWorker.js +75 -0
  26. package/lib/Cloudflare/PoolWorker.js.map +1 -0
  27. package/lib/Cloudflare/index.d.ts +7 -0
  28. package/lib/Cloudflare/index.d.ts.map +1 -0
  29. package/lib/Cloudflare/index.js +7 -0
  30. package/lib/Cloudflare/index.js.map +1 -0
  31. package/lib/Cloudflare/internal/Objects.d.ts +38 -0
  32. package/lib/Cloudflare/internal/Objects.d.ts.map +1 -0
  33. package/lib/Cloudflare/internal/Objects.js +102 -0
  34. package/lib/Cloudflare/internal/Objects.js.map +1 -0
  35. package/lib/Cron.d.ts +143 -0
  36. package/lib/Cron.d.ts.map +1 -0
  37. package/lib/Cron.js +83 -0
  38. package/lib/Cron.js.map +1 -0
  39. package/lib/Database.d.ts +95 -0
  40. package/lib/Database.d.ts.map +1 -0
  41. package/lib/Database.js +38 -0
  42. package/lib/Database.js.map +1 -0
  43. package/lib/Docker/Database.d.ts +42 -0
  44. package/lib/Docker/Database.d.ts.map +1 -0
  45. package/lib/Docker/Database.js +53 -0
  46. package/lib/Docker/Database.js.map +1 -0
  47. package/lib/Docker/SwarmPool.d.ts +44 -0
  48. package/lib/Docker/SwarmPool.d.ts.map +1 -0
  49. package/lib/Docker/SwarmPool.js +56 -0
  50. package/lib/Docker/SwarmPool.js.map +1 -0
  51. package/lib/Docker/index.d.ts +7 -0
  52. package/lib/Docker/index.d.ts.map +1 -0
  53. package/lib/Docker/index.js +7 -0
  54. package/lib/Docker/index.js.map +1 -0
  55. package/lib/Entity.d.ts +348 -0
  56. package/lib/Entity.d.ts.map +1 -0
  57. package/lib/Entity.js +308 -0
  58. package/lib/Entity.js.map +1 -0
  59. package/lib/Fly/Database.d.ts +50 -0
  60. package/lib/Fly/Database.d.ts.map +1 -0
  61. package/lib/Fly/Database.js +56 -0
  62. package/lib/Fly/Database.js.map +1 -0
  63. package/lib/Fly/MachinePool.d.ts +39 -0
  64. package/lib/Fly/MachinePool.d.ts.map +1 -0
  65. package/lib/Fly/MachinePool.js +51 -0
  66. package/lib/Fly/MachinePool.js.map +1 -0
  67. package/lib/Fly/index.d.ts +7 -0
  68. package/lib/Fly/index.d.ts.map +1 -0
  69. package/lib/Fly/index.js +7 -0
  70. package/lib/Fly/index.js.map +1 -0
  71. package/lib/GCP/ComputeEnginePool.d.ts +41 -0
  72. package/lib/GCP/ComputeEnginePool.d.ts.map +1 -0
  73. package/lib/GCP/ComputeEnginePool.js +172 -0
  74. package/lib/GCP/ComputeEnginePool.js.map +1 -0
  75. package/lib/GCP/Database.d.ts +48 -0
  76. package/lib/GCP/Database.d.ts.map +1 -0
  77. package/lib/GCP/Database.js +115 -0
  78. package/lib/GCP/Database.js.map +1 -0
  79. package/lib/GCP/index.d.ts +7 -0
  80. package/lib/GCP/index.d.ts.map +1 -0
  81. package/lib/GCP/index.js +7 -0
  82. package/lib/GCP/index.js.map +1 -0
  83. package/lib/Hetzner/Database.d.ts +48 -0
  84. package/lib/Hetzner/Database.d.ts.map +1 -0
  85. package/lib/Hetzner/Database.js +91 -0
  86. package/lib/Hetzner/Database.js.map +1 -0
  87. package/lib/Hetzner/ServerPool.d.ts +35 -0
  88. package/lib/Hetzner/ServerPool.d.ts.map +1 -0
  89. package/lib/Hetzner/ServerPool.js +98 -0
  90. package/lib/Hetzner/ServerPool.js.map +1 -0
  91. package/lib/Hetzner/index.d.ts +7 -0
  92. package/lib/Hetzner/index.d.ts.map +1 -0
  93. package/lib/Hetzner/index.js +7 -0
  94. package/lib/Hetzner/index.js.map +1 -0
  95. package/lib/Host.d.ts +60 -0
  96. package/lib/Host.d.ts.map +1 -0
  97. package/lib/Host.js +31 -0
  98. package/lib/Host.js.map +1 -0
  99. package/lib/Kubernetes/Database.d.ts +40 -0
  100. package/lib/Kubernetes/Database.d.ts.map +1 -0
  101. package/lib/Kubernetes/Database.js +56 -0
  102. package/lib/Kubernetes/Database.js.map +1 -0
  103. package/lib/Kubernetes/DeploymentPool.d.ts +54 -0
  104. package/lib/Kubernetes/DeploymentPool.d.ts.map +1 -0
  105. package/lib/Kubernetes/DeploymentPool.js +49 -0
  106. package/lib/Kubernetes/DeploymentPool.js.map +1 -0
  107. package/lib/Kubernetes/index.d.ts +7 -0
  108. package/lib/Kubernetes/index.d.ts.map +1 -0
  109. package/lib/Kubernetes/index.js +7 -0
  110. package/lib/Kubernetes/index.js.map +1 -0
  111. package/lib/Pool.d.ts +66 -0
  112. package/lib/Pool.d.ts.map +1 -0
  113. package/lib/Pool.js +47 -0
  114. package/lib/Pool.js.map +1 -0
  115. package/lib/Providers.d.ts +19 -0
  116. package/lib/Providers.d.ts.map +1 -0
  117. package/lib/Providers.js +31 -0
  118. package/lib/Providers.js.map +1 -0
  119. package/lib/ShardGroups.d.ts +24 -0
  120. package/lib/ShardGroups.d.ts.map +1 -0
  121. package/lib/ShardGroups.js +19 -0
  122. package/lib/ShardGroups.js.map +1 -0
  123. package/lib/Singleton.d.ts +110 -0
  124. package/lib/Singleton.d.ts.map +1 -0
  125. package/lib/Singleton.js +60 -0
  126. package/lib/Singleton.js.map +1 -0
  127. package/lib/Workflow.d.ts +251 -0
  128. package/lib/Workflow.d.ts.map +1 -0
  129. package/lib/Workflow.js +288 -0
  130. package/lib/Workflow.js.map +1 -0
  131. package/lib/index.d.ts +12 -0
  132. package/lib/index.d.ts.map +1 -0
  133. package/lib/index.js +12 -0
  134. package/lib/index.js.map +1 -0
  135. package/lib/internal/Bootstrap.d.ts +28 -0
  136. package/lib/internal/Bootstrap.d.ts.map +1 -0
  137. package/lib/internal/Bootstrap.js +257 -0
  138. package/lib/internal/Bootstrap.js.map +1 -0
  139. package/lib/internal/EntityRuntime.d.ts +40 -0
  140. package/lib/internal/EntityRuntime.d.ts.map +1 -0
  141. package/lib/internal/EntityRuntime.js +698 -0
  142. package/lib/internal/EntityRuntime.js.map +1 -0
  143. package/lib/internal/Gateway.d.ts +82 -0
  144. package/lib/internal/Gateway.d.ts.map +1 -0
  145. package/lib/internal/Gateway.js +218 -0
  146. package/lib/internal/Gateway.js.map +1 -0
  147. package/lib/internal/HostedPlatform.d.ts +36 -0
  148. package/lib/internal/HostedPlatform.d.ts.map +1 -0
  149. package/lib/internal/HostedPlatform.js +36 -0
  150. package/lib/internal/HostedPlatform.js.map +1 -0
  151. package/lib/internal/HostedRuntime.d.ts +58 -0
  152. package/lib/internal/HostedRuntime.d.ts.map +1 -0
  153. package/lib/internal/HostedRuntime.js +72 -0
  154. package/lib/internal/HostedRuntime.js.map +1 -0
  155. package/lib/internal/Naming.d.ts +12 -0
  156. package/lib/internal/Naming.d.ts.map +1 -0
  157. package/lib/internal/Naming.js +40 -0
  158. package/lib/internal/Naming.js.map +1 -0
  159. package/lib/internal/ObjectSharding.d.ts +141 -0
  160. package/lib/internal/ObjectSharding.d.ts.map +1 -0
  161. package/lib/internal/ObjectSharding.js +337 -0
  162. package/lib/internal/ObjectSharding.js.map +1 -0
  163. package/lib/internal/PoolHost.d.ts +80 -0
  164. package/lib/internal/PoolHost.d.ts.map +1 -0
  165. package/lib/internal/PoolHost.js +203 -0
  166. package/lib/internal/PoolHost.js.map +1 -0
  167. package/lib/internal/PoolHostService.d.ts +25 -0
  168. package/lib/internal/PoolHostService.d.ts.map +1 -0
  169. package/lib/internal/PoolHostService.js +10 -0
  170. package/lib/internal/PoolHostService.js.map +1 -0
  171. package/lib/internal/Wire.d.ts +60 -0
  172. package/lib/internal/Wire.d.ts.map +1 -0
  173. package/lib/internal/Wire.js +36 -0
  174. package/lib/internal/Wire.js.map +1 -0
  175. package/lib/internal/WorkflowGateway.d.ts +36 -0
  176. package/lib/internal/WorkflowGateway.d.ts.map +1 -0
  177. package/lib/internal/WorkflowGateway.js +124 -0
  178. package/lib/internal/WorkflowGateway.js.map +1 -0
  179. package/lib/internal/external-runner.d.ts +2 -0
  180. package/lib/internal/external-runner.d.ts.map +1 -0
  181. package/lib/internal/external-runner.js +7 -0
  182. package/lib/internal/external-runner.js.map +1 -0
  183. package/lib/internal/runner.d.ts +2 -0
  184. package/lib/internal/runner.d.ts.map +1 -0
  185. package/lib/internal/runner.js +5 -0
  186. package/lib/internal/runner.js.map +1 -0
  187. package/package.json +143 -3
  188. package/src/AWS/Database.ts +139 -0
  189. package/src/AWS/EcsPool.ts +118 -0
  190. package/src/AWS/index.ts +6 -0
  191. package/src/AWS/internal/Gateway.ts +82 -0
  192. package/src/Cloudflare/DurableObjectPool.ts +198 -0
  193. package/src/Cloudflare/PoolWorker.ts +124 -0
  194. package/src/Cloudflare/index.ts +6 -0
  195. package/src/Cloudflare/internal/Objects.ts +160 -0
  196. package/src/Cron.ts +202 -0
  197. package/src/Database.ts +116 -0
  198. package/src/Docker/Database.ts +94 -0
  199. package/src/Docker/SwarmPool.ts +83 -0
  200. package/src/Docker/index.ts +6 -0
  201. package/src/Entity.ts +621 -0
  202. package/src/Fly/Database.ts +102 -0
  203. package/src/Fly/MachinePool.ts +72 -0
  204. package/src/Fly/index.ts +6 -0
  205. package/src/GCP/ComputeEnginePool.ts +203 -0
  206. package/src/GCP/Database.ts +168 -0
  207. package/src/GCP/index.ts +6 -0
  208. package/src/Hetzner/Database.ts +142 -0
  209. package/src/Hetzner/ServerPool.ts +116 -0
  210. package/src/Hetzner/index.ts +6 -0
  211. package/src/Host.ts +68 -0
  212. package/src/Kubernetes/Database.ts +89 -0
  213. package/src/Kubernetes/DeploymentPool.ts +77 -0
  214. package/src/Kubernetes/index.ts +6 -0
  215. package/src/Pool.ts +104 -0
  216. package/src/Providers.ts +49 -0
  217. package/src/ShardGroups.ts +41 -0
  218. package/src/Singleton.ts +153 -0
  219. package/src/Workflow.ts +556 -0
  220. package/src/index.ts +11 -0
  221. package/src/internal/Bootstrap.ts +368 -0
  222. package/src/internal/EntityRuntime.ts +996 -0
  223. package/src/internal/Gateway.ts +366 -0
  224. package/src/internal/HostedPlatform.ts +83 -0
  225. package/src/internal/HostedRuntime.ts +118 -0
  226. package/src/internal/Naming.ts +44 -0
  227. package/src/internal/ObjectSharding.ts +578 -0
  228. package/src/internal/PoolHost.ts +330 -0
  229. package/src/internal/PoolHostService.ts +25 -0
  230. package/src/internal/Wire.ts +83 -0
  231. package/src/internal/WorkflowGateway.ts +188 -0
  232. package/src/internal/external-runner.ts +10 -0
  233. package/src/internal/runner.ts +5 -0
package/lib/Entity.js ADDED
@@ -0,0 +1,308 @@
1
+ import * as nodePath from "node:path";
2
+ import * as Output from "alchemy/Output";
3
+ import { Platform, } from "alchemy/Platform";
4
+ import * as Provider from "alchemy/Provider";
5
+ import { RuntimeContext, sanitizeKey } from "alchemy/RuntimeContext";
6
+ import { ClusterSchema, Entity as EffectEntity } from "effect/cluster";
7
+ import * as Data from "effect/Data";
8
+ import * as Effect from "effect/Effect";
9
+ import * as Effectable from "effect/Effectable";
10
+ import * as Layer from "effect/Layer";
11
+ import * as Option from "effect/Option";
12
+ import * as Redacted from "effect/Redacted";
13
+ import { bindRoute, makeNamespace } from "./internal/Gateway.js";
14
+ import { makeHostedRuntimeContext } from "./internal/HostedRuntime.js";
15
+ import { PoolHost } from "./internal/PoolHostService.js";
16
+ export const EntityTypeId = "Cluster.Entity";
17
+ /** A class yielded outside any runtime was called. */
18
+ export class EntityCalledOutsideRuntime extends Data.TaggedError("Cluster.EntityCalledOutsideRuntime") {
19
+ }
20
+ /** The Effect entity behind each declared type, for the runtime context. */
21
+ const entities = new Map();
22
+ const createRuntimeContext = makeHostedRuntimeContext(EntityTypeId, (id, instance, context, props) => ({
23
+ kind: "entity",
24
+ entity: entities.get(id),
25
+ instance: instance,
26
+ context,
27
+ options: {
28
+ maxIdleTime: props?.maxIdleTime,
29
+ concurrency: props?.concurrency,
30
+ mailboxCapacity: props?.mailboxCapacity,
31
+ disableFatalDefects: props?.disableFatalDefects,
32
+ },
33
+ }),
34
+ // Every RPC's middleware must reach the pool with the entity.
35
+ (id, context) => {
36
+ const missing = [...entities.get(id).protocol.requests.values()].flatMap((rpc) => [...rpc.middlewares]
37
+ .filter((middleware) => !context.mapUnsafe.has(middleware.key))
38
+ .map((middleware) => `${rpc._tag} uses ${middleware.key}`));
39
+ return missing.length === 0
40
+ ? Effect.void
41
+ : Effect.die(new EntityMiddlewareMissing({
42
+ message: `Cluster.Entity '${id}' is missing RPC middleware (${missing.join(", ")}). ` +
43
+ `Provide its Layer with the pool: Effect.provide(Default.pipe(Layer.provide(AuthLive))).`,
44
+ }));
45
+ });
46
+ /** An entity's RPC uses middleware nothing provided to its pool. */
47
+ export class EntityMiddlewareMissing extends Data.TaggedError("Cluster.EntityMiddlewareMissing") {
48
+ }
49
+ const platform = Platform(EntityTypeId, { createRuntimeContext });
50
+ /** The resource type behind every class, which the providers register. @internal */
51
+ export const EntityResource = platform;
52
+ /** The Platform declaration behind a class, which a runner builds. @internal */
53
+ export const PlatformDeclaration = Symbol.for("alchemy/Cluster/Entity/Platform");
54
+ /** The Effect entity a class hosts, which a runner reads. @internal */
55
+ export const EntityDefinition = Symbol.for("alchemy/Cluster/Entity/Definition");
56
+ /**
57
+ * An **Entity** in an Effect Cluster: a stateful, addressable instance per
58
+ * entity id, whose messages are typed by its RPCs. Runners place each
59
+ * instance on one runner and deliver its messages one at a time.
60
+ *
61
+ * Declare it from an Effect Cluster entity with its implementation, and
62
+ * the class is both what callers `yield*` and what deploys it. Or declare
63
+ * it bare, like Effect Cluster's `Entity.make`, from a type and its RPCs,
64
+ * and implement it with `.make(props, impl)`, as a Layer: callers import
65
+ * the bare declaration without bundling the implementation or its pool.
66
+ *
67
+ * The implementation has two phases. The outer Effect runs once per runner
68
+ * and resolves bindings. The inner Effect runs once per instance, on its
69
+ * first message, and returns its handlers, typed by the entity's `.of`. Providing a
70
+ * pool Layer to the implementation, such as
71
+ * `ClusterDocker.SwarmPool("Default")`, is what deploys it.
72
+ *
73
+ * `yield* Counter` from any runtime returns the entity's namespace, like a
74
+ * Durable Object's: `getByName(entityId)` returns a client with the same
75
+ * shape as Effect Cluster's `Entity.client(entityId)`. Inside the cluster,
76
+ * calls go through Effect Cluster directly; elsewhere they go through the
77
+ * pool's gateway over HTTP, so callers need no database access and no
78
+ * route to the pool.
79
+ *
80
+ * ### Declaring an entity
81
+ * **Example:** A counter, with its implementation
82
+ * ```typescript
83
+ * // src/Counter.ts
84
+ * const Counters = Entity.make("Counter", [
85
+ * Rpc.make("Increment", { payload: { amount: Schema.Int }, success: Schema.Int }),
86
+ * ]);
87
+ *
88
+ * export default class Counter extends Cluster.Entity<Counter>()(
89
+ * Counters,
90
+ * { main: import.meta.url },
91
+ * Effect.gen(function* () {
92
+ * return Effect.gen(function* () {
93
+ * const count = yield* Ref.make(0);
94
+ * return Counters.of({
95
+ * Increment: ({ payload }) => Ref.updateAndGet(count, (n) => n + payload.amount),
96
+ * });
97
+ * });
98
+ * }).pipe(Effect.provide(Default)),
99
+ * ) {}
100
+ * ```
101
+ *
102
+ * ### Keeping its code out of its callers
103
+ * **Example:** A bare declaration, which callers import
104
+ * ```typescript
105
+ * // src/Counter.ts
106
+ * export const Increment = Rpc.make("Increment", {
107
+ * payload: { amount: Schema.Int },
108
+ * success: Schema.Int,
109
+ * });
110
+ *
111
+ * export class Counter extends Cluster.Entity<Counter>()("Counter", [Increment]) {}
112
+ * ```
113
+ *
114
+ * **Example:** An existing Effect Cluster entity
115
+ * ```typescript
116
+ * import { Counter as CounterEntity } from "./entities/counter.ts";
117
+ *
118
+ * export class Counter extends Cluster.Entity<Counter>()(CounterEntity) {}
119
+ * ```
120
+ *
121
+ * **Example:** Its implementation, as a Layer, deployed to a pool
122
+ * ```typescript
123
+ * // src/CounterLive.ts
124
+ * export default Counter.make(
125
+ * { main: import.meta.url },
126
+ * Effect.gen(function* () {
127
+ * return Effect.gen(function* () {
128
+ * const count = yield* Ref.make(0);
129
+ * return Counter.of({
130
+ * Increment: ({ payload }) => Ref.updateAndGet(count, (n) => n + payload.amount),
131
+ * });
132
+ * });
133
+ * }).pipe(Effect.provide(Default)),
134
+ * );
135
+ * ```
136
+ *
137
+ * **Example:** Middleware, provided with the pool
138
+ * ```typescript
139
+ * export const Increment = Rpc.make("Increment", {
140
+ * payload: { amount: Schema.Int },
141
+ * success: Schema.Int,
142
+ * }).middleware(Auth);
143
+ *
144
+ * export default Counter.make(
145
+ * { main: import.meta.url },
146
+ * Effect.gen(function* () {
147
+ * return Effect.gen(function* () {
148
+ * return Counter.of({
149
+ * Increment: () => Effect.map(CurrentUser, (user) => user.id.length),
150
+ * });
151
+ * });
152
+ * }).pipe(Effect.provide(Default.pipe(Layer.provide(AuthLive)))),
153
+ * );
154
+ * ```
155
+ *
156
+ * **Example:** An existing Effect Cluster program, run as it is
157
+ * ```typescript
158
+ * import { Tally as TallyEntity } from "./entities/tally.ts";
159
+ *
160
+ * export const Tally = Cluster.Entity(TallyEntity, {
161
+ * main: "./entities/tally.ts",
162
+ * program: "./runner.ts",
163
+ * }).pipe(Effect.provide(Legacy));
164
+ * ```
165
+ *
166
+ * ### Calling an entity
167
+ * **Example:** From any runtime
168
+ * ```typescript
169
+ * const counters = yield* Counter;
170
+ * const counter = yield* counters.getByName("visits");
171
+ * const count = yield* counter.Increment({ amount: 1 });
172
+ * ```
173
+ *
174
+ * **Example:** Without waiting for the reply
175
+ * ```typescript
176
+ * yield* counter.Increment({ amount: 1 }, { discard: true });
177
+ * ```
178
+ *
179
+ * **Example:** Through client middleware
180
+ * ```typescript
181
+ * const counter = yield* counters.getByName("visits");
182
+ * yield* counter.Increment({ amount: 1 }).pipe(Effect.provide(AuthClient));
183
+ * ```
184
+ *
185
+ * @resource
186
+ */
187
+ export const Entity = ((...outer) => outer.length > 0
188
+ ? external(outer[0], outer[1])
189
+ : (typeOrEntity, ...rest) => {
190
+ const [rpcs, props, impl] = typeof typeOrEntity === "string" ? rest : [undefined, ...rest];
191
+ const entity = placedInShardGroup(typeof typeOrEntity === "string"
192
+ ? EffectEntity.make(typeOrEntity, rpcs)
193
+ : typeOrEntity);
194
+ entities.set(entity.type, entity);
195
+ const members = { of: (handlers) => handlers, protocol: entity.protocol };
196
+ // With an implementation, the class deploys it; without, `.make` does.
197
+ if (impl !== undefined) {
198
+ return declare(entity, platform()(entity.type, props, impl), members);
199
+ }
200
+ const tag = platform()(entity.type);
201
+ return declare(entity, tag, { ...members, make: tag.make });
202
+ });
203
+ /** The environment key holding the shard group an entity's pool serves. @internal */
204
+ export const shardGroupKey = (type) => sanitizeKey(`CLUSTER_${type}_SHARD_GROUP`);
205
+ /**
206
+ * Place an entity in its pool's shard group, read when a message is
207
+ * routed: its pool, and every runtime that yields the class, have it in
208
+ * their environment. Effect Cluster's own client (`Counter.entity.client`)
209
+ * then routes to the right pool too.
210
+ */
211
+ const placedInShardGroup = (entity) => {
212
+ const key = shardGroupKey(entity.type);
213
+ return entity.annotate(ClusterSchema.ShardGroup,
214
+ // A synchronous lookup at routing time; workerd has no `process`.
215
+ () => globalThis.process?.env[key] ??
216
+ "default");
217
+ };
218
+ /**
219
+ * An external entity: declare its resource, then have the pool it was
220
+ * given run its program. The pool Layer is what provides `PoolHost`.
221
+ */
222
+ const external = (entity, props) => {
223
+ entities.set(entity.type, entity);
224
+ const declaration = Effect.gen(function* () {
225
+ const resource = yield* platform(entity.type, {
226
+ main: props.main,
227
+ });
228
+ if (globalThis.__ALCHEMY_RUNTIME__)
229
+ return resource;
230
+ const host = yield* PoolHost;
231
+ yield* host.deployExternal({
232
+ type: entity.type,
233
+ main: yield* Effect.sync(() => nodePath.resolve(props.main)),
234
+ handler: entity.type,
235
+ program: props.program,
236
+ resource,
237
+ });
238
+ return resource;
239
+ });
240
+ return Object.assign(clientOf(entity, declaration), { entity });
241
+ };
242
+ /**
243
+ * A class whose `yield*` is its client. While deploying it registers the
244
+ * class and binds what a caller needs into the caller's environment; at
245
+ * runtime it only reads them back, so the Construction phase never runs in
246
+ * a caller.
247
+ */
248
+ const declare = (entity, declaration, members) => Object.assign(class {
249
+ }, members, {
250
+ LogicalId: entity.type,
251
+ entity,
252
+ [PlatformDeclaration]: declaration,
253
+ [EntityDefinition]: entity,
254
+ }, Effectable.Prototype({
255
+ label: `${EntityTypeId}<${entity.type}>`,
256
+ evaluate: () => clientOf(entity, declaration),
257
+ }));
258
+ const clientOf = (entity, declaration) => Effect.gen(function* () {
259
+ // Built on a memo map of its own: a class built inside another's phase
260
+ // would otherwise reuse the caller's already-built pool Layer.
261
+ const resource = globalThis.__ALCHEMY_RUNTIME__
262
+ ? undefined
263
+ : yield* declaration.pipe(Effect.provideServiceEffect(Layer.CurrentMemoMap, Layer.makeMemoMap));
264
+ const ctx = Option.getOrUndefined(yield* Effect.serviceOption(RuntimeContext));
265
+ if (ctx === undefined)
266
+ return outsideRuntime(entity);
267
+ // Bound as environment values while deploying; read back at runtime.
268
+ const bind = (key, value) => ctx
269
+ .set(sanitizeKey(`CLUSTER_${entity.type}_${key}`), Output.isOutput(value) ? value : Output.literal(value))
270
+ .pipe(Effect.flatMap((bound) => ctx.get(bound)));
271
+ const url = (yield* bind("GATEWAY", resource?.gateway)) ?? "";
272
+ // The pool's shard group, for Effect Cluster's own client (`.entity.client`).
273
+ yield* bind("SHARD_GROUP", resource?.pool);
274
+ const token = yield* bind("TOKEN", resource?.token);
275
+ // A Worker calling a gateway Worker reaches it through a service binding.
276
+ const service = sanitizeKey(`CLUSTER_${entity.type}_SERVICE`);
277
+ if (!globalThis.__ALCHEMY_RUNTIME__)
278
+ yield* bindRoute(entity.type, service);
279
+ return makeNamespace(entity, {
280
+ url,
281
+ token: token === undefined ? "" : Redacted.isRedacted(token) ? Redacted.value(token) : token,
282
+ service,
283
+ });
284
+ });
285
+ const outsideRuntime = (entity) => ({
286
+ getByName: (entityId) => Effect.die(new EntityCalledOutsideRuntime({
287
+ message: `Cluster.Entity '${entity.type}/${entityId}' can only be called inside a runtime.`,
288
+ })),
289
+ });
290
+ export const EntityProvider = () => Provider.succeed(platform, {
291
+ stables: ["type"],
292
+ reconcile: ({ id, bindings }) => Effect.sync(() => {
293
+ const attrs = {
294
+ type: id,
295
+ pool: undefined,
296
+ gateway: undefined,
297
+ token: undefined,
298
+ };
299
+ for (const { data } of bindings) {
300
+ attrs.pool = data.pool ?? attrs.pool;
301
+ attrs.gateway = data.gateway ?? attrs.gateway;
302
+ attrs.token = data.token ?? attrs.token;
303
+ }
304
+ return attrs;
305
+ }),
306
+ delete: () => Effect.void,
307
+ });
308
+ //# sourceMappingURL=Entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Entity.js","sources":["../src/Entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import * as Fly from "@alchemy.run/fly";
2
+ import * as Layer from "effect/Layer";
3
+ import { Database as ClusterDatabase } from "../Database.ts";
4
+ import type { Providers } from "../Providers.ts";
5
+ export interface DatabaseProps {
6
+ /**
7
+ * Fly region of the database.
8
+ * @default "iad"
9
+ */
10
+ region?: string;
11
+ /**
12
+ * Postgres image.
13
+ * @default "postgres:17-alpine"
14
+ */
15
+ image?: string;
16
+ /**
17
+ * Size of the database's volume, in GB.
18
+ * @default 10
19
+ */
20
+ diskSize?: number;
21
+ /**
22
+ * Machine size of the database.
23
+ * @default { cpus: 1, memoryMb: 1024 }
24
+ */
25
+ guest?: Fly.MachineGuest;
26
+ /**
27
+ * The Fly private network the database and runners join.
28
+ * @default a network for the stack and stage
29
+ */
30
+ network?: string;
31
+ }
32
+ /**
33
+ * The database an Effect Cluster coordinates through, on Fly.io, as a
34
+ * `Cluster.Database` Layer: Postgres on a Fly Machine with a volume, in an
35
+ * App on a private network that the pool's runners join. Nothing outside the
36
+ * network reaches it.
37
+ *
38
+ * One Postgres on one volume is meant for development and small clusters;
39
+ * for Fly's Managed Postgres, pass its URL to `Cluster.Database.fromUrl`.
40
+ *
41
+ * ### Running the database on Fly
42
+ * **Example:** Postgres in Ashburn
43
+ * ```typescript
44
+ * export const Database = ClusterFly.Database("Cluster");
45
+ * ```
46
+ *
47
+ * @primitive
48
+ */
49
+ export declare const Database: (id: string, props?: DatabaseProps) => Layer.Layer<ClusterDatabase, never, Providers | Fly.Providers | import("alchemy").Stack>;
50
+ //# sourceMappingURL=Database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.d.ts","sources":["../../src/Fly/Database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAIxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAsB,QAAQ,IAAI,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,OAAQ,MAAM,UAAS,aAAa,6FA4CtD,CAAC"}
@@ -0,0 +1,56 @@
1
+ import * as Fly from "@alchemy.run/fly";
2
+ import * as Output from "alchemy/Output";
3
+ import { Random } from "alchemy/Random";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Redacted from "effect/Redacted";
7
+ import { clusterShardGroups, Database as ClusterDatabase } from "../Database.js";
8
+ /** Where Postgres keeps its data, below the volume's mount point. */
9
+ const DATA = "/var/lib/postgresql/data";
10
+ /**
11
+ * The database an Effect Cluster coordinates through, on Fly.io, as a
12
+ * `Cluster.Database` Layer: Postgres on a Fly Machine with a volume, in an
13
+ * App on a private network that the pool's runners join. Nothing outside the
14
+ * network reaches it.
15
+ *
16
+ * One Postgres on one volume is meant for development and small clusters;
17
+ * for Fly's Managed Postgres, pass its URL to `Cluster.Database.fromUrl`.
18
+ *
19
+ * ### Running the database on Fly
20
+ * **Example:** Postgres in Ashburn
21
+ * ```typescript
22
+ * export const Database = ClusterFly.Database("Cluster");
23
+ * ```
24
+ *
25
+ * @primitive
26
+ */
27
+ export const Database = (id, props = {}) => Layer.effect(ClusterDatabase, Effect.gen(function* () {
28
+ if (globalThis.__ALCHEMY_RUNTIME__) {
29
+ // Runners read their database from their own environment at runtime.
30
+ return { url: Redacted.make(""), shardGroups: undefined };
31
+ }
32
+ const network = props.network ?? (yield* Fly.stackNetwork);
33
+ const password = yield* Random(`${id}Password`, { bytes: 24 });
34
+ const app = yield* Fly.App(`${id}App`, { network });
35
+ const machine = yield* Fly.Machine(`${id}Postgres`, {
36
+ app,
37
+ region: props.region ?? "iad",
38
+ image: props.image ?? "postgres:17-alpine",
39
+ guest: props.guest ?? { cpus: 1, memoryMb: 1024 },
40
+ mounts: [{ path: DATA, sizeGb: props.diskSize ?? 10 }],
41
+ env: password.text.pipe(Output.map((text) => ({
42
+ POSTGRES_PASSWORD: Redacted.value(text),
43
+ POSTGRES_DB: "cluster",
44
+ // A fresh volume holds `lost+found`: keep the data below it.
45
+ PGDATA: `${DATA}/pgdata`,
46
+ }))),
47
+ restart: { policy: "always" },
48
+ });
49
+ return {
50
+ // Ordered after the Machine: runners connect once it exists.
51
+ url: Output.all(app.appName, machine.machineId, password.text).pipe(Output.map(([appName, , text]) => Redacted.make(`postgres://postgres:${encodeURIComponent(text.pipe(Redacted.value))}@${appName}.internal:5432/cluster`))),
52
+ network: { kind: "fly", network },
53
+ shardGroups: yield* clusterShardGroups(id),
54
+ };
55
+ }));
56
+ //# sourceMappingURL=Database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.js","sources":["../../src/Fly/Database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import * as Fly from "@alchemy.run/fly";
2
+ import { type PoolSettings } from "../internal/PoolHost.ts";
3
+ export interface MachinePoolOptions extends PoolSettings {
4
+ /**
5
+ * Number of runners.
6
+ * @default 2
7
+ */
8
+ replicas?: number;
9
+ /**
10
+ * Fly region of the pool.
11
+ * @default "iad"
12
+ */
13
+ region?: string;
14
+ /**
15
+ * Machine size of each runner.
16
+ * @default { cpus: 1, memoryMb: 1024 }
17
+ */
18
+ guest?: Fly.MachineGuest;
19
+ }
20
+ /**
21
+ * Run a pool of Effect Cluster runners on Fly.io: a Fly Service whose Machines
22
+ * host every class given this pool, on the database's private
23
+ * network. Runners talk to each other and the database over the network;
24
+ * the Service serves their gateway at `https://{name}.fly.dev` to callers
25
+ * anywhere. Requires a database on a Fly network — `ClusterFly.Database`.
26
+ *
27
+ * ### Running on Fly
28
+ * **Example:** Two runners in Ashburn
29
+ * ```typescript
30
+ * export const Database = ClusterFly.Database("Cluster");
31
+ * export const Default = ClusterFly.MachinePool("Default", { replicas: 2 }).pipe(
32
+ * Layer.provide(Database),
33
+ * );
34
+ * ```
35
+ *
36
+ * @primitive
37
+ */
38
+ export declare const MachinePool: (id: string, options?: MachinePoolOptions) => import("effect/Layer").Layer<import("../internal/PoolHostService.ts").PoolHost, never, import("../Database.ts").Database | import("../Providers.ts").Providers | Fly.Providers | import("alchemy").Stack>;
39
+ //# sourceMappingURL=MachinePool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MachinePool.d.ts","sources":["../../src/Fly/MachinePool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAIxC,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,WAAW,OAAQ,MAAM,YAAW,kBAAkB,8MA6B/D,CAAC"}
@@ -0,0 +1,51 @@
1
+ import * as Fly from "@alchemy.run/fly";
2
+ import * as Output from "alchemy/Output";
3
+ import * as Effect from "effect/Effect";
4
+ import { GATEWAY_PORT } from "../internal/Gateway.js";
5
+ import { makePoolLayer } from "../internal/PoolHost.js";
6
+ /**
7
+ * Run a pool of Effect Cluster runners on Fly.io: a Fly Service whose Machines
8
+ * host every class given this pool, on the database's private
9
+ * network. Runners talk to each other and the database over the network;
10
+ * the Service serves their gateway at `https://{name}.fly.dev` to callers
11
+ * anywhere. Requires a database on a Fly network — `ClusterFly.Database`.
12
+ *
13
+ * ### Running on Fly
14
+ * **Example:** Two runners in Ashburn
15
+ * ```typescript
16
+ * export const Database = ClusterFly.Database("Cluster");
17
+ * export const Default = ClusterFly.MachinePool("Default", { replicas: 2 }).pipe(
18
+ * Layer.provide(Database),
19
+ * );
20
+ * ```
21
+ *
22
+ * @primitive
23
+ */
24
+ export const MachinePool = (id, options = {}) => makePoolLayer({
25
+ label: "ClusterFly.MachinePool",
26
+ id,
27
+ settings: options,
28
+ expected: "a Fly private network (ClusterFly.Database)",
29
+ // Fly App names are at most 30 characters.
30
+ nameLength: 30,
31
+ network: (database) => (database.network?.kind === "fly" ? database.network : undefined),
32
+ // The Service's App is named after the pool.
33
+ gateway: (name) => Effect.succeed({ url: `https://${name}.fly.dev` }),
34
+ nodes: ({ network, name, program, environment }) => Fly.Service(`${id}Pool`, {
35
+ name,
36
+ network: network.network,
37
+ main: program.pipe(Output.map((p) => `${p.dir}/runner.mjs`)),
38
+ region: options.region ?? "iad",
39
+ count: options.replicas ?? 2,
40
+ guest: options.guest ?? { cpus: 1, memoryMb: 1024 },
41
+ port: GATEWAY_PORT,
42
+ public: true,
43
+ env: environment.pipe(
44
+ // Runners listen on the private network, which is IPv6.
45
+ Output.map((env) => ({
46
+ ...env,
47
+ CLUSTER_LISTEN_HOST: "::",
48
+ }))),
49
+ }).pipe(Effect.asVoid),
50
+ });
51
+ //# sourceMappingURL=MachinePool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MachinePool.js","sources":["../../src/Fly/MachinePool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAqB,MAAM,yBAAyB,CAAC;AAoB3E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAAU,EAAE,OAAO,GAAuB,EAAE,EAAE,EAAE,CAC1E,aAAa,CAAC;IACZ,KAAK,EAAE,wBAAwB;IAC/B,EAAE;IACF,QAAQ,EAAE,OAAO;IACjB,QAAQ,EAAE,6CAA6C;IACvD,2CAA2C;IAC3C,UAAU,EAAE,EAAE;IACd,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACxF,6CAA6C;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,IAAI,UAAU,EAAE,CAAC;IACrE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,CACjD,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE;QACvB,IAAI;QACJ,OAAO,EAAE,OAAO,CAAC,OAAiB;QAClC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC,CAAsB;QACjF,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;QAC/B,KAAK,EAAE,OAAO,CAAC,QAAQ,IAAI,CAAC;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QACnD,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,IAAI;QACZ,GAAG,EAAE,WAAW,CAAC,IAAI;QACnB,wDAAwD;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACnB,GAAG,GAAG;YACN,mBAAmB,EAAE,IAAI;SAC1B,CAAC,CAAC,CACiC;KACvC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;CACzB,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @alchemy.run/effect-cluster/Fly — run an Effect Cluster's database and runners
3
+ * on Fly.io. Requires `@alchemy.run/fly`.
4
+ */
5
+ export * from "./Database.ts";
6
+ export * from "./MachinePool.ts";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sources":["../../src/Fly/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @alchemy.run/effect-cluster/Fly — run an Effect Cluster's database and runners
3
+ * on Fly.io. Requires `@alchemy.run/fly`.
4
+ */
5
+ export * from "./Database.js";
6
+ export * from "./MachinePool.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../src/Fly/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { type PoolSettings } from "../internal/PoolHost.ts";
2
+ export interface ComputeEnginePoolOptions extends PoolSettings {
3
+ /**
4
+ * Number of runners, one instance each, spread across zones.
5
+ * @default 2
6
+ */
7
+ replicas?: number;
8
+ /**
9
+ * Machine type of each runner.
10
+ * @default "e2-small"
11
+ */
12
+ machineType?: string;
13
+ /**
14
+ * Zones the pool is spread across, in the database's region.
15
+ * @default the region's `-a`, `-b` and `-c` zones
16
+ */
17
+ zones?: string[];
18
+ }
19
+ /**
20
+ * Run a pool of Effect Cluster runners on Compute Engine: instances in the
21
+ * database's VPC, spread across zones, each running every class given these
22
+ * runners from a Cloud Storage bucket, behind a regional load balancer on a
23
+ * static address that serves their gateway to callers anywhere. Runners
24
+ * reach each other and the database through the cluster's network tag.
25
+ * Requires a database on a GCP network — `ClusterGCP.Database`.
26
+ *
27
+ * Cloud Run can't host runners: runners take connections from each other.
28
+ *
29
+ * ### Running on Compute Engine
30
+ * **Example:** Two runners
31
+ * ```typescript
32
+ * export const Database = ClusterGCP.Database("Cluster");
33
+ * export const Default = ClusterGCP.ComputeEnginePool("Default", { replicas: 2 }).pipe(
34
+ * Layer.provide(Database),
35
+ * );
36
+ * ```
37
+ *
38
+ * @primitive
39
+ */
40
+ export declare const ComputeEnginePool: (id: string, options?: ComputeEnginePoolOptions) => import("effect/Layer").Layer<import("../internal/PoolHostService.ts").PoolHost, never, import("../Database.ts").Database | import("../Providers.ts").Providers | import("@alchemy.run/gcp/Providers").Providers | import("alchemy").Stack>;
41
+ //# sourceMappingURL=ComputeEnginePool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComputeEnginePool.d.ts","sources":["../../src/GCP/ComputeEnginePool.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAQ3E,MAAM,WAAW,wBAAyB,SAAQ,YAAY;IAC5D;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAgDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,OAAQ,MAAM,YAAW,wBAAwB,+OAuG3E,CAAC"}