Skip to content

Commit ff04d57

Browse files
authored
feat(ec2): NAT Gateway, EIP, Egress IGW, SecurityGroups (#24)
1 parent 00a1e98 commit ff04d57

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+5674
-542
lines changed

.oxfmtrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"useTabs": false,
66
"printWidth": 80,
77
"endOfLine": "lf",
8-
"experimentalTernaries": true,
8+
"ternaries": true,
99
"experimental_sort_imports": {
1010
"order": "asc"
1111
}

.oxlintrc.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"$schema": "./node_modules/oxfmt/configuration_schema.json",
33
"rules": {
44
"no-misused-new": "off",
5-
"require-yield": "off"
5+
"require-yield": "off",
6+
"no-non-null-asserted-optional-chain": "off"
67
}
7-
}
8+
}

.vscode/settings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@
5555
"[jsx]": {
5656
"editor.indentSize": 2,
5757
"editor.defaultFormatter": "oxc.oxc-vscode"
58-
},
59-
}
58+
}
59+
}

AGENTS.md

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,52 @@ Conduct each engagement with the user as follows:
44

55
1. Read the [README](README.md) to get a high-level overview of the project.
66
2. Read the following code files to familiarize yourself with the common tasks:
7-
- Declaring a Resource (see [queue.ts](alchemy-effect/src/aws/sqs/queue.ts))
8-
- Implementing a Resource Provider (see [queue.provider.ts](alchemy-effect/src/aws/sqs/queue.provider.ts) or [table.provider.ts](alchemy-effect/src/aws/dynamodb/table.provider.ts))
9-
- Declaring a Capability (see [queue.consume.ts](alchemy-effect/src/aws/sqs/queue.consume.ts))
10-
- Implementing a Capability's Binding:
11-
- Push-based Binding (see [queue.send-message.ts](alchemy-effect/src/aws/sqs/queue.send-message.ts))
12-
- Pull-based Binding using postattach (see [queue.event-source.ts](alchemy-effect/src/aws/sqs/queue.event-source.ts))
13-
- Fine-grained IAM policy modelling in the type-system (see [table.get-item.ts](alchemy-effect/src/aws/dynamodb/table.get-item.ts))
14-
- Declaring a Function (aka Runtime) see [function.ts](alchemy-effect/src/aws/lambda/function.ts)
15-
- Creating a Client for an AWS scope (see [aws/lambda/client.ts](alchemy-effect/src/aws/lambda/client.ts) and [aws/sqs/client.ts](alchemy-effect/src/aws/sqs/client.ts))
16-
- Compiling the AWS.live layer (see [aws/index.ts](alchemy-effect/src/aws/index.ts))
17-
- Using resources (see [example/src/api.ts](example/src/api.ts) and [example/src/consumer.ts](example/src/consumer.ts))
18-
3. When implementing a Resource Provider, make sure to read the [itty-aws](./alchemy-effect/node_modules/itty-aws/dist/services/*/types.d.ts) type defnitions for that service and come up with a plan for which errors to retry, which to consider fatal, and design the overall create, update, delete flow for each of the resource lifecycle handlers. We are using effect, so we gain the benefit of type-safe errors, delcarative retry behavior (declarative flow control).
7+
8+
- Declaring a Resource (see [queue.ts](alchemy-effect/src/aws/sqs/queue.ts))
9+
- Implementing a Resource Provider (see [queue.provider.ts](alchemy-effect/src/aws/sqs/queue.provider.ts) and [table.provider.ts](alchemy-effect/src/aws/dynamodb/table.provider.ts) and [vpc.provider.ts](alchemy-effect/src/aws/ec2/vpc.provider.ts) and [subnet.provider.ts](alchemy-effect/src/aws/ec2/subnet.provider.ts))
10+
- Implementing tests for Resource Providers (see [queue.provider.test.ts](alchemy-effect/test/aws/sqs/queue.provider.test.ts) and [table.provider.test.ts](alchemy-effect/test/aws/dynamodb/table.provider.test.ts) and [vpc.provider.test.ts](alchemy-effect/test/aws/ec2/vpc.provider.test.ts))
11+
- Implementing a comprehensive smoke test, see [vpc.test.ts](alchemy-effect/test/aws/ec2/vpc.test.ts)
12+
- Declaring a Capability (see [queue.consume.ts](alchemy-effect/src/aws/sqs/queue.consume.ts))
13+
- Implementing a Capability's Binding:
14+
- Push-based Binding (see [queue.send-message.ts](alchemy-effect/src/aws/sqs/queue.send-message.ts))
15+
- Pull-based Binding using postattach (see [queue.event-source.ts](alchemy-effect/src/aws/sqs/queue.event-source.ts))
16+
- Fine-grained IAM policy modelling in the type-system (see [table.get-item.ts](alchemy-effect/src/aws/dynamodb/table.get-item.ts))
17+
- Declaring a Function (aka Runtime) see [function.ts](alchemy-effect/src/aws/lambda/function.ts)
18+
- Creating a Client for an AWS scope (see [aws/lambda/client.ts](alchemy-effect/src/aws/lambda/client.ts) and [aws/sqs/client.ts](alchemy-effect/src/aws/sqs/client.ts))
19+
- Compiling the AWS.live layer (see [aws/index.ts](alchemy-effect/src/aws/index.ts))
20+
- Using resources (see [example/src/api.ts](example/src/api.ts) and [example/src/consumer.ts](example/src/consumer.ts))
21+
22+
Provider Implementation Tips:
23+
24+
- The `diff` function should return `undefined` (not `{ action: "noop" }`) when properties don't require replacement - this allows the `update` function to be called for in-place attribute changes.
25+
- Only include service-specific attributes conditionally (e.g., SQS FIFO attributes like `FifoQueue`, `ContentBasedDeduplication` should only be sent for FIFO queues, not standard queues).
1926

2027
Restrictions:
28+
2129
1. Never use `Effect.catchAll`, always use `Effect.catchTag` or `Effect.catchTags`
2230
1. Always use `bun` (never npm, pnpm, yarn, etc.)
2331

2432
:::caution
2533
Never (ever!) delete .alchemy/
2634

27-
Tests are designed ot be idempotent.
28-
When making changes to providers, you should keep running the tests and fixing providers until the tests pass.
29-
If you think the state is corrupted stop and let me know.
30-
You can always add a `yield* destroy()` at the beginning of each test to clean up state. Do not delete .alchemy files or folders.
35+
Tests are designed ot be idempotent.
36+
When making changes to providers, you should keep running the tests and fixing providers until the tests pass.
37+
If you think the state is corrupted, stop and let me know.
38+
You should always add a `yield* destroy()` at the beginning of each test to clean up state. Do not delete `.alchemy` files or folders.
3139

3240
Never manually delete resources with the aws cli or api calls. Tests must be designed to be idempotent and self-healing.
3341
:::
3442

3543
# Testing
3644

3745
To test, use the following command:
46+
3847
```
3948
bun vitest run ./alchemy-effect/test/<path>/<to>/<test>.test.ts
4049
```
50+
51+
Run with DEBUG=1 to see debug logs, e.g.
52+
53+
```ts
54+
Effect.tapError(Effect.logDebug),
55+
```

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,3 @@
9292
##### &nbsp;&nbsp;&nbsp;&nbsp;[View changes on GitHub](https://github.com/alchemy-run/alchemy/compare/c46b447ca0d46a9e4dbf08a6789770a420d90be5...v0.1.0)
9393

9494
---
95-

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
# `alchemy-effect`
44

55
`alchemy-effect` is an **Infrastructure-as-Effects (IaE)** framework that unifies business logic and infrastructure config into a single, type-safe program with the following benefits:
6+
67
1. Type-Checked IAM Policies
78
2. Optimally Tree-Shaken Bundles
89
3. Testable Business Logic
910
4. Re-usable Components
10-
5. Reviewable Deployment Plans
11+
5. Reviewable Deployment Plans
1112

1213
## Install
14+
1315
```bash
1416
bun add alchemy-effect
1517
```
@@ -27,11 +29,13 @@ You will receive a type error if you mess up your Bindings:
2729
> This error means you are missing the `SendMessage<Messages>` binding (you provided `never` instead of `SendMessage<Messages>`).
2830
2931
## Plan & Deploy
32+
3033
An `alchemy-effect` program produces a Plan that can be reviewed prior to deployment:
3134

3235
<img src="./images/alchemy-effect-plan.gif" alt="alchemy-effect plan video" width="600"/>
3336

34-
## Type-Level Plan
37+
## Type-Level Plan
38+
3539
All knowable information about the Plan is available at compile-time:
3640

3741
<img src="./images/alchemy-effect-plan-type.png" alt="alchemy-effect plan type" width="600"/>
@@ -50,17 +54,19 @@ export default Api.handler.pipe(
5054
);
5155
```
5256

53-
## Pluggable Layers
57+
## Pluggable Layers
58+
5459
Everything (including the CLI) is provided as Effect layers:
5560

5661
<img src="./images/alchemy-effect-layers.png" alt="alchemy-effect layers" width="600"/>
5762

5863
## Literally Typed Outputs
64+
5965
The output of deploying a stack is totally known at compile-time, e.g. the `.fifo` suffix of a SQS FIFO Queue:
6066

6167
<img src="./images/alchemy-effect-output.png" alt="alchemy-effect output" width="600"/>
6268

63-
# Concepts 🔱
69+
# Concepts 🔱
6470

6571
<img src="./images/alchemy-effect-triple.png" alt="alchemy-effect logo" width="600"/>
6672

@@ -78,7 +84,7 @@ Resources are declared along-side your business logic as classes, e.g. a FIFO SQ
7884
class Messages extends SQS.Queue("Messages", {
7985
fifo: true,
8086
schema: S.String,
81-
}) {}
87+
}) {}
8288
```
8389

8490
## Functions
@@ -122,7 +128,7 @@ class Api extends Lambda.serve("Api", {
122128
123129
# Components
124130

125-
Infrastructure and business logic can be encapsulated as a Component using a simple function.
131+
Infrastructure and business logic can be encapsulated as a Component using a simple function.
126132

127133
```ts
128134
const Monitor = <const ID extends string, ReqAlarm, ReqResolved>(
@@ -176,4 +182,4 @@ const Monitor = <const ID extends string, ReqAlarm, ReqResolved>(
176182
# Building your own Resources, Capabilities, and Bindings
177183
178184
> [!CAUTION]
179-
> WIP - docs coming soon!
185+
> WIP - docs coming soon!

alchemy-effect/package.json

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,117 @@
11
{
22
"name": "alchemy-effect",
33
"version": "0.5.0",
4+
"homepage": "https://alchemy.run",
45
"license": "Apache-2.0",
56
"author": "Sam Goodwin <sam@alchemy.run>",
6-
"homepage": "https://alchemy.run",
77
"repository": {
88
"type": "git",
99
"url": "git+https://github.com/alchemy-run/alchemy-effect.git",
1010
"directory": "alchemy"
1111
},
12-
"type": "module",
13-
"sideEffects": false,
12+
"bin": {
13+
"alchemy-effect": "bin/alchemy-effect.js"
14+
},
1415
"files": [
1516
"bin",
1617
"lib",
1718
"src"
1819
],
19-
"bin": {
20-
"alchemy-effect": "bin/alchemy-effect.js"
21-
},
22-
"scripts": {
23-
"dev": "tsdown --watch",
24-
"build": "bun bundle && bun pm pack",
25-
"bundle": "tsdown",
26-
"bundle:watch": "tsdown --watch",
27-
"publish:npm": "cp ../README.md . && bun publish && rm README.md",
28-
"test:benchmark": "tsx --tsconfig tsconfig.test.json test/types.benchmark.ts"
29-
},
20+
"type": "module",
21+
"sideEffects": false,
3022
"exports": {
3123
".": {
24+
"types": "./src/index.d.ts",
3225
"bun": "./src/index.ts",
33-
"import": "./lib/index.js",
34-
"types": "./src/index.d.ts"
26+
"import": "./lib/index.js"
3527
},
3628
"./Output": {
29+
"types": "./lib/output.d.ts",
3730
"bun": "./src/output.ts",
38-
"import": "./lib/output.js",
39-
"types": "./lib/output.d.ts"
31+
"import": "./lib/output.js"
4032
},
4133
"./aws": {
34+
"types": "./lib/aws/index.d.ts",
4235
"bun": "./src/aws/index.ts",
43-
"import": "./lib/aws/index.js",
44-
"types": "./lib/aws/index.d.ts"
36+
"import": "./lib/aws/index.js"
4537
},
4638
"./aws/dynamodb": {
39+
"types": "./lib/aws/dynamodb/index.d.ts",
4740
"bun": "./src/aws/dynamodb/index.ts",
48-
"import": "./lib/aws/dynamodb/index.js",
49-
"types": "./lib/aws/dynamodb/index.d.ts"
41+
"import": "./lib/aws/dynamodb/index.js"
5042
},
5143
"./aws/ec2": {
44+
"types": "./lib/aws/ec2/index.d.ts",
5245
"bun": "./src/aws/ec2/index.ts",
53-
"import": "./lib/aws/ec2/index.js",
54-
"types": "./lib/aws/ec2/index.d.ts"
46+
"import": "./lib/aws/ec2/index.js"
5547
},
5648
"./aws/lambda": {
49+
"types": "./lib/aws/lambda/index.d.ts",
5750
"bun": "./src/aws/lambda/index.ts",
58-
"import": "./lib/aws/lambda/index.js",
59-
"types": "./lib/aws/lambda/index.d.ts"
51+
"import": "./lib/aws/lambda/index.js"
6052
},
6153
"./aws/sqs": {
54+
"types": "./lib/sqs/aws/index.d.ts",
6255
"bun": "./src/aws/sqs/index.ts",
63-
"import": "./lib/aws/sqs/index.js",
64-
"types": "./lib/sqs/aws/index.d.ts"
56+
"import": "./lib/aws/sqs/index.js"
6557
},
6658
"./aws/sts": {
59+
"types": "./lib/sts.d.ts",
6760
"bun": "./src/aws/sts.ts",
68-
"import": "./lib/aws/sts.js",
69-
"types": "./lib/sts.d.ts"
61+
"import": "./lib/aws/sts.js"
7062
},
7163
"./aws/iam": {
64+
"types": "./lib/iam.d.ts",
7265
"bun": "./src/aws/iam.ts",
73-
"import": "./lib/aws/iam.js",
74-
"types": "./lib/iam.d.ts"
66+
"import": "./lib/aws/iam.js"
7567
},
7668
"./aws/credentials": {
69+
"types": "./lib/aws/credentials.d.ts",
7770
"bun": "./src/aws/credentials.ts",
78-
"import": "./lib/aws/credentials.js",
79-
"types": "./lib/aws/credentials.d.ts"
71+
"import": "./lib/aws/credentials.js"
8072
},
8173
"./cli": {
74+
"types": "./lib/cli/index.d.ts",
8275
"bun": "./src/cli/index.ts",
83-
"import": "./lib/cli/index.js",
84-
"types": "./lib/cli/index.d.ts"
76+
"import": "./lib/cli/index.js"
8577
},
8678
"./cloudflare": {
79+
"types": "./lib/cloudflare/index.d.ts",
8780
"bun": "./src/cloudflare/index.ts",
88-
"import": "./lib/cloudflare/index.js",
89-
"types": "./lib/cloudflare/index.d.ts"
81+
"import": "./lib/cloudflare/index.js"
9082
},
9183
"./cloudflare/assets": {
84+
"types": "./lib/cloudflare/worker/assets.fetch.d.ts",
9285
"bun": "./src/cloudflare/worker/assets.fetch.ts",
93-
"import": "./lib/cloudflare/worker/assets.fetch.js",
94-
"types": "./lib/cloudflare/worker/assets.fetch.d.ts"
86+
"import": "./lib/cloudflare/worker/assets.fetch.js"
9587
},
9688
"./cloudflare/worker": {
89+
"types": "./lib/cloudflare/worker/index.d.ts",
9790
"bun": "./src/cloudflare/worker/index.ts",
98-
"import": "./lib/cloudflare/worker/index.js",
99-
"types": "./lib/cloudflare/worker/index.d.ts"
91+
"import": "./lib/cloudflare/worker/index.js"
10092
},
10193
"./cloudflare/kv": {
94+
"types": "./lib/cloudflare/kv/index.d.ts",
10295
"bun": "./src/cloudflare/kv/index.ts",
103-
"import": "./lib/cloudflare/kv/index.js",
104-
"types": "./lib/cloudflare/kv/index.d.ts"
96+
"import": "./lib/cloudflare/kv/index.js"
10597
},
10698
"./cloudflare/r2": {
99+
"types": "./lib/cloudflare/r2/index.d.ts",
107100
"bun": "./src/cloudflare/r2/index.ts",
108-
"import": "./lib/cloudflare/r2/index.js",
109-
"types": "./lib/cloudflare/r2/index.d.ts"
101+
"import": "./lib/cloudflare/r2/index.js"
110102
}
111103
},
104+
"publishConfig": {
105+
"access": "public"
106+
},
107+
"scripts": {
108+
"dev": "tsdown --watch",
109+
"build": "bun bundle && bun pm pack",
110+
"bundle": "tsdown",
111+
"bundle:watch": "tsdown --watch",
112+
"publish:npm": "cp ../README.md . && bun publish && rm README.md",
113+
"test:benchmark": "tsx --tsconfig tsconfig.test.json test/types.benchmark.ts"
114+
},
112115
"dependencies": {
113116
"@aws-sdk/credential-providers": "catalog:",
114117
"@effect/vitest": "^0.27.0",
@@ -160,8 +163,5 @@
160163
"effect": {
161164
"optional": true
162165
}
163-
},
164-
"publishConfig": {
165-
"access": "public"
166166
}
167167
}

0 commit comments

Comments
 (0)