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.
- package/bin/{alchemy-effect.js → alchemy.js} +173 -173
- package/bin/alchemy.js.map +1 -0
- package/bin/{alchemy-effect.sh → alchemy.sh} +2 -2
- package/lib/cli/index.js +169 -169
- package/lib/cli/index.js.map +1 -1
- package/package.json +75 -67
- package/src/AWS/AGENTS.md +10 -10
- package/src/AWS/Assets.ts +1 -1
- package/src/AWS/Credentials.ts +0 -1
- package/src/AWS/DynamoDB/Table.ts +63 -10
- package/src/AWS/EC2/hosted.ts +1 -1
- package/src/AWS/ECS/Task.ts +1 -1
- package/src/AWS/Kinesis/Stream.ts +44 -8
- package/src/AWS/Lambda/Function.ts +218 -10
- package/src/AWS/S3/Bucket.ts +26 -19
- package/src/AWS/S3/BucketNotifications.ts +1 -1
- package/src/AWS/SNS/Topic.ts +47 -10
- package/src/AWS/SQS/Queue.ts +66 -0
- package/src/Cloudflare/Container/Container.ts +122 -0
- package/src/Cloudflare/Container/ContainerApplication.ts +6 -3
- package/src/Cloudflare/Container/ContainerBinding.ts +2 -4
- package/src/Cloudflare/Container/StartContainer.ts +1 -1
- package/src/Cloudflare/D1/D1Database.ts +23 -4
- package/src/Cloudflare/KV/KVNamespace.ts +25 -0
- package/src/Cloudflare/Providers.ts +1 -6
- package/src/Cloudflare/R2/R2Bucket.ts +45 -2
- package/src/Cloudflare/Website/StaticSite.ts +79 -6
- package/src/Cloudflare/Website/Vite.ts +67 -13
- package/src/Cloudflare/Workers/Assets.ts +170 -207
- package/src/Cloudflare/Workers/DurableObjectNamespace.ts +629 -0
- package/src/Cloudflare/Workers/DurableObjectState.ts +63 -0
- package/src/Cloudflare/Workers/DurableObjectStorage.ts +256 -0
- package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +66 -7
- package/src/Cloudflare/Workers/InferEnv.ts +11 -7
- package/src/Cloudflare/Workers/Rpc.ts +13 -2
- package/src/Cloudflare/Workers/ScheduledEvents.ts +185 -0
- package/src/Cloudflare/Workers/WebSocket.ts +1 -1
- package/src/Cloudflare/Workers/Worker.ts +406 -49
- package/src/Cloudflare/Workers/Workflow.ts +53 -11
- package/src/Cloudflare/Workers/index.ts +4 -1
- package/src/Construct.ts +2 -2
- package/src/GitHub/Comment.ts +224 -0
- package/src/GitHub/Secret.ts +257 -0
- package/src/GitHub/Variable.ts +166 -0
- package/src/GitHub/index.ts +3 -0
- package/src/Kubernetes/client.ts +2 -2
- package/src/Output.ts +1 -1
- package/src/Platform.ts +1 -1
- package/src/Provider.ts +1 -1
- package/src/Test/Vitest.ts +4 -3
- package/src/Util/PlatformServices.ts +21 -0
- package/src/Util/dedent.ts +59 -0
- package/src/Util/index.ts +1 -0
- package/bin/alchemy-effect.js.map +0 -1
- package/src/Cloudflare/Workers/DurableObject.ts +0 -527
- package/src/Daemon/Client.ts +0 -116
- package/src/Daemon/Config.ts +0 -28
- package/src/Daemon/Errors.ts +0 -48
- package/src/Daemon/Lock.ts +0 -162
- package/src/Daemon/ProcessRegistry.ts +0 -284
- package/src/Daemon/RpcSchema.ts +0 -43
- package/src/Daemon/RpcServer.ts +0 -231
- package/src/Daemon/index.ts +0 -27
- package/src/Spawn.ts +0 -23
- /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.
|
|
120
|
-
*
|
|
121
|
-
* execution
|
|
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
|
-
*
|
|
124
|
-
*
|
|
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
|
-
*
|
|
127
|
-
*
|
|
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/
|
|
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
|
|
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";
|
package/src/AWS/S3/Bucket.ts
CHANGED
|
@@ -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
|
|
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
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
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
|
-
* @
|
|
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
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
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
|
-
* @
|
|
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
|
|
38
|
+
* import * as S3 from "alchemy/AWS/S3";
|
|
39
39
|
*
|
|
40
40
|
* yield* S3.notifications(bucket, {
|
|
41
41
|
* events: ["s3:ObjectCreated:*"],
|
package/src/AWS/SNS/Topic.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
70
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
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");
|
package/src/AWS/SQS/Queue.ts
CHANGED
|
@@ -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
|
|
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
|
|
491
|
+
import { NodeHttpServer } from "alchemy/Http";
|
|
489
492
|
const HttpServer = NodeHttpServer;
|
|
490
493
|
`
|
|
491
494
|
}
|
|
492
|
-
import { Stack } from "alchemy
|
|
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
|
-
|
|
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/
|
|
9
|
+
import { DurableObjectState } from "../Workers/DurableObjectState.ts";
|
|
10
10
|
import { type Container, ContainerError } from "./Container.ts";
|
|
11
11
|
|
|
12
12
|
/**
|