alchemy 2.0.0-beta.2 → 2.0.0-beta.test-export-fix-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 (65) hide show
  1. package/bin/{alchemy-effect.js → alchemy.js} +173 -173
  2. package/bin/alchemy.js.map +1 -0
  3. package/bin/{alchemy-effect.sh → alchemy.sh} +2 -2
  4. package/lib/cli/index.js +169 -169
  5. package/lib/cli/index.js.map +1 -1
  6. package/package.json +75 -67
  7. package/src/AWS/AGENTS.md +10 -10
  8. package/src/AWS/Assets.ts +1 -1
  9. package/src/AWS/Credentials.ts +0 -1
  10. package/src/AWS/DynamoDB/Table.ts +63 -10
  11. package/src/AWS/EC2/hosted.ts +1 -1
  12. package/src/AWS/ECS/Task.ts +1 -1
  13. package/src/AWS/Kinesis/Stream.ts +44 -8
  14. package/src/AWS/Lambda/Function.ts +218 -10
  15. package/src/AWS/S3/Bucket.ts +26 -19
  16. package/src/AWS/S3/BucketNotifications.ts +1 -1
  17. package/src/AWS/SNS/Topic.ts +47 -10
  18. package/src/AWS/SQS/Queue.ts +66 -0
  19. package/src/Cloudflare/Container/Container.ts +122 -0
  20. package/src/Cloudflare/Container/ContainerApplication.ts +6 -3
  21. package/src/Cloudflare/Container/ContainerBinding.ts +2 -4
  22. package/src/Cloudflare/Container/StartContainer.ts +1 -1
  23. package/src/Cloudflare/D1/D1Database.ts +23 -4
  24. package/src/Cloudflare/KV/KVNamespace.ts +25 -0
  25. package/src/Cloudflare/Providers.ts +1 -6
  26. package/src/Cloudflare/R2/R2Bucket.ts +45 -2
  27. package/src/Cloudflare/Website/StaticSite.ts +79 -6
  28. package/src/Cloudflare/Website/Vite.ts +67 -13
  29. package/src/Cloudflare/Workers/Assets.ts +170 -207
  30. package/src/Cloudflare/Workers/DurableObjectNamespace.ts +629 -0
  31. package/src/Cloudflare/Workers/DurableObjectState.ts +63 -0
  32. package/src/Cloudflare/Workers/DurableObjectStorage.ts +256 -0
  33. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +66 -7
  34. package/src/Cloudflare/Workers/InferEnv.ts +11 -7
  35. package/src/Cloudflare/Workers/Rpc.ts +13 -2
  36. package/src/Cloudflare/Workers/ScheduledEvents.ts +185 -0
  37. package/src/Cloudflare/Workers/WebSocket.ts +1 -1
  38. package/src/Cloudflare/Workers/Worker.ts +406 -49
  39. package/src/Cloudflare/Workers/Workflow.ts +53 -11
  40. package/src/Cloudflare/Workers/index.ts +4 -1
  41. package/src/Construct.ts +2 -2
  42. package/src/GitHub/Comment.ts +224 -0
  43. package/src/GitHub/Secret.ts +257 -0
  44. package/src/GitHub/Variable.ts +166 -0
  45. package/src/GitHub/index.ts +3 -0
  46. package/src/Kubernetes/client.ts +2 -2
  47. package/src/Output.ts +1 -1
  48. package/src/Platform.ts +1 -1
  49. package/src/Provider.ts +1 -1
  50. package/src/Test/Vitest.ts +4 -3
  51. package/src/Util/PlatformServices.ts +21 -0
  52. package/src/Util/dedent.ts +59 -0
  53. package/src/Util/index.ts +1 -0
  54. package/bin/alchemy-effect.js.map +0 -1
  55. package/src/Cloudflare/Workers/DurableObject.ts +0 -527
  56. package/src/Daemon/Client.ts +0 -116
  57. package/src/Daemon/Config.ts +0 -28
  58. package/src/Daemon/Errors.ts +0 -48
  59. package/src/Daemon/Lock.ts +0 -162
  60. package/src/Daemon/ProcessRegistry.ts +0 -284
  61. package/src/Daemon/RpcSchema.ts +0 -43
  62. package/src/Daemon/RpcServer.ts +0 -231
  63. package/src/Daemon/index.ts +0 -27
  64. package/src/Spawn.ts +0 -23
  65. /package/bin/{alchemy-effect.ts → alchemy.ts} +0 -0
@@ -116,25 +116,233 @@ export type FunctionShape = Main<FunctionServices>;
116
116
  * An AWS Lambda host resource that combines code bundling, IAM role
117
117
  * provisioning, and runtime binding collection.
118
118
  *
119
- * `Function` is the canonical runtime host for AWS. Resource bindings attach
120
- * environment variables and IAM statements during deployment, while the runtime
121
- * execution context collects listeners and exports from the Effect program.
119
+ * `Function` is the canonical runtime host for AWS. Alchemy automatically
120
+ * bundles your TypeScript entry module with Rolldown, creates an IAM
121
+ * execution role, and uploads the zip artifact. On subsequent deploys, the
122
+ * function is only updated when the bundle hash changes.
122
123
  *
123
- * @section Creating Functions
124
- * @example Basic Function
124
+ * There are two ways to define a Lambda Function:
125
+ *
126
+ * - **Async** — plain handler export, no Effect runtime in the bundle.
127
+ * - **Effect** — Effect implementation with typed bindings and event sources.
128
+ *
129
+ * See the {@link https://alchemy.run/guides/async-lambda | Async Lambda Guide}
130
+ * for plain handler patterns, or the
131
+ * {@link https://alchemy.run/guides/lambda | Effect Lambda Guide}
132
+ * for the full Effect-based approach with bindings, event sources, and sinks.
133
+ *
134
+ * @section Async Functions
135
+ * Point `main` at a file that exports a standard Lambda handler. No
136
+ * Effect runtime is included in the bundle. Useful when migrating
137
+ * existing Lambda functions or when you don't need Effect.
138
+ *
139
+ * @example Defining an async Lambda in your stack
125
140
  * ```typescript
126
- * const func = yield* Function("OrdersFunction", {
127
- * main: "./src/orders.ts",
141
+ * // alchemy.run.ts
142
+ * import * as AWS from "alchemy/AWS";
143
+ *
144
+ * const func = yield* AWS.Lambda.Function("ApiFunction", {
145
+ * main: "./src/handler.ts",
146
+ * url: true,
128
147
  * });
129
148
  * ```
130
149
  *
150
+ * @example Writing the async handler
151
+ * ```typescript
152
+ * // src/handler.ts
153
+ * export const handler = async (event: any) => {
154
+ * return {
155
+ * statusCode: 200,
156
+ * body: JSON.stringify({ message: "Hello from Lambda!" }),
157
+ * };
158
+ * };
159
+ * ```
160
+ *
161
+ * @section Effect Functions
162
+ * Pass the Effect implementation as the third argument. Bindings
163
+ * attach IAM permissions and environment variables at deploy time,
164
+ * while the runtime execution context collects listeners and exports.
165
+ *
166
+ * @example Effect Function with HTTP handler
167
+ * ```typescript
168
+ * export default class ApiFunction extends AWS.Lambda.Function<ApiFunction>()(
169
+ * "ApiFunction",
170
+ * { main: import.meta.filename, url: true },
171
+ * Effect.gen(function* () {
172
+ * // init: bind resources
173
+ * const getItem = yield* DynamoDB.GetItem.bind(table);
174
+ *
175
+ * return {
176
+ * // runtime: use them
177
+ * fetch: Effect.gen(function* () {
178
+ * const request = yield* HttpServerRequest;
179
+ * const url = new URL(request.url);
180
+ * const id = url.searchParams.get("id");
181
+ * const result = yield* getItem({ Key: { pk: { S: id! } } });
182
+ * return yield* HttpServerResponse.json(result.Item);
183
+ * }),
184
+ * };
185
+ * }),
186
+ * ) {}
187
+ * ```
188
+ *
189
+ * @section Configuration
131
190
  * @example Function with URL
132
191
  * ```typescript
133
- * const func = yield* Function("ApiFunction", {
134
- * main: "./src/api.ts",
192
+ * const func = yield* AWS.Lambda.Function("ApiFunction", {
193
+ * main: "./src/handler.ts",
135
194
  * url: true,
136
195
  * });
137
196
  * ```
197
+ *
198
+ * @example Function in a VPC
199
+ * ```typescript
200
+ * const func = yield* AWS.Lambda.Function("VpcFunction", {
201
+ * main: "./src/handler.ts",
202
+ * vpc: {
203
+ * subnetIds: ["subnet-abc123", "subnet-def456"],
204
+ * securityGroupIds: ["sg-xyz789"],
205
+ * },
206
+ * });
207
+ * ```
208
+ *
209
+ * @section S3 Bindings
210
+ * Bind S3 operations in the init phase to give the function IAM
211
+ * permissions and inject the bucket name as an environment variable.
212
+ *
213
+ * @example Read and write S3 objects
214
+ * ```typescript
215
+ * // init
216
+ * const getObject = yield* S3.GetObject.bind(bucket);
217
+ * const putObject = yield* S3.PutObject.bind(bucket);
218
+ *
219
+ * return {
220
+ * fetch: Effect.gen(function* () {
221
+ * // runtime
222
+ * yield* putObject({ Key: "hello.txt", Body: "Hello!" });
223
+ * const obj = yield* getObject({ Key: "hello.txt" });
224
+ * return HttpServerResponse.text("OK");
225
+ * }),
226
+ * };
227
+ * ```
228
+ *
229
+ * @section DynamoDB Bindings
230
+ * Bind DynamoDB operations in the init phase to grant table-scoped
231
+ * IAM permissions.
232
+ *
233
+ * @example Get and put items
234
+ * ```typescript
235
+ * // init
236
+ * const getItem = yield* DynamoDB.GetItem.bind(table);
237
+ * const putItem = yield* DynamoDB.PutItem.bind(table);
238
+ *
239
+ * return {
240
+ * fetch: Effect.gen(function* () {
241
+ * // runtime
242
+ * yield* putItem({ Item: { pk: { S: "user#1" }, name: { S: "Alice" } } });
243
+ * const result = yield* getItem({ Key: { pk: { S: "user#1" } } });
244
+ * return yield* HttpServerResponse.json(result.Item);
245
+ * }),
246
+ * };
247
+ * ```
248
+ *
249
+ * @section SQS Bindings
250
+ * Bind SQS operations in the init phase to send messages to a queue.
251
+ *
252
+ * @example Send a message
253
+ * ```typescript
254
+ * // init
255
+ * const sendMessage = yield* SQS.SendMessage.bind(queue);
256
+ *
257
+ * return {
258
+ * fetch: Effect.gen(function* () {
259
+ * // runtime
260
+ * yield* sendMessage({
261
+ * MessageBody: JSON.stringify({ orderId: "123" }),
262
+ * });
263
+ * return HttpServerResponse.text("Queued");
264
+ * }),
265
+ * };
266
+ * ```
267
+ *
268
+ * @section SNS Bindings
269
+ * Bind SNS operations in the init phase to publish messages to a
270
+ * topic.
271
+ *
272
+ * @example Publish a notification
273
+ * ```typescript
274
+ * // init
275
+ * const publish = yield* SNS.Publish.bind(topic);
276
+ *
277
+ * return {
278
+ * fetch: Effect.gen(function* () {
279
+ * // runtime
280
+ * yield* publish({
281
+ * Message: JSON.stringify({ event: "order.created" }),
282
+ * Subject: "OrderCreated",
283
+ * });
284
+ * return HttpServerResponse.text("Published");
285
+ * }),
286
+ * };
287
+ * ```
288
+ *
289
+ * @section Kinesis Bindings
290
+ * Bind Kinesis operations in the init phase to put records into a
291
+ * stream.
292
+ *
293
+ * @example Put a record
294
+ * ```typescript
295
+ * // init
296
+ * const putRecord = yield* Kinesis.PutRecord.bind(stream);
297
+ *
298
+ * return {
299
+ * fetch: Effect.gen(function* () {
300
+ * // runtime
301
+ * yield* putRecord({
302
+ * PartitionKey: "order-123",
303
+ * Data: new TextEncoder().encode(JSON.stringify({ orderId: "123" })),
304
+ * });
305
+ * return HttpServerResponse.text("Sent");
306
+ * }),
307
+ * };
308
+ * ```
309
+ *
310
+ * @section Event Sources
311
+ * Lambda functions can be triggered by event sources like SQS queues,
312
+ * DynamoDB streams, S3 notifications, SNS topics, and Kinesis streams.
313
+ *
314
+ * @example Process SQS messages
315
+ * ```typescript
316
+ * yield* SQS.messages(queue).process(
317
+ * Effect.fn(function* (message) {
318
+ * yield* Effect.log(`Received: ${message.body}`);
319
+ * }),
320
+ * );
321
+ * ```
322
+ *
323
+ * @example Process DynamoDB stream changes
324
+ * ```typescript
325
+ * yield* DynamoDB.streams(table, {
326
+ * StreamViewType: "NEW_AND_OLD_IMAGES",
327
+ * }).process(
328
+ * Effect.fn(function* (record) {
329
+ * yield* Effect.log(`Change: ${record.eventName}`);
330
+ * }),
331
+ * );
332
+ * ```
333
+ *
334
+ * @example Process S3 notifications
335
+ * ```typescript
336
+ * yield* S3.notifications(bucket, {
337
+ * events: ["s3:ObjectCreated:*"],
338
+ * }).subscribe((stream) =>
339
+ * stream.pipe(
340
+ * Stream.runForEach((event) =>
341
+ * Effect.log(`New object: ${event.key}`),
342
+ * ),
343
+ * ),
344
+ * );
345
+ * ```
138
346
  */
139
347
  export const Function: Platform<
140
348
  Function,
@@ -433,7 +641,7 @@ export const FunctionProvider = () =>
433
641
  virtualEntryPlugin(
434
642
  (importPath) => `
435
643
  import { NodeServices } from "@effect/platform-node";
436
- import { Stack } from "alchemy-effect/Stack";
644
+ import { Stack } from "alchemy/Stack";
437
645
  import * as Config from "effect/Config";
438
646
  import * as ConfigProvider from "effect/ConfigProvider";
439
647
  import * as Credentials from "@distilled.cloud/aws/Credentials";
@@ -91,7 +91,7 @@ export interface Bucket extends Resource<
91
91
  * @section Creating a Bucket
92
92
  * @example Basic Bucket
93
93
  * ```typescript
94
- * import * as S3 from "alchemy-effect/AWS/S3";
94
+ * import * as S3 from "alchemy/AWS/S3";
95
95
  *
96
96
  * const bucket = yield* S3.Bucket("my-bucket", {});
97
97
  * ```
@@ -110,37 +110,44 @@ export interface Bucket extends Resource<
110
110
  * });
111
111
  * ```
112
112
  *
113
- * @section Reading Objects
114
- * @example Get an object from a bucket
115
- * ```typescript
116
- * const getObject = yield* S3.GetObject.bind(bucket);
117
- *
118
- * const response = yield* getObject({ Key: "hello.txt" });
119
- * ```
113
+ * @section Runtime Operations
114
+ * Bind S3 operations in the init phase and use them in runtime
115
+ * handlers. Bindings inject the bucket name and grant scoped IAM
116
+ * permissions automatically.
120
117
  *
121
- * @section Writing Objects
122
- * @example Put an object into a bucket
118
+ * @example Read and write objects
123
119
  * ```typescript
120
+ * // init
121
+ * const getObject = yield* S3.GetObject.bind(bucket);
124
122
  * const putObject = yield* S3.PutObject.bind(bucket);
125
123
  *
126
- * yield* putObject({
127
- * Key: "hello.txt",
128
- * Body: "Hello, World!",
129
- * ContentType: "text/plain",
130
- * });
124
+ * return {
125
+ * fetch: Effect.gen(function* () {
126
+ * // runtime
127
+ * yield* putObject({
128
+ * Key: "hello.txt",
129
+ * Body: "Hello, World!",
130
+ * ContentType: "text/plain",
131
+ * });
132
+ * const response = yield* getObject({ Key: "hello.txt" });
133
+ * return HttpServerResponse.text("OK");
134
+ * }),
135
+ * };
131
136
  * ```
132
137
  *
133
- * @section Deleting Objects
134
- * @example Delete an object from a bucket
138
+ * @example Delete an object
135
139
  * ```typescript
140
+ * // init
136
141
  * const deleteObject = yield* S3.DeleteObject.bind(bucket);
137
- *
138
- * yield* deleteObject({ Key: "hello.txt" });
139
142
  * ```
140
143
  *
141
144
  * @section Event Notifications
145
+ * Subscribe to bucket events from the init phase. The subscription
146
+ * and Lambda invoke permissions are created automatically.
147
+ *
142
148
  * @example Process object creation events
143
149
  * ```typescript
150
+ * // init
144
151
  * yield* S3.notifications(bucket, {
145
152
  * events: ["s3:ObjectCreated:*"],
146
153
  * }).subscribe((stream) =>
@@ -35,7 +35,7 @@ export interface NotificationsProps<Events extends S3EventType[]> {
35
35
  * @section Subscribing to Events
36
36
  * @example Process all object creation events
37
37
  * ```typescript
38
- * import * as S3 from "alchemy-effect/AWS/S3";
38
+ * import * as S3 from "alchemy/AWS/S3";
39
39
  *
40
40
  * yield* S3.notifications(bucket, {
41
41
  * events: ["s3:ObjectCreated:*"],
@@ -60,20 +60,30 @@ export interface Topic extends Resource<
60
60
  * An Amazon SNS topic for fan-out messaging and notifications.
61
61
  *
62
62
  * `Topic` owns the SNS topic lifecycle while raw AWS topic attributes remain
63
- * available through the `attributes` prop so the full core pub/sub surface can be
64
- * configured without waiting on additional typed wrappers.
63
+ * available through the `attributes` prop so the full core pub/sub surface can
64
+ * be configured without waiting on additional typed wrappers. A topic name is
65
+ * auto-generated unless you provide one explicitly.
65
66
  *
66
67
  * @section Creating Topics
67
68
  * @example Standard Topic
68
69
  * ```typescript
69
- * const topic = yield* Topic("OrdersTopic", {
70
- * topicName: "orders-events",
70
+ * import * as SNS from "alchemy/AWS/SNS";
71
+ *
72
+ * const topic = yield* SNS.Topic("OrdersTopic");
73
+ * ```
74
+ *
75
+ * @example Topic with Display Name
76
+ * ```typescript
77
+ * const topic = yield* SNS.Topic("NotificationsTopic", {
78
+ * attributes: {
79
+ * DisplayName: "App Notifications",
80
+ * },
71
81
  * });
72
82
  * ```
73
83
  *
74
84
  * @example FIFO Topic
75
85
  * ```typescript
76
- * const topic = yield* Topic("OrdersFifoTopic", {
86
+ * const topic = yield* SNS.Topic("OrdersFifoTopic", {
77
87
  * fifo: true,
78
88
  * attributes: {
79
89
  * ContentBasedDeduplication: "true",
@@ -82,14 +92,41 @@ export interface Topic extends Resource<
82
92
  * ```
83
93
  *
84
94
  * @section Runtime Publishing
85
- * @example Publish from a Lambda Function
95
+ * Bind publish operations in the init phase and use them in runtime
96
+ * handlers.
97
+ *
98
+ * @example Publish from a handler
86
99
  * ```typescript
100
+ * // init
87
101
  * const publish = yield* SNS.Publish.bind(topic);
88
102
  *
89
- * yield* publish({
90
- * Message: JSON.stringify({ orderId: "123" }),
91
- * Subject: "OrderCreated",
92
- * });
103
+ * return {
104
+ * fetch: Effect.gen(function* () {
105
+ * // runtime
106
+ * yield* publish({
107
+ * Message: JSON.stringify({ orderId: "123" }),
108
+ * Subject: "OrderCreated",
109
+ * });
110
+ * return HttpServerResponse.text("Published");
111
+ * }),
112
+ * };
113
+ * ```
114
+ *
115
+ * @section Subscriptions
116
+ * Subscribe a Lambda function to process messages published to the
117
+ * topic. The subscription and invoke permissions are created
118
+ * automatically.
119
+ *
120
+ * @example Process topic notifications
121
+ * ```typescript
122
+ * // init
123
+ * yield* SNS.notifications(topic).subscribe((stream) =>
124
+ * stream.pipe(
125
+ * Stream.runForEach((message) =>
126
+ * Effect.log(`Received: ${message.Message}`),
127
+ * ),
128
+ * ),
129
+ * );
93
130
  * ```
94
131
  */
95
132
  export const Topic = Resource<Topic>("AWS.SNS.Topic");
@@ -87,6 +87,72 @@ export interface Queue extends Resource<
87
87
  Providers
88
88
  > {}
89
89
 
90
+ /**
91
+ * An Amazon SQS queue for reliable, decoupled message processing.
92
+ *
93
+ * `Queue` owns the lifecycle of a standard or FIFO SQS queue. A queue name
94
+ * is auto-generated from the app, stage, and logical ID unless you provide
95
+ * one explicitly. FIFO queues automatically append the `.fifo` suffix.
96
+ *
97
+ * @section Creating Queues
98
+ * @example Standard Queue
99
+ * ```typescript
100
+ * import * as SQS from "alchemy/AWS/SQS";
101
+ *
102
+ * const queue = yield* SQS.Queue("OrdersQueue");
103
+ * ```
104
+ *
105
+ * @example FIFO Queue
106
+ * ```typescript
107
+ * const queue = yield* SQS.Queue("OrdersFifoQueue", {
108
+ * fifo: true,
109
+ * contentBasedDeduplication: true,
110
+ * });
111
+ * ```
112
+ *
113
+ * @example Queue with Custom Settings
114
+ * ```typescript
115
+ * const queue = yield* SQS.Queue("ProcessingQueue", {
116
+ * visibilityTimeout: 120,
117
+ * messageRetentionPeriod: 86400,
118
+ * receiveMessageWaitTimeSeconds: 20,
119
+ * });
120
+ * ```
121
+ *
122
+ * @section Sending Messages
123
+ * Bind send operations in the init phase and use them in runtime
124
+ * handlers.
125
+ *
126
+ * @example Send a message from a handler
127
+ * ```typescript
128
+ * // init
129
+ * const sendMessage = yield* SQS.SendMessage.bind(queue);
130
+ *
131
+ * return {
132
+ * fetch: Effect.gen(function* () {
133
+ * // runtime
134
+ * yield* sendMessage({
135
+ * MessageBody: JSON.stringify({ orderId: "123" }),
136
+ * });
137
+ * return HttpServerResponse.text("Queued");
138
+ * }),
139
+ * };
140
+ * ```
141
+ *
142
+ * @section Event Sources
143
+ * Process messages from a queue using a Lambda event source mapping.
144
+ * Messages are automatically deleted after successful processing.
145
+ *
146
+ * @example Process queue messages
147
+ * ```typescript
148
+ * // init
149
+ * yield* SQS.messages(queue).process(
150
+ * Effect.fn(function* (message) {
151
+ * yield* Effect.log(`Received: ${message.body}`);
152
+ * }),
153
+ * );
154
+ * ```
155
+ */
90
156
  export const Queue = Resource<Queue>("AWS.SQS.Queue");
91
157
 
92
158
  export const QueueProvider = () =>
@@ -48,6 +48,128 @@ export type Container = {
48
48
  interceptAllOutboundHttp(binding: Fetcher): Effect.Effect<void>;
49
49
  };
50
50
 
51
+ /**
52
+ * A Cloudflare Container that runs a long-lived process alongside a
53
+ * Durable Object.
54
+ *
55
+ * Containers always use the **Container Layer** pattern — the class
56
+ * and `.make()` must live in separate files. A Container must be
57
+ * bound to a Durable Object, and the DO imports the class to get a
58
+ * typed handle. If the class and `.make()` lived in the same file,
59
+ * the DO's bundle would pull in all of the container's runtime
60
+ * dependencies (process spawners, Node APIs, SDKs, etc.), which
61
+ * would bloat the bundle and likely break the Cloudflare Workers
62
+ * runtime. Keeping them separate ensures the bundler only includes
63
+ * the tiny class in the DO's output.
64
+ *
65
+ * See the {@link https://alchemy.run/concepts/platform | Platform
66
+ * concept} page for how this fits into the async / effect / layer
67
+ * progression.
68
+ *
69
+ * @section Container Layer
70
+ * Define the class and `.make()` in separate files. The class
71
+ * declares the container's identity, configuration, and typed
72
+ * shape. `.make()` provides the runtime implementation as a
73
+ * default export. Use `Container.of` to construct the typed
74
+ * shape — it ensures your implementation matches the methods
75
+ * declared on the class.
76
+ *
77
+ * @example Container class
78
+ * ```typescript
79
+ * // src/Sandbox.ts
80
+ * export class Sandbox extends Cloudflare.Container<
81
+ * Sandbox,
82
+ * {
83
+ * exec: (cmd: string) => Effect.Effect<{
84
+ * exitCode: number;
85
+ * stdout: string;
86
+ * stderr: string;
87
+ * }>;
88
+ * }
89
+ * >()(
90
+ * "Sandbox",
91
+ * { main: import.meta.filename },
92
+ * ) {}
93
+ * ```
94
+ *
95
+ * @example Container .make()
96
+ * ```typescript
97
+ * // src/Sandbox.runtime.ts
98
+ * export default Sandbox.make(
99
+ * Effect.gen(function* () {
100
+ * const cp = yield* ChildProcessSpawner;
101
+ *
102
+ * return Sandbox.of({
103
+ * exec: (cmd) =>
104
+ * cp.spawn(ChildProcess.make(cmd, { shell: true })).pipe(
105
+ * Effect.map(({ exitCode, stdout, stderr }) => ({
106
+ * exitCode, stdout, stderr,
107
+ * })),
108
+ * Effect.scoped,
109
+ * ),
110
+ * fetch: Effect.succeed(
111
+ * HttpServerResponse.text("Hello from container!"),
112
+ * ),
113
+ * });
114
+ * }),
115
+ * );
116
+ * ```
117
+ *
118
+ * @section Configuration
119
+ * The props object accepts `main` (entrypoint file), `instanceType`
120
+ * (compute size), `runtime` (`"bun"` or `"node"`), and
121
+ * `observability` settings. Use `Stack.useSync` to vary config by
122
+ * stage.
123
+ *
124
+ * @example Stage-dependent configuration
125
+ * ```typescript
126
+ * export class Sandbox extends Cloudflare.Container<Sandbox>()(
127
+ * "Sandbox",
128
+ * Stack.useSync((stack) => ({
129
+ * main: import.meta.filename,
130
+ * instanceType: stack.stage === "prod" ? "standard-1" : "dev",
131
+ * observability: { logs: { enabled: true } },
132
+ * })),
133
+ * ) {}
134
+ * ```
135
+ *
136
+ * @section Starting from a Durable Object
137
+ * Use `Cloudflare.Container.bind` in the outer init phase to bind
138
+ * the container class, then `Cloudflare.start` in the inner
139
+ * per-instance phase to start it. Because the DO only imports the
140
+ * class, the runtime implementation is completely excluded from the
141
+ * DO's bundle.
142
+ *
143
+ * @example Binding and starting a container
144
+ * ```typescript
145
+ * // init (outer Effect) — only imports the class
146
+ * const sandbox = yield* Cloudflare.Container.bind(Sandbox);
147
+ *
148
+ * // per-instance (inner Effect)
149
+ * return Effect.gen(function* () {
150
+ * const container = yield* Cloudflare.start(sandbox);
151
+ *
152
+ * return {
153
+ * exec: (cmd: string) => container.exec(cmd),
154
+ * };
155
+ * });
156
+ * ```
157
+ *
158
+ * @section HTTP Requests to Container Ports
159
+ * Use `getTcpPort` to get a `fetch` handle for a specific port on
160
+ * the running container. This lets you make HTTP requests to
161
+ * servers running inside the container process.
162
+ *
163
+ * @example Fetching from a container port
164
+ * ```typescript
165
+ * const container = yield* Cloudflare.start(sandbox);
166
+ * const { fetch } = yield* container.getTcpPort(3000);
167
+ *
168
+ * const response = yield* fetch(
169
+ * HttpClientRequest.get("http://container/health"),
170
+ * );
171
+ * ```
172
+ */
51
173
  export const Container: Platform<
52
174
  ContainerApplication,
53
175
  ContainerServices,
@@ -223,6 +223,9 @@ export type ContainerServices =
223
223
 
224
224
  export type ContainerShape = Main<ContainerServices>;
225
225
 
226
+ /**
227
+ * @internal
228
+ */
226
229
  export interface ContainerApplication<Shape = unknown> extends Resource<
227
230
  ContainerTypeId,
228
231
  ContainerApplicationProps,
@@ -480,16 +483,16 @@ ${
480
483
  runtime === "bun"
481
484
  ? `
482
485
  import { BunServices } from "@effect/platform-bun";
483
- import { BunHttpServer } from "alchemy-effect/Http";
486
+ import { BunHttpServer } from "alchemy/Http";
484
487
  const HttpServer = BunHttpServer;
485
488
  `
486
489
  : `
487
490
  import { NodeServices } from "@effect/platform-node";
488
- import { NodeHttpServer } from "alchemy-effect/Http";
491
+ import { NodeHttpServer } from "alchemy/Http";
489
492
  const HttpServer = NodeHttpServer;
490
493
  `
491
494
  }
492
- import { Stack } from "alchemy-effect/Stack";
495
+ import { Stack } from "alchemy/Stack";
493
496
  import * as Effect from "effect/Effect";
494
497
  import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
495
498
  import * as Layer from "effect/Layer";
@@ -5,10 +5,8 @@ import {
5
5
  toCloudflareFetcher,
6
6
  type Fetcher,
7
7
  } from "../Fetcher.ts";
8
- import {
9
- DurableObjectNamespace,
10
- DurableObjectState,
11
- } from "../Workers/DurableObject.ts";
8
+ import { DurableObjectNamespace } from "../Workers/DurableObjectNamespace.ts";
9
+ import { DurableObjectState } from "../Workers/DurableObjectState.ts";
12
10
  import { Worker } from "../Workers/Worker.ts";
13
11
  import type { Container } from "./Container.ts";
14
12
  import type { ContainerApplication } from "./ContainerApplication.ts";
@@ -6,7 +6,7 @@ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
6
6
  import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest";
7
7
  import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse";
8
8
  import { type Fetcher } from "../Fetcher.ts";
9
- import { DurableObjectState } from "../Workers/DurableObject.ts";
9
+ import { DurableObjectState } from "../Workers/DurableObjectState.ts";
10
10
  import { type Container, ContainerError } from "./Container.ts";
11
11
 
12
12
  /**