@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
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The Durable Object classes a `ClusterCloudflare.DurableObjectPool` Worker
3
+ * exports. Every entity type shares one class, `ClusterEntity`, whose objects
4
+ * are named by `(type, id)`; every workflow shares `ClusterWorkflow`, one
5
+ * object per execution; every singleton and cron shares `ClusterScheduled`,
6
+ * one object per name. What each object runs is host-neutral (see
7
+ * `internal/ObjectSharding.ts`); this module gives it workerd's storage.
8
+ *
9
+ * @internal
10
+ */
11
+ import * as Cloudflare from "@alchemy.run/cloudflare/Workers";
12
+ import { makeDurableObjectEngine } from "alchemy/Workers/DurableObjectEngine";
13
+ import * as Context from "effect/Context";
14
+ import * as Effect from "effect/Effect";
15
+ import * as Predicate from "effect/Predicate";
16
+ import { InstanceStorage } from "../../Host.js";
17
+ import { makeEntityObject, makeScheduledObject, NotHosted, registry, workflowClassOf, workflowObjectName, } from "../../internal/ObjectSharding.js";
18
+ /** Cloudflare, as a host of the cluster's objects. */
19
+ export const host = {
20
+ group: "cloudflare",
21
+ // workerd marks a transient failure `retryable`, and an `overloaded`
22
+ // object wants less traffic, not more.
23
+ isRetryable: (error) => Predicate.hasProperty(error, "cause") &&
24
+ Predicate.hasProperty(error.cause, "retryable") &&
25
+ error.cause.retryable === true &&
26
+ !(Predicate.hasProperty(error.cause, "overloaded") && error.cause.overloaded === true),
27
+ };
28
+ /** The most bytes workerd's SQLite stores in one value. */
29
+ const MAX_ENCODED_BYTES = 2 * 1024 * 1024;
30
+ const objectName = (state) => state.id.name ?? "";
31
+ /**
32
+ * The object's storage, called directly: a statement is one synchronous
33
+ * call, so the mailbox costs no event-loop turns.
34
+ */
35
+ const storageOf = (state) => {
36
+ const storage = state.raw.storage;
37
+ return {
38
+ exec: (query, ...bindings) => Effect.sync(() => storage.sql.exec(query, ...bindings).toArray()),
39
+ // Every statement is synchronous, so the transaction is too.
40
+ transaction: (effect) => Effect.sync(() => storage.transactionSync(() => Effect.runSync(effect))),
41
+ getAlarm: Effect.promise(() => storage.getAlarm()),
42
+ setAlarm: (at) => Effect.promise(() => storage.setAlarm(at)),
43
+ };
44
+ };
45
+ /** Every entity instance: one object per `(type, id)`. */
46
+ export const EntityObject = Cloudflare.DurableObject()("ClusterEntity", Effect.gen(function* () {
47
+ const state = yield* Cloudflare.DurableObjectState;
48
+ return Effect.gen(function* () {
49
+ const runtime = yield* Effect.context();
50
+ return yield* makeEntityObject({
51
+ name: objectName(state),
52
+ host,
53
+ storage: storageOf(state),
54
+ // The object's own key-value storage, for platforms built on
55
+ // entities (a Domain model's) to keep each instance's data in.
56
+ runtime: Context.add(runtime, InstanceStorage, state.storage),
57
+ maxEncodedBytes: MAX_ENCODED_BYTES,
58
+ waitUntil: (effect) => state.waitUntil(effect).pipe(Effect.provideContext(runtime)),
59
+ });
60
+ });
61
+ }));
62
+ /** Every singleton and cron: one object per name. */
63
+ export const ScheduledObject = Cloudflare.DurableObject()("ClusterScheduled", Effect.gen(function* () {
64
+ const state = yield* Cloudflare.DurableObjectState;
65
+ return Effect.gen(function* () {
66
+ const runtime = yield* Effect.context();
67
+ return yield* makeScheduledObject({
68
+ name: objectName(state),
69
+ storage: storageOf(state),
70
+ runtime,
71
+ });
72
+ });
73
+ }));
74
+ /**
75
+ * Every workflow execution: one object per `(workflow, execution)`, running
76
+ * Alchemy's Durable Object workflow engine. The engine declares a class per
77
+ * workflow; here each of those is a view onto this one class, so the Worker
78
+ * exports the same classes whatever workflows it hosts.
79
+ */
80
+ export const WorkflowObject = Cloudflare.DurableObject()("ClusterWorkflow", Effect.gen(function* () {
81
+ const state = yield* Cloudflare.DurableObjectState;
82
+ return Effect.gen(function* () {
83
+ const className = workflowClassOf(objectName(state));
84
+ const impl = registry.workflows.get(className);
85
+ if (impl === undefined) {
86
+ return yield* Effect.die(new NotHosted({ message: `This Worker hosts no Cluster.Workflow '${className}'.` }));
87
+ }
88
+ const object = yield* impl.pipe(Effect.provideService(Cloudflare.DurableObjectState, state));
89
+ return (yield* object.pipe(Effect.provideService(Cloudflare.DurableObjectState, state)));
90
+ });
91
+ }));
92
+ /** The workflow engine, its executions on {@link WorkflowObject}. */
93
+ export const workflowEngine = (objects) => makeDurableObjectEngine({
94
+ declare: (className, impl) => Effect.sync(() => {
95
+ registry.workflows.set(className, impl);
96
+ return {
97
+ getByName: (executionId) => objects().getByName(workflowObjectName(className, executionId)),
98
+ };
99
+ }),
100
+ state: Cloudflare.DurableObjectState,
101
+ });
102
+ //# sourceMappingURL=Objects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Objects.js","sources":["../../../src/Cloudflare/internal/Objects.ts"],"names":[],"mappings":"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"}
package/lib/Cron.d.ts ADDED
@@ -0,0 +1,143 @@
1
+ import type { InputProps } from "alchemy/Input";
2
+ import type { Named } from "alchemy/Named";
3
+ import type { PlatformIdentity, PlatformServices } from "alchemy/Platform";
4
+ import type { RuntimeContext } from "alchemy/RuntimeContext";
5
+ import type { Self } from "alchemy/Self";
6
+ import type { ClusterCron } from "effect/cluster";
7
+ import type * as ConfigError from "effect/Config";
8
+ import type * as Context from "effect/Context";
9
+ import * as Cron_ from "effect/Cron";
10
+ import type * as Duration from "effect/Duration";
11
+ import * as Effect from "effect/Effect";
12
+ import type * as Layer from "effect/Layer";
13
+ import type { Scope } from "effect/Scope";
14
+ import { type HostedProps, type HostedResource } from "./internal/HostedPlatform.ts";
15
+ import type { Providers } from "./Providers.ts";
16
+ export declare const CronTypeId = "Cluster.Cron";
17
+ export type CronTypeId = typeof CronTypeId;
18
+ /** When a cron runs: its Layer's props, like Effect's `ClusterCron.make` options. */
19
+ export interface CronProps extends HostedProps {
20
+ /**
21
+ * The schedule, as a cron expression with an optional leading seconds
22
+ * field: `"0 3 * * *"`, `"*\/10 * * * * *"`.
23
+ */
24
+ cron: string;
25
+ /** The time zone the expression is read in. @default UTC */
26
+ timeZone?: string;
27
+ /**
28
+ * Compute each run from the previous run's scheduled time rather than
29
+ * from when it finished.
30
+ * @default false
31
+ */
32
+ calculateNextRunFromPrevious?: boolean;
33
+ /** Skip a run that is due longer ago than this, as after an outage. */
34
+ skipIfOlderThan?: Duration.Input;
35
+ }
36
+ export interface ClusterCronResource extends HostedResource<CronTypeId, CronProps> {
37
+ }
38
+ /**
39
+ * A cron as a runner runs it: its schedule, the job its Construction phase
40
+ * returned, and the services that phase ran with.
41
+ * @internal
42
+ */
43
+ export interface HostedCron {
44
+ readonly kind: "cron";
45
+ readonly name: string;
46
+ readonly cron: Cron_.Cron;
47
+ readonly options: Pick<CronProps, "calculateNextRunFromPrevious" | "skipIfOlderThan">;
48
+ readonly run: Effect.Effect<void, unknown, Scope | ClusterCron.CurrentScheduledTime>;
49
+ readonly context: Context.Context<never>;
50
+ }
51
+ declare const InvalidCron_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
52
+ readonly _tag: "Cluster.InvalidCron";
53
+ } & Readonly<A>;
54
+ /** A cron expression that doesn't parse. */
55
+ export declare class InvalidCron extends InvalidCron_base<{
56
+ readonly message: string;
57
+ }> {
58
+ }
59
+ type CronServices = ClusterCronResource | Providers | RuntimeContext | Self | Self<ClusterCronResource>;
60
+ /** The resource type behind every cron, which the providers register. @internal */
61
+ export declare const CronResource: import("alchemy").Platform<HostedResource<"Cluster.Cron", CronProps>, any, unknown, import("./internal/HostedRuntime.ts").HostedRuntimeContext<HostedCron>, {}, CronProps>;
62
+ export declare const CronProvider: () => Layer.Layer<import("alchemy").Provider<HostedResource<"Cluster.Cron", CronProps>>, never, never>;
63
+ /** A declared cron, implemented by `.make(props, impl)`. */
64
+ export type CronTag<Self, Name extends string> = Named<Name> & PlatformIdentity<Name> & {
65
+ /**
66
+ * The cron's implementation, as a Layer: its schedule, and an Effect
67
+ * that resolves bindings once per runner and returns the job to run.
68
+ * Provide a pool Layer to `impl` to deploy it; provide the returned
69
+ * Layer to the Stack.
70
+ */
71
+ make<InitReq = never>(props: InputProps<CronProps>, impl: Effect.Effect<Effect.Effect<void, unknown, Scope | ClusterCron.CurrentScheduledTime>, ConfigError.ConfigError, InitReq>): Layer.Layer<Self, never, Providers | Exclude<InitReq, CronServices | PlatformServices>>;
72
+ new (_: never): {};
73
+ };
74
+ /** A cron declared with its implementation: the class is what deploys it. */
75
+ export type CronInline<Name extends string, Req> = Effect.Effect<ClusterCronResource, never, Req> & Named<Name> & PlatformIdentity<Name> & {
76
+ new (_: never): {};
77
+ };
78
+ /** The class surface of {@link Cron}. */
79
+ export interface CronClass {
80
+ <Self>(): {
81
+ /**
82
+ * Declared with its implementation: an Effect that resolves bindings
83
+ * once per runner and returns the job. Provide a pool Layer to
84
+ * `impl` to deploy it; `yield*` the class in the Stack.
85
+ */
86
+ <const Name extends string, InitReq = never>(name: Name, props: InputProps<CronProps>, impl: Effect.Effect<Effect.Effect<void, unknown, Scope | ClusterCron.CurrentScheduledTime>, ConfigError.ConfigError, InitReq>): CronInline<Name, Providers | Exclude<InitReq, CronServices | PlatformServices>>;
87
+ /** A bare declaration, implemented by `.make(props, impl)`. */
88
+ <const Name extends string>(name: Name): CronTag<Self, Name>;
89
+ };
90
+ }
91
+ /**
92
+ * A **Cron** on an Effect Cluster: a job that runs on a schedule, once per
93
+ * scheduled time across the cluster, on whichever runner owns it. Each run
94
+ * is a persisted message, so a run due while its runner restarts still
95
+ * happens.
96
+ *
97
+ * Declare it by name, with its schedule, as Effect's `ClusterCron.make`
98
+ * takes it, and its implementation: an Effect that resolves bindings once
99
+ * per runner and returns the job. The job can read the time it was
100
+ * scheduled for with `ClusterCron.CurrentScheduledTime`. Providing a pool
101
+ * Layer to the implementation is what deploys it. Or declare it bare and
102
+ * implement it with `.make(props, impl)`, as a Layer.
103
+ *
104
+ * ### Declaring a cron
105
+ * **Example:** A nightly job
106
+ * ```typescript
107
+ * // src/Nightly.ts
108
+ * export default class Nightly extends Cluster.Cron<Nightly>()(
109
+ * "Nightly",
110
+ * { main: import.meta.url, cron: "0 3 * * *", skipIfOlderThan: "1 hour" },
111
+ * Effect.gen(function* () {
112
+ * const counters = yield* Counter;
113
+ * return Effect.gen(function* () {
114
+ * const counter = yield* counters.getByName("nightly");
115
+ * yield* counter.Increment({ amount: 1 });
116
+ * }).pipe(Effect.orDie);
117
+ * }).pipe(Effect.provide(Default)),
118
+ * ) {}
119
+ * ```
120
+ *
121
+ * **Example:** Declared bare, and implemented as a Layer
122
+ * ```typescript
123
+ * // src/Nightly.ts
124
+ * export class Nightly extends Cluster.Cron<Nightly>()("Nightly") {}
125
+ *
126
+ * // src/NightlyLive.ts
127
+ * export default Nightly.make(
128
+ * { main: import.meta.url, cron: "0 3 * * *", skipIfOlderThan: "1 hour" },
129
+ * Effect.gen(function* () {
130
+ * const counters = yield* Counter;
131
+ * return Effect.gen(function* () {
132
+ * const counter = yield* counters.getByName("nightly");
133
+ * yield* counter.Increment({ amount: 1 });
134
+ * }).pipe(Effect.orDie);
135
+ * }).pipe(Effect.provide(Default)),
136
+ * );
137
+ * ```
138
+ *
139
+ * @resource
140
+ */
141
+ export declare const Cron: CronClass;
142
+ export {};
143
+ //# sourceMappingURL=Cron.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cron.d.ts","sources":["../src/Cron.ts"],"names":[],"mappings":"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"}
package/lib/Cron.js ADDED
@@ -0,0 +1,83 @@
1
+ import * as Cron_ from "effect/Cron";
2
+ import * as Data from "effect/Data";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Result from "effect/Result";
5
+ import { makeHostedPlatform, } from "./internal/HostedPlatform.js";
6
+ export const CronTypeId = "Cluster.Cron";
7
+ /** A cron expression that doesn't parse. */
8
+ export class InvalidCron extends Data.TaggedError("Cluster.InvalidCron") {
9
+ }
10
+ const { platform, provider, declare } = makeHostedPlatform(CronTypeId, (id, run, context, props) => ({
11
+ kind: "cron",
12
+ name: id,
13
+ // Checked while deploying, so it parses.
14
+ cron: Cron_.parse(props.cron, props.timeZone).success,
15
+ options: {
16
+ calculateNextRunFromPrevious: props.calculateNextRunFromPrevious,
17
+ skipIfOlderThan: props.skipIfOlderThan,
18
+ },
19
+ run: run,
20
+ context,
21
+ }),
22
+ // A schedule that doesn't parse fails the deploy, not the runner.
23
+ (id, _context, props) => {
24
+ const cron = Cron_.parse(props.cron, props.timeZone);
25
+ return Result.isSuccess(cron)
26
+ ? Effect.void
27
+ : Effect.die(new InvalidCron({ message: `Cluster.Cron '${id}': ${cron.failure.message}` }));
28
+ });
29
+ /** The resource type behind every cron, which the providers register. @internal */
30
+ export const CronResource = platform;
31
+ export const CronProvider = provider;
32
+ /**
33
+ * A **Cron** on an Effect Cluster: a job that runs on a schedule, once per
34
+ * scheduled time across the cluster, on whichever runner owns it. Each run
35
+ * is a persisted message, so a run due while its runner restarts still
36
+ * happens.
37
+ *
38
+ * Declare it by name, with its schedule, as Effect's `ClusterCron.make`
39
+ * takes it, and its implementation: an Effect that resolves bindings once
40
+ * per runner and returns the job. The job can read the time it was
41
+ * scheduled for with `ClusterCron.CurrentScheduledTime`. Providing a pool
42
+ * Layer to the implementation is what deploys it. Or declare it bare and
43
+ * implement it with `.make(props, impl)`, as a Layer.
44
+ *
45
+ * ### Declaring a cron
46
+ * **Example:** A nightly job
47
+ * ```typescript
48
+ * // src/Nightly.ts
49
+ * export default class Nightly extends Cluster.Cron<Nightly>()(
50
+ * "Nightly",
51
+ * { main: import.meta.url, cron: "0 3 * * *", skipIfOlderThan: "1 hour" },
52
+ * Effect.gen(function* () {
53
+ * const counters = yield* Counter;
54
+ * return Effect.gen(function* () {
55
+ * const counter = yield* counters.getByName("nightly");
56
+ * yield* counter.Increment({ amount: 1 });
57
+ * }).pipe(Effect.orDie);
58
+ * }).pipe(Effect.provide(Default)),
59
+ * ) {}
60
+ * ```
61
+ *
62
+ * **Example:** Declared bare, and implemented as a Layer
63
+ * ```typescript
64
+ * // src/Nightly.ts
65
+ * export class Nightly extends Cluster.Cron<Nightly>()("Nightly") {}
66
+ *
67
+ * // src/NightlyLive.ts
68
+ * export default Nightly.make(
69
+ * { main: import.meta.url, cron: "0 3 * * *", skipIfOlderThan: "1 hour" },
70
+ * Effect.gen(function* () {
71
+ * const counters = yield* Counter;
72
+ * return Effect.gen(function* () {
73
+ * const counter = yield* counters.getByName("nightly");
74
+ * yield* counter.Increment({ amount: 1 });
75
+ * }).pipe(Effect.orDie);
76
+ * }).pipe(Effect.provide(Default)),
77
+ * );
78
+ * ```
79
+ *
80
+ * @resource
81
+ */
82
+ export const Cron = (() => (name, props, impl) => declare(name, {}, props, impl));
83
+ //# sourceMappingURL=Cron.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cron.js","sources":["../src/Cron.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AAEpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAGL,kBAAkB,GACnB,MAAM,8BAA8B,CAAC;AAGtC,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC;AAsCzC,4CAA4C;AAC5C,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAErE;CAAG;AASL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,kBAAkB,CACxD,UAAU,EACV,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,EAAE;IACR,yCAAyC;IACzC,IAAI,EAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAuC,CAAC,OAAO;IAC5F,OAAO,EAAE;QACP,4BAA4B,EAAE,KAAK,CAAC,4BAA4B;QAChE,eAAe,EAAE,KAAK,CAAC,eAAe;KACvC;IACD,GAAG,EAAE,GAAwB;IAC7B,OAAO;CACR,CAAC;AACF,kEAAkE;AAClE,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;IACtB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QAC3B,CAAC,CAAC,MAAM,CAAC,IAAI;QACb,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAChG,CAAC,CACF,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAoDrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,CAAC,MAAM,IAAI,GAAc,CAAC,GAAG,EAAE,CACnC,CAAC,IAAY,EAAE,KAA6B,EAAE,IAAc,EAAE,EAAE,CAC9D,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAyB,CAAC"}
@@ -0,0 +1,95 @@
1
+ import type { Input } from "alchemy/Input";
2
+ import * as Config from "effect/Config";
3
+ import * as Context from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Redacted from "effect/Redacted";
7
+ import type { Providers } from "./Providers.ts";
8
+ import { ShardGroups } from "./ShardGroups.ts";
9
+ /**
10
+ * The private network runners must join to reach the database and each
11
+ * other. A pool Layer matches on `kind` and refuses a network it cannot
12
+ * join.
13
+ */
14
+ export type ClusterNetwork = {
15
+ /** A Docker network; runner containers join it by name. */
16
+ readonly kind: "docker";
17
+ readonly name: Input<string>;
18
+ } | {
19
+ /** An AWS VPC; runners run in its subnets, in the pool's security group. */
20
+ readonly kind: "aws";
21
+ readonly vpcId: Input<string>;
22
+ readonly subnetIds: Input<string>[];
23
+ /** The group every runner joins: it reaches the database and the other runners. */
24
+ readonly securityGroupId: Input<string>;
25
+ } | {
26
+ /** A Fly private network; runners' Apps join it by name. */
27
+ readonly kind: "fly";
28
+ readonly network: Input<string>;
29
+ } | {
30
+ /** A Hetzner private Network; runners' Servers attach to it. */
31
+ readonly kind: "hetzner";
32
+ readonly networkId: Input<number>;
33
+ /** Its IPv4 range, where each runner finds its own address. */
34
+ readonly ipRange: string;
35
+ readonly location: string;
36
+ } | {
37
+ /** A GCP VPC; runners' instances join its subnetwork. */
38
+ readonly kind: "gcp";
39
+ readonly network: Input<string>;
40
+ readonly subnetwork: Input<string>;
41
+ readonly region: string;
42
+ /** The subnetwork's IPv4 range, where each runner finds its own address. */
43
+ readonly cidr: string;
44
+ /** The network tag every runner carries: it reaches the database and the other runners. */
45
+ readonly tag: string;
46
+ } | {
47
+ /** A Kubernetes cluster; runners run as a Deployment in its namespace. */
48
+ readonly kind: "kubernetes";
49
+ /** The cluster, as `@alchemy.run/kubernetes` takes it. */
50
+ readonly cluster: unknown;
51
+ readonly namespace: string;
52
+ };
53
+ /** What every database Layer gives the pool it is provided to. */
54
+ export interface DatabaseShape {
55
+ /**
56
+ * The Postgres connection URL runners use for runner registration,
57
+ * shard locks and persisted messages.
58
+ */
59
+ readonly url: Input<Redacted.Redacted<string>>;
60
+ /** The network runners join to reach it, when it is private. */
61
+ readonly network?: ClusterNetwork;
62
+ /** Every shard group in the cluster, which each pool registers with. */
63
+ readonly shardGroups: ShardGroups;
64
+ }
65
+ declare const Database_base: Context.ServiceClass<Database, "Cluster.Database", DatabaseShape>;
66
+ /**
67
+ * The database an Effect Cluster coordinates through: runners register in
68
+ * it, lock their shards in it, and keep persisted messages in it. Each
69
+ * host's `Database` Layer provides it.
70
+ */
71
+ export declare class Database extends Database_base {
72
+ }
73
+ export interface DatabaseProps {
74
+ /** A Postgres connection URL. */
75
+ url: Redacted.Redacted<string> | Config.Config<Redacted.Redacted<string>>;
76
+ }
77
+ /**
78
+ * A Postgres database you already run, as an Effect Cluster `Database`
79
+ * Layer: Neon, PlanetScale, RDS, or any Postgres the pool can reach.
80
+ *
81
+ * ### Using an existing database
82
+ * **Example:** A Postgres URL from config
83
+ * ```typescript
84
+ * export const Database = Cluster.Database.fromUrl({
85
+ * url: Config.Redacted("CLUSTER_DATABASE_URL"),
86
+ * });
87
+ * ```
88
+ *
89
+ * @primitive
90
+ */
91
+ export declare const fromUrl: (props: DatabaseProps) => Layer.Layer<Database, Config.ConfigError, Providers>;
92
+ /** The shard groups resource every database Layer declares for its cluster. @internal */
93
+ export declare const clusterShardGroups: (id: string) => Effect.Effect<ShardGroups, never, Providers>;
94
+ export {};
95
+ //# sourceMappingURL=Database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.d.ts","sources":["../src/Database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import * as Config from "effect/Config";
2
+ import * as Context from "effect/Context";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Layer from "effect/Layer";
5
+ import * as Redacted from "effect/Redacted";
6
+ import { ShardGroups } from "./ShardGroups.js";
7
+ /**
8
+ * The database an Effect Cluster coordinates through: runners register in
9
+ * it, lock their shards in it, and keep persisted messages in it. Each
10
+ * host's `Database` Layer provides it.
11
+ */
12
+ export class Database extends Context.Service()("Cluster.Database") {
13
+ }
14
+ /**
15
+ * A Postgres database you already run, as an Effect Cluster `Database`
16
+ * Layer: Neon, PlanetScale, RDS, or any Postgres the pool can reach.
17
+ *
18
+ * ### Using an existing database
19
+ * **Example:** A Postgres URL from config
20
+ * ```typescript
21
+ * export const Database = Cluster.Database.fromUrl({
22
+ * url: Config.Redacted("CLUSTER_DATABASE_URL"),
23
+ * });
24
+ * ```
25
+ *
26
+ * @primitive
27
+ */
28
+ export const fromUrl = (props) => Layer.effect(Database, Effect.gen(function* () {
29
+ if (globalThis.__ALCHEMY_RUNTIME__) {
30
+ // Runners read their database from their own environment at runtime.
31
+ return { url: Redacted.make(""), shardGroups: undefined };
32
+ }
33
+ const url = Config.isConfig(props.url) ? yield* props.url : props.url;
34
+ return { url, shardGroups: yield* clusterShardGroups("Database") };
35
+ }));
36
+ /** The shard groups resource every database Layer declares for its cluster. @internal */
37
+ export const clusterShardGroups = (id) => ShardGroups(`${id}ShardGroups`, {});
38
+ //# sourceMappingURL=Database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.js","sources":["../src/Database.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAkE/C;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,OAAO,CAAC,OAAO,EAA2B,CAAC,kBAAkB,CAAC;CAAG;AAO/F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,KAAoB,EACkC,EAAE,CACxD,KAAK,CAAC,MAAM,CACV,QAAQ,EACR,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,IAAI,UAAU,CAAC,mBAAmB,EAAE,CAAC;QACnC,qEAAqE;QACrE,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,SAAkB,EAAE,CAAC;IACrE,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;IACtE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC;AACrE,CAAC,CAAC,CACH,CAAC;AAEJ,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,42 @@
1
+ import * as Docker from "@alchemy.run/docker";
2
+ import * as Layer from "effect/Layer";
3
+ import { Database as ClusterDatabase } from "../Database.ts";
4
+ import type { Providers } from "../Providers.ts";
5
+ /** The Postgres release `ClusterDocker.Database` runs by default. */
6
+ export declare const DEFAULT_POSTGRES_IMAGE = "postgres:17-alpine";
7
+ export interface DatabaseProps {
8
+ /**
9
+ * Postgres image.
10
+ * @default "postgres:17-alpine"
11
+ */
12
+ image?: string;
13
+ /**
14
+ * Publish Postgres on this host port, to inspect it from your machine.
15
+ * @default not published
16
+ */
17
+ port?: number;
18
+ /**
19
+ * The Docker engine to run on: a context name, a `Docker.Context`, or a
20
+ * `Docker.Swarm`.
21
+ * @default the current Docker context
22
+ */
23
+ context?: Docker.Docker.EngineRef;
24
+ }
25
+ /**
26
+ * The database an Effect Cluster coordinates through, on a Docker swarm, as
27
+ * an `Cluster.Database` Layer: Postgres as a one-replica swarm service
28
+ * with its data on a named volume, on an overlay network the pool's runners join.
29
+ * The Docker engine must be in swarm mode (`docker swarm init`).
30
+ *
31
+ * One Postgres on one disk is meant for development and tests.
32
+ *
33
+ * ### Running a local database
34
+ * **Example:** Postgres for local entities
35
+ * ```typescript
36
+ * export const Database = ClusterDocker.Database("Cluster");
37
+ * ```
38
+ *
39
+ * @primitive
40
+ */
41
+ export declare const Database: (id: string, props?: DatabaseProps) => Layer.Layer<ClusterDatabase, never, Providers | Docker.Providers>;
42
+ //# sourceMappingURL=Database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.d.ts","sources":["../../src/Docker/Database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;AAI9C,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,qEAAqE;AACrE,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;CACnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,OACf,MAAM,UACH,aAAa,KACnB,KAAK,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,SAAS,CA2ChE,CAAC"}
@@ -0,0 +1,53 @@
1
+ import * as Docker from "@alchemy.run/docker";
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
+ /** The Postgres release `ClusterDocker.Database` runs by default. */
9
+ export const DEFAULT_POSTGRES_IMAGE = "postgres:17-alpine";
10
+ /**
11
+ * The database an Effect Cluster coordinates through, on a Docker swarm, as
12
+ * an `Cluster.Database` Layer: Postgres as a one-replica swarm service
13
+ * with its data on a named volume, on an overlay network the pool's runners join.
14
+ * The Docker engine must be in swarm mode (`docker swarm init`).
15
+ *
16
+ * One Postgres on one disk is meant for development and tests.
17
+ *
18
+ * ### Running a local database
19
+ * **Example:** Postgres for local entities
20
+ * ```typescript
21
+ * export const Database = ClusterDocker.Database("Cluster");
22
+ * ```
23
+ *
24
+ * @primitive
25
+ */
26
+ export const Database = (id, props = {}) => Layer.effect(ClusterDatabase, Effect.gen(function* () {
27
+ if (globalThis.__ALCHEMY_RUNTIME__) {
28
+ // Runners read their database from their own environment at runtime.
29
+ return { url: Redacted.make(""), shardGroups: undefined };
30
+ }
31
+ const context = props.context === undefined ? {} : { context: props.context };
32
+ const password = yield* Random(`${id}Password`, { bytes: 24 });
33
+ const network = yield* Docker.Network(`${id}Network`, { driver: "overlay", ...context });
34
+ const volumeContext = Docker.dockerEngineContextName(props.context);
35
+ const data = yield* Docker.Volume(`${id}Data`, volumeContext === undefined ? {} : { context: volumeContext });
36
+ const alias = "cluster-db";
37
+ const db = yield* Docker.Service(`${id}Postgres`, {
38
+ image: props.image ?? DEFAULT_POSTGRES_IMAGE,
39
+ ...context,
40
+ environment: { POSTGRES_PASSWORD: password.text, POSTGRES_DB: "cluster" },
41
+ ports: props.port === undefined ? [] : [{ external: props.port, internal: 5432 }],
42
+ volumes: [{ hostPath: data.name, containerPath: "/var/lib/postgresql/data" }],
43
+ networks: [{ name: network.name, aliases: [alias] }],
44
+ replicas: 1,
45
+ });
46
+ return {
47
+ // Ordered after the service: runners connect once it exists.
48
+ url: Output.all(db.name, password.text).pipe(Output.map(([, text]) => Redacted.make(`postgres://postgres:${encodeURIComponent(text.pipe(Redacted.value))}@${alias}:5432/cluster`))),
49
+ network: { kind: "docker", name: network.name },
50
+ shardGroups: yield* clusterShardGroups(id),
51
+ };
52
+ }));
53
+ //# sourceMappingURL=Database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.js","sources":["../../src/Docker/Database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import * as Docker from "@alchemy.run/docker";
2
+ import type { Stack } from "alchemy/Stack";
3
+ import type * as Layer from "effect/Layer";
4
+ import type { Database } from "../Database.ts";
5
+ import { type PoolSettings } from "../internal/PoolHost.ts";
6
+ import type { PoolHost } from "../internal/PoolHostService.ts";
7
+ import type { Providers } from "../Providers.ts";
8
+ export interface SwarmPoolOptions extends PoolSettings {
9
+ /**
10
+ * Number of runners.
11
+ * @default 1
12
+ */
13
+ replicas?: number;
14
+ /**
15
+ * Publish the gateway on this host port, so code outside the swarm (a
16
+ * test, a script) can call the entities at `http://localhost:<port>`.
17
+ * @default not published
18
+ */
19
+ gatewayPort?: number;
20
+ }
21
+ /**
22
+ * Run a pool of Effect Cluster runners on a Docker swarm: a swarm service whose
23
+ * tasks host every `Cluster.Entity` given this pool, on the
24
+ * database's overlay network. Runners find each other through the
25
+ * database and talk on the network; the gateway is served on the same
26
+ * network. Requires an `Cluster.Database` on a Docker network —
27
+ * `ClusterDocker.Database`.
28
+ *
29
+ * Every entity given the same pool shares it: declare the pool once
30
+ * and provide them to each class.
31
+ *
32
+ * ### Running entities on Docker
33
+ * **Example:** Two runners on the local swarm
34
+ * ```typescript
35
+ * export const Database = ClusterDocker.Database("Cluster");
36
+ * export const Default = ClusterDocker.SwarmPool("Default", { replicas: 2 }).pipe(
37
+ * Layer.provide(Database),
38
+ * );
39
+ * ```
40
+ *
41
+ * @primitive
42
+ */
43
+ export declare const SwarmPool: (id: string, options?: SwarmPoolOptions) => Layer.Layer<PoolHost, never, Database | Stack | Providers | Docker.Providers>;
44
+ //# sourceMappingURL=SwarmPool.d.ts.map