effortless-aws 0.2.1 → 0.4.0
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/README.md +152 -37
- package/dist/{chunk-7JVA4742.js → chunk-AHRNISIY.js} +4 -0
- package/dist/cli/index.js +197 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +118 -29
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/wrap-http.js +19 -4
- package/dist/runtime/wrap-site.js +132 -0
- package/dist/runtime/wrap-table-stream.js +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# effortless-aws
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AWS serverless is the best way to run production software. Lambda gives you 99.95% availability out of the box, scales to zero, handles thousands of concurrent requests, and you never manage a server. DynamoDB, SQS, S3, EventBridge — these are battle-tested building blocks with guarantees most self-hosted infrastructure can't match. The event-driven model maps naturally to real business logic: an order is placed, a file is uploaded, a record changes.
|
|
4
|
+
|
|
5
|
+
The problem is never AWS itself — it's the tooling. CloudFormation templates, IAM policies, Terraform state files, CDK constructs. You end up spending more time on infrastructure plumbing than on your actual product. And even when infrastructure is sorted out, wiring serverless resources together — connecting a Lambda to a DynamoDB stream, granting cross-service permissions, passing table names between functions — is tedious and error-prone.
|
|
6
|
+
|
|
7
|
+
**Effortless** is a TypeScript framework for developers who build on AWS serverless. It handles three things:
|
|
8
|
+
|
|
9
|
+
1. **Infrastructure from code.** You write handlers, export them, and deploy. The framework derives Lambda functions, API Gateway routes, DynamoDB tables, streams, and IAM roles directly from your TypeScript exports. No config files.
|
|
10
|
+
2. **Bundling and packaging.** Your code is automatically bundled with [esbuild](https://esbuild.github.io/) — tree-shaken, split per function, with shared dependencies extracted into a common [Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html). No build config to maintain.
|
|
11
|
+
3. **Typed cross-resource communication.** Reference one handler from another with `deps: { orders }` and get a fully typed client injected at runtime, with IAM permissions wired automatically. Serverless resources talk to each other through code, not through copied ARNs and manual policies.
|
|
4
12
|
|
|
5
13
|
```bash
|
|
6
14
|
npm install effortless-aws
|
|
@@ -9,32 +17,13 @@ npm install effortless-aws
|
|
|
9
17
|
## What it looks like
|
|
10
18
|
|
|
11
19
|
```typescript
|
|
12
|
-
|
|
13
|
-
import { defineHttp, defineTable, param } from "effortless-aws";
|
|
14
|
-
|
|
15
|
-
// DynamoDB table — just export it, get the table
|
|
16
|
-
export const orders = defineTable<Order>({
|
|
17
|
-
pk: { name: "id", type: "string" },
|
|
18
|
-
onRecord: async ({ record, table }) => {
|
|
19
|
-
if (record.eventName === "INSERT") {
|
|
20
|
-
await table.put({ ...record.new!, status: "confirmed" });
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
});
|
|
20
|
+
import { defineHttp } from "effortless-aws";
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
deps: { orders },
|
|
31
|
-
params: { apiKey: param("stripe-key") },
|
|
32
|
-
context: async ({ params }) => ({
|
|
33
|
-
stripe: new Stripe(params.apiKey),
|
|
34
|
-
}),
|
|
35
|
-
onRequest: async ({ data, ctx, deps }) => {
|
|
36
|
-
await deps.orders.put({ id: crypto.randomUUID(), ...data });
|
|
37
|
-
return { status: 201, body: { ok: true } };
|
|
22
|
+
export const hello = defineHttp({
|
|
23
|
+
method: "GET",
|
|
24
|
+
path: "/hello",
|
|
25
|
+
onRequest: async () => {
|
|
26
|
+
return { status: 200, body: { message: "Hello!" } };
|
|
38
27
|
},
|
|
39
28
|
});
|
|
40
29
|
```
|
|
@@ -43,7 +32,7 @@ export const createOrder = defineHttp({
|
|
|
43
32
|
npx eff deploy
|
|
44
33
|
```
|
|
45
34
|
|
|
46
|
-
That's it. No YAML, no CloudFormation, no state files.
|
|
35
|
+
That's it — one export, one command. No YAML, no CloudFormation, no state files.
|
|
47
36
|
|
|
48
37
|
## Why
|
|
49
38
|
|
|
@@ -69,34 +58,105 @@ Traditional Lambda development splits infrastructure and code across multiple fi
|
|
|
69
58
|
|
|
70
59
|
**Cold start caching** — `context` factory runs once per cold start, cached across invocations. Put DB connections, SDK clients, config there.
|
|
71
60
|
|
|
72
|
-
##
|
|
61
|
+
## Examples
|
|
73
62
|
|
|
74
|
-
###
|
|
63
|
+
### Path params
|
|
75
64
|
|
|
76
65
|
```typescript
|
|
77
66
|
export const getUser = defineHttp({
|
|
78
67
|
method: "GET",
|
|
79
68
|
path: "/users/{id}",
|
|
80
69
|
onRequest: async ({ req }) => {
|
|
81
|
-
|
|
70
|
+
const user = await findUser(req.params.id);
|
|
71
|
+
return { status: 200, body: user };
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
> Creates: Lambda function, API Gateway `GET /users/{id}` route, IAM execution role.
|
|
77
|
+
|
|
78
|
+
### Schema validation
|
|
79
|
+
|
|
80
|
+
Works with any validation library — Zod, Effect Schema, or a plain function.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
export const createUser = defineHttp({
|
|
84
|
+
method: "POST",
|
|
85
|
+
path: "/users",
|
|
86
|
+
schema: (input) => parseUser(input),
|
|
87
|
+
onRequest: async ({ data }) => {
|
|
88
|
+
// data is typed from schema return type
|
|
89
|
+
return { status: 201, body: { id: data.id } };
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Context (cold-start cache)
|
|
95
|
+
|
|
96
|
+
`context` runs once per cold start. Put SDK clients, DB connections, config here.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
export const listOrders = defineHttp({
|
|
100
|
+
method: "GET",
|
|
101
|
+
path: "/orders",
|
|
102
|
+
context: () => ({
|
|
103
|
+
db: new DatabaseClient(),
|
|
104
|
+
}),
|
|
105
|
+
onRequest: async ({ ctx }) => {
|
|
106
|
+
const orders = await ctx.db.findAll();
|
|
107
|
+
return { status: 200, body: orders };
|
|
82
108
|
},
|
|
83
109
|
});
|
|
84
110
|
```
|
|
85
111
|
|
|
86
|
-
###
|
|
112
|
+
### SSM params
|
|
113
|
+
|
|
114
|
+
`param("key")` reads from Parameter Store at cold start. Auto IAM, auto caching.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
export const charge = defineHttp({
|
|
118
|
+
method: "POST",
|
|
119
|
+
path: "/charge",
|
|
120
|
+
params: { apiKey: param("stripe-key") },
|
|
121
|
+
context: async ({ params }) => ({
|
|
122
|
+
stripe: new Stripe(params.apiKey),
|
|
123
|
+
}),
|
|
124
|
+
onRequest: async ({ ctx, data }) => {
|
|
125
|
+
await ctx.stripe.charges.create(data);
|
|
126
|
+
return { status: 200, body: { ok: true } };
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
> Creates: Lambda, API Gateway route, IAM role with `ssm:GetParameter` on `/{project}/{stage}/stripe-key`.
|
|
132
|
+
|
|
133
|
+
### Cross-handler deps
|
|
134
|
+
|
|
135
|
+
`deps: { orders }` auto-wires IAM and injects a typed `TableClient`.
|
|
87
136
|
|
|
88
137
|
```typescript
|
|
89
|
-
|
|
138
|
+
type Order = { id: string; name: string };
|
|
139
|
+
|
|
140
|
+
export const orders = defineTable<Order>({
|
|
90
141
|
pk: { name: "id", type: "string" },
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
export const createOrder = defineHttp({
|
|
145
|
+
method: "POST",
|
|
146
|
+
path: "/orders",
|
|
147
|
+
deps: { orders },
|
|
148
|
+
onRequest: async ({ deps }) => {
|
|
149
|
+
await deps.orders.put({ id: crypto.randomUUID(), name: "New order" });
|
|
150
|
+
return { status: 201, body: { ok: true } };
|
|
95
151
|
},
|
|
96
152
|
});
|
|
97
153
|
```
|
|
98
154
|
|
|
99
|
-
|
|
155
|
+
> Creates: DynamoDB table, Lambda, API Gateway route. The Lambda's IAM role gets DynamoDB read/write permissions on the `orders` table automatically. Table name is injected via environment variable.
|
|
156
|
+
|
|
157
|
+
### DynamoDB table (resource only)
|
|
158
|
+
|
|
159
|
+
Export a table — get the DynamoDB resource. No Lambda, no stream.
|
|
100
160
|
|
|
101
161
|
```typescript
|
|
102
162
|
export const sessions = defineTable({
|
|
@@ -105,6 +165,61 @@ export const sessions = defineTable({
|
|
|
105
165
|
});
|
|
106
166
|
```
|
|
107
167
|
|
|
168
|
+
> Creates: DynamoDB table with TTL enabled. No Lambda, no stream — just the table.
|
|
169
|
+
|
|
170
|
+
### DynamoDB table with stream
|
|
171
|
+
|
|
172
|
+
Add `onRecord` to process changes. Each record is handled individually with automatic partial batch failure reporting.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
type User = { id: string; email: string; name: string };
|
|
176
|
+
|
|
177
|
+
export const users = defineTable<User>({
|
|
178
|
+
pk: { name: "id", type: "string" },
|
|
179
|
+
sk: { name: "email", type: "string" },
|
|
180
|
+
onRecord: async ({ record }) => {
|
|
181
|
+
if (record.eventName === "INSERT") {
|
|
182
|
+
await sendWelcomeEmail(record.new!.email);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
> Creates: DynamoDB table with stream enabled, Lambda for stream processing, event source mapping between them, IAM role with DynamoDB read/write permissions. Failed records are reported individually via [partial batch responses](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting).
|
|
189
|
+
|
|
190
|
+
### Full example
|
|
191
|
+
|
|
192
|
+
Everything together — table, HTTP handler with validation, deps, params, and context.
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
type Order = { id: string; total: number; chargeId?: string };
|
|
196
|
+
|
|
197
|
+
export const orders = defineTable<Order>({
|
|
198
|
+
pk: { name: "id", type: "string" },
|
|
199
|
+
onRecord: async ({ record }) => {
|
|
200
|
+
if (record.eventName === "INSERT") {
|
|
201
|
+
await notifyWarehouse(record.new!);
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export const createOrder = defineHttp({
|
|
207
|
+
method: "POST",
|
|
208
|
+
path: "/orders",
|
|
209
|
+
schema: (input) => parseOrder(input),
|
|
210
|
+
deps: { orders },
|
|
211
|
+
params: { apiKey: param("stripe-key") },
|
|
212
|
+
context: async ({ params }) => ({
|
|
213
|
+
stripe: new Stripe(params.apiKey),
|
|
214
|
+
}),
|
|
215
|
+
onRequest: async ({ data, ctx, deps }) => {
|
|
216
|
+
const charge = await ctx.stripe.charges.create({ amount: data.total });
|
|
217
|
+
await deps.orders.put({ id: crypto.randomUUID(), ...data, chargeId: charge.id });
|
|
218
|
+
return { status: 201, body: { ok: true } };
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
108
223
|
## Configuration
|
|
109
224
|
|
|
110
225
|
```typescript
|
|
@@ -113,6 +113,8 @@ var computeTtl = (ttlSeconds = DEFAULT_TTL_SECONDS) => Math.floor(Date.now() / 1
|
|
|
113
113
|
|
|
114
114
|
// src/runtime/handler-utils.ts
|
|
115
115
|
import { randomUUID } from "crypto";
|
|
116
|
+
import { readFileSync } from "fs";
|
|
117
|
+
import { join } from "path";
|
|
116
118
|
|
|
117
119
|
// src/runtime/ssm-client.ts
|
|
118
120
|
import { SSM } from "@aws-sdk/client-ssm";
|
|
@@ -253,6 +255,7 @@ var buildParams = async (params) => {
|
|
|
253
255
|
}
|
|
254
256
|
return result;
|
|
255
257
|
};
|
|
258
|
+
var readStatic = (filePath) => readFileSync(join(process.cwd(), filePath), "utf-8");
|
|
256
259
|
var createHandlerRuntime = (handler, handlerType) => {
|
|
257
260
|
const platform = createPlatformClient();
|
|
258
261
|
const handlerName = process.env.EFF_HANDLER ?? "unknown";
|
|
@@ -280,6 +283,7 @@ var createHandlerRuntime = (handler, handlerType) => {
|
|
|
280
283
|
if (deps) args.deps = deps;
|
|
281
284
|
const params = await getParams();
|
|
282
285
|
if (params) args.params = params;
|
|
286
|
+
if (handler.static) args.readStatic = readStatic;
|
|
283
287
|
return args;
|
|
284
288
|
};
|
|
285
289
|
const logExecution = (startTime, input, output) => {
|
package/dist/cli/index.js
CHANGED
|
@@ -70498,7 +70498,7 @@ var parseSource = (source) => {
|
|
|
70498
70498
|
const project2 = new Project({ useInMemoryFileSystem: true });
|
|
70499
70499
|
return project2.createSourceFile("input.ts", source);
|
|
70500
70500
|
};
|
|
70501
|
-
var RUNTIME_PROPS = ["onRequest", "onRecord", "onBatchComplete", "onBatch", "context", "schema", "onError", "deps", "params"];
|
|
70501
|
+
var RUNTIME_PROPS = ["onRequest", "onRecord", "onBatchComplete", "onBatch", "context", "schema", "onError", "deps", "params", "static"];
|
|
70502
70502
|
var buildConfigWithoutRuntime = (obj) => {
|
|
70503
70503
|
const props = obj.getProperties().filter((p3) => {
|
|
70504
70504
|
if (p3.getKind() === SyntaxKind.PropertyAssignment) {
|
|
@@ -70572,6 +70572,19 @@ var extractParamEntries = (obj) => {
|
|
|
70572
70572
|
}
|
|
70573
70573
|
return entries2;
|
|
70574
70574
|
};
|
|
70575
|
+
var extractStaticGlobs = (obj) => {
|
|
70576
|
+
const staticProp = obj.getProperties().find((p3) => {
|
|
70577
|
+
if (p3.getKind() === SyntaxKind.PropertyAssignment) {
|
|
70578
|
+
return p3.getName() === "static";
|
|
70579
|
+
}
|
|
70580
|
+
return false;
|
|
70581
|
+
});
|
|
70582
|
+
if (!staticProp || staticProp.getKind() !== SyntaxKind.PropertyAssignment) return [];
|
|
70583
|
+
const init = staticProp.getInitializer();
|
|
70584
|
+
if (!init || init.getKind() !== SyntaxKind.ArrayLiteralExpression) return [];
|
|
70585
|
+
const arrayLiteral = init;
|
|
70586
|
+
return arrayLiteral.getElements().filter((e) => e.getKind() === SyntaxKind.StringLiteral).map((e) => e.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue());
|
|
70587
|
+
};
|
|
70575
70588
|
var handlerRegistry = {
|
|
70576
70589
|
http: {
|
|
70577
70590
|
defineFn: "defineHttp",
|
|
@@ -70584,6 +70597,12 @@ var handlerRegistry = {
|
|
|
70584
70597
|
handlerProps: ["onRecord", "onBatch"],
|
|
70585
70598
|
wrapperFn: "wrapTableStream",
|
|
70586
70599
|
wrapperPath: "~/runtime/wrap-table-stream"
|
|
70600
|
+
},
|
|
70601
|
+
site: {
|
|
70602
|
+
defineFn: "defineSite",
|
|
70603
|
+
handlerProps: [],
|
|
70604
|
+
wrapperFn: "wrapSite",
|
|
70605
|
+
wrapperPath: "~/runtime/wrap-site"
|
|
70587
70606
|
}
|
|
70588
70607
|
};
|
|
70589
70608
|
var extractHandlerConfigs = (source, type2) => {
|
|
@@ -70605,7 +70624,8 @@ var extractHandlerConfigs = (source, type2) => {
|
|
|
70605
70624
|
const hasHandler = handlerProps.some((p3) => extractPropertyFromObject(objLiteral, p3) !== void 0);
|
|
70606
70625
|
const depsKeys = extractDepsKeys(objLiteral);
|
|
70607
70626
|
const paramEntries = extractParamEntries(objLiteral);
|
|
70608
|
-
|
|
70627
|
+
const staticGlobs = extractStaticGlobs(objLiteral);
|
|
70628
|
+
results.push({ exportName: "default", config: configObj, hasHandler, depsKeys, paramEntries, staticGlobs });
|
|
70609
70629
|
}
|
|
70610
70630
|
}
|
|
70611
70631
|
}
|
|
@@ -70626,7 +70646,8 @@ var extractHandlerConfigs = (source, type2) => {
|
|
|
70626
70646
|
const hasHandler = handlerProps.some((p3) => extractPropertyFromObject(objLiteral, p3) !== void 0);
|
|
70627
70647
|
const depsKeys = extractDepsKeys(objLiteral);
|
|
70628
70648
|
const paramEntries = extractParamEntries(objLiteral);
|
|
70629
|
-
|
|
70649
|
+
const staticGlobs = extractStaticGlobs(objLiteral);
|
|
70650
|
+
results.push({ exportName: decl.getName(), config: configObj, hasHandler, depsKeys, paramEntries, staticGlobs });
|
|
70630
70651
|
}
|
|
70631
70652
|
});
|
|
70632
70653
|
});
|
|
@@ -70646,6 +70667,7 @@ export const handler = ${wrapperFn}(${importName});
|
|
|
70646
70667
|
// src/build/bundle.ts
|
|
70647
70668
|
var extractConfigs = (source) => extractHandlerConfigs(source, "http");
|
|
70648
70669
|
var extractTableConfigs = (source) => extractHandlerConfigs(source, "table");
|
|
70670
|
+
var extractSiteConfigs = (source) => extractHandlerConfigs(source, "site");
|
|
70649
70671
|
var runtimeDir = path5.resolve(path5.dirname(fileURLToPath2(import.meta.url)), "../../dist/runtime");
|
|
70650
70672
|
var bundle = (input) => Effect_exports.gen(function* () {
|
|
70651
70673
|
const exportName = input.exportName ?? "default";
|
|
@@ -70687,8 +70709,27 @@ var zip12 = (input) => Effect_exports.async((resume2) => {
|
|
|
70687
70709
|
archive.on("end", () => resume2(Effect_exports.succeed(Buffer.concat(chunks2))));
|
|
70688
70710
|
archive.on("error", (err) => resume2(Effect_exports.fail(err)));
|
|
70689
70711
|
archive.append(input.content, { name: input.filename ?? "index.mjs", date: FIXED_DATE2 });
|
|
70712
|
+
if (input.staticFiles) {
|
|
70713
|
+
for (const file6 of input.staticFiles) {
|
|
70714
|
+
archive.append(file6.content, { name: file6.zipPath, date: FIXED_DATE2 });
|
|
70715
|
+
}
|
|
70716
|
+
}
|
|
70690
70717
|
archive.finalize();
|
|
70691
70718
|
});
|
|
70719
|
+
var resolveStaticFiles = (globs, projectDir) => {
|
|
70720
|
+
const files = [];
|
|
70721
|
+
for (const pattern2 of globs) {
|
|
70722
|
+
const matches = globSync(pattern2, { cwd: projectDir });
|
|
70723
|
+
for (const match18 of matches) {
|
|
70724
|
+
const absPath = path5.join(projectDir, match18);
|
|
70725
|
+
files.push({
|
|
70726
|
+
content: fsSync2.readFileSync(absPath),
|
|
70727
|
+
zipPath: match18
|
|
70728
|
+
});
|
|
70729
|
+
}
|
|
70730
|
+
}
|
|
70731
|
+
return files;
|
|
70732
|
+
};
|
|
70692
70733
|
var findHandlerFiles = (patterns, cwd) => {
|
|
70693
70734
|
const files = /* @__PURE__ */ new Set();
|
|
70694
70735
|
for (const pattern2 of patterns) {
|
|
@@ -70700,15 +70741,18 @@ var findHandlerFiles = (patterns, cwd) => {
|
|
|
70700
70741
|
var discoverHandlers = (files) => {
|
|
70701
70742
|
const httpHandlers = [];
|
|
70702
70743
|
const tableHandlers = [];
|
|
70744
|
+
const siteHandlers = [];
|
|
70703
70745
|
for (const file6 of files) {
|
|
70704
70746
|
if (!fsSync2.statSync(file6).isFile()) continue;
|
|
70705
70747
|
const source = fsSync2.readFileSync(file6, "utf-8");
|
|
70706
70748
|
const http = extractConfigs(source);
|
|
70707
70749
|
const table3 = extractTableConfigs(source);
|
|
70750
|
+
const site = extractSiteConfigs(source);
|
|
70708
70751
|
if (http.length > 0) httpHandlers.push({ file: file6, exports: http });
|
|
70709
70752
|
if (table3.length > 0) tableHandlers.push({ file: file6, exports: table3 });
|
|
70753
|
+
if (site.length > 0) siteHandlers.push({ file: file6, exports: site });
|
|
70710
70754
|
}
|
|
70711
|
-
return { httpHandlers, tableHandlers };
|
|
70755
|
+
return { httpHandlers, tableHandlers, siteHandlers };
|
|
70712
70756
|
};
|
|
70713
70757
|
|
|
70714
70758
|
// src/deploy/shared.ts
|
|
@@ -70750,7 +70794,8 @@ var deployCoreLambda = ({
|
|
|
70750
70794
|
layerArn,
|
|
70751
70795
|
external,
|
|
70752
70796
|
depsEnv,
|
|
70753
|
-
depsPermissions
|
|
70797
|
+
depsPermissions,
|
|
70798
|
+
staticGlobs
|
|
70754
70799
|
}) => Effect_exports.gen(function* () {
|
|
70755
70800
|
const tagCtx = {
|
|
70756
70801
|
project: input.project,
|
|
@@ -70779,7 +70824,8 @@ var deployCoreLambda = ({
|
|
|
70779
70824
|
...bundleType ? { type: bundleType } : {},
|
|
70780
70825
|
...external && external.length > 0 ? { external } : {}
|
|
70781
70826
|
});
|
|
70782
|
-
const
|
|
70827
|
+
const staticFiles = staticGlobs && staticGlobs.length > 0 ? resolveStaticFiles(staticGlobs, input.projectDir) : void 0;
|
|
70828
|
+
const code2 = yield* zip12({ content: bundled, staticFiles });
|
|
70783
70829
|
const environment2 = {
|
|
70784
70830
|
EFF_PROJECT: input.project,
|
|
70785
70831
|
EFF_STAGE: tagCtx.stage,
|
|
@@ -70803,7 +70849,7 @@ var deployCoreLambda = ({
|
|
|
70803
70849
|
});
|
|
70804
70850
|
|
|
70805
70851
|
// src/deploy/deploy-http.ts
|
|
70806
|
-
var deployLambda = ({ input, fn: fn2, layerArn, external, depsEnv, depsPermissions }) => Effect_exports.gen(function* () {
|
|
70852
|
+
var deployLambda = ({ input, fn: fn2, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect_exports.gen(function* () {
|
|
70807
70853
|
const { exportName, config: config2 } = fn2;
|
|
70808
70854
|
const handlerName = config2.name ?? exportName;
|
|
70809
70855
|
const { functionArn } = yield* deployCoreLambda({
|
|
@@ -70817,7 +70863,8 @@ var deployLambda = ({ input, fn: fn2, layerArn, external, depsEnv, depsPermissio
|
|
|
70817
70863
|
...layerArn ? { layerArn } : {},
|
|
70818
70864
|
...external ? { external } : {},
|
|
70819
70865
|
...depsEnv ? { depsEnv } : {},
|
|
70820
|
-
...depsPermissions ? { depsPermissions } : {}
|
|
70866
|
+
...depsPermissions ? { depsPermissions } : {},
|
|
70867
|
+
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
70821
70868
|
});
|
|
70822
70869
|
return { exportName, functionArn, config: config2, handlerName };
|
|
70823
70870
|
});
|
|
@@ -70940,7 +70987,7 @@ var deployAll = (input) => Effect_exports.gen(function* () {
|
|
|
70940
70987
|
|
|
70941
70988
|
// src/deploy/deploy-table.ts
|
|
70942
70989
|
var TABLE_DEFAULT_PERMISSIONS = ["dynamodb:*", "logs:*"];
|
|
70943
|
-
var deployTableFunction = ({ input, fn: fn2, layerArn, external, depsEnv, depsPermissions }) => Effect_exports.gen(function* () {
|
|
70990
|
+
var deployTableFunction = ({ input, fn: fn2, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect_exports.gen(function* () {
|
|
70944
70991
|
const { exportName, config: config2 } = fn2;
|
|
70945
70992
|
const handlerName = config2.name ?? exportName;
|
|
70946
70993
|
const tagCtx = {
|
|
@@ -70971,7 +71018,8 @@ var deployTableFunction = ({ input, fn: fn2, layerArn, external, depsEnv, depsPe
|
|
|
70971
71018
|
...layerArn ? { layerArn } : {},
|
|
70972
71019
|
...external ? { external } : {},
|
|
70973
71020
|
depsEnv: selfEnv,
|
|
70974
|
-
...depsPermissions ? { depsPermissions } : {}
|
|
71021
|
+
...depsPermissions ? { depsPermissions } : {},
|
|
71022
|
+
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
70975
71023
|
});
|
|
70976
71024
|
yield* Effect_exports.logInfo("Setting up event source mapping...");
|
|
70977
71025
|
yield* ensureEventSourceMapping({
|
|
@@ -71053,6 +71101,35 @@ var deployAllTables = (input) => Effect_exports.gen(function* () {
|
|
|
71053
71101
|
)
|
|
71054
71102
|
);
|
|
71055
71103
|
|
|
71104
|
+
// src/deploy/deploy-site.ts
|
|
71105
|
+
import { execSync } from "child_process";
|
|
71106
|
+
var deploySiteLambda = ({ input, fn: fn2, layerArn, external, depsEnv, depsPermissions }) => Effect_exports.gen(function* () {
|
|
71107
|
+
const { exportName, config: config2 } = fn2;
|
|
71108
|
+
const handlerName = config2.name ?? exportName;
|
|
71109
|
+
if (config2.build) {
|
|
71110
|
+
yield* Effect_exports.logInfo(`Building site: ${config2.build}`);
|
|
71111
|
+
yield* Effect_exports.try({
|
|
71112
|
+
try: () => execSync(config2.build, { cwd: input.projectDir, stdio: "inherit" }),
|
|
71113
|
+
catch: (error4) => new Error(`Site build failed: ${error4}`)
|
|
71114
|
+
});
|
|
71115
|
+
}
|
|
71116
|
+
const staticGlobs = [`${config2.dir}/**/*`];
|
|
71117
|
+
const { functionArn } = yield* deployCoreLambda({
|
|
71118
|
+
input,
|
|
71119
|
+
exportName,
|
|
71120
|
+
handlerName,
|
|
71121
|
+
bundleType: "site",
|
|
71122
|
+
...config2.memory ? { memory: config2.memory } : {},
|
|
71123
|
+
timeout: config2.timeout ?? 5,
|
|
71124
|
+
...layerArn ? { layerArn } : {},
|
|
71125
|
+
...external ? { external } : {},
|
|
71126
|
+
...depsEnv ? { depsEnv } : {},
|
|
71127
|
+
...depsPermissions ? { depsPermissions } : {},
|
|
71128
|
+
staticGlobs
|
|
71129
|
+
});
|
|
71130
|
+
return { exportName, functionArn, config: config2, handlerName };
|
|
71131
|
+
});
|
|
71132
|
+
|
|
71056
71133
|
// src/deploy/deploy.ts
|
|
71057
71134
|
var prepareLayer = (input) => Effect_exports.gen(function* () {
|
|
71058
71135
|
const layerResult = yield* ensureLayer({
|
|
@@ -71184,7 +71261,8 @@ var deployHttpHandlers = (ctx) => Effect_exports.gen(function* () {
|
|
|
71184
71261
|
...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
|
|
71185
71262
|
...ctx.external.length > 0 ? { external: ctx.external } : {},
|
|
71186
71263
|
depsEnv: withPlatform.depsEnv,
|
|
71187
|
-
depsPermissions: withPlatform.depsPermissions
|
|
71264
|
+
depsPermissions: withPlatform.depsPermissions,
|
|
71265
|
+
...fn2.staticGlobs.length > 0 ? { staticGlobs: fn2.staticGlobs } : {}
|
|
71188
71266
|
}).pipe(
|
|
71189
71267
|
Effect_exports.provide(
|
|
71190
71268
|
clients_exports.makeClients({
|
|
@@ -71240,7 +71318,8 @@ var deployTableHandlers = (ctx) => Effect_exports.gen(function* () {
|
|
|
71240
71318
|
...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
|
|
71241
71319
|
...ctx.external.length > 0 ? { external: ctx.external } : {},
|
|
71242
71320
|
depsEnv: withPlatform.depsEnv,
|
|
71243
|
-
depsPermissions: withPlatform.depsPermissions
|
|
71321
|
+
depsPermissions: withPlatform.depsPermissions,
|
|
71322
|
+
...fn2.staticGlobs.length > 0 ? { staticGlobs: fn2.staticGlobs } : {}
|
|
71244
71323
|
}).pipe(
|
|
71245
71324
|
Effect_exports.provide(
|
|
71246
71325
|
clients_exports.makeClients({
|
|
@@ -71255,19 +71334,86 @@ var deployTableHandlers = (ctx) => Effect_exports.gen(function* () {
|
|
|
71255
71334
|
}
|
|
71256
71335
|
return results;
|
|
71257
71336
|
});
|
|
71337
|
+
var deploySiteHandlers = (ctx) => Effect_exports.gen(function* () {
|
|
71338
|
+
const results = [];
|
|
71339
|
+
for (const { file: file6, exports } of ctx.handlers) {
|
|
71340
|
+
yield* Effect_exports.logInfo(`Processing ${path7.basename(file6)} (${exports.length} site handler(s))`);
|
|
71341
|
+
const deployInput = {
|
|
71342
|
+
projectDir: ctx.input.projectDir,
|
|
71343
|
+
file: file6,
|
|
71344
|
+
project: ctx.input.project,
|
|
71345
|
+
region: ctx.input.region
|
|
71346
|
+
};
|
|
71347
|
+
if (ctx.input.stage) deployInput.stage = ctx.input.stage;
|
|
71348
|
+
for (const fn2 of exports) {
|
|
71349
|
+
const withPlatform = {
|
|
71350
|
+
depsEnv: { ...ctx.platformEnv },
|
|
71351
|
+
depsPermissions: [...ctx.platformPermissions]
|
|
71352
|
+
};
|
|
71353
|
+
const { exportName, functionArn, config: config2, handlerName } = yield* deploySiteLambda({
|
|
71354
|
+
input: deployInput,
|
|
71355
|
+
fn: fn2,
|
|
71356
|
+
...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
|
|
71357
|
+
...ctx.external.length > 0 ? { external: ctx.external } : {},
|
|
71358
|
+
depsEnv: withPlatform.depsEnv,
|
|
71359
|
+
depsPermissions: withPlatform.depsPermissions
|
|
71360
|
+
}).pipe(
|
|
71361
|
+
Effect_exports.provide(
|
|
71362
|
+
clients_exports.makeClients({
|
|
71363
|
+
lambda: { region: ctx.input.region },
|
|
71364
|
+
iam: { region: ctx.input.region }
|
|
71365
|
+
})
|
|
71366
|
+
)
|
|
71367
|
+
);
|
|
71368
|
+
const basePath = config2.path.replace(/\/+$/, "") || "/";
|
|
71369
|
+
const { apiUrl: rootUrl } = yield* addRouteToApi({
|
|
71370
|
+
apiId: ctx.apiId,
|
|
71371
|
+
region: ctx.input.region,
|
|
71372
|
+
functionArn,
|
|
71373
|
+
method: "GET",
|
|
71374
|
+
path: basePath
|
|
71375
|
+
}).pipe(
|
|
71376
|
+
Effect_exports.provide(
|
|
71377
|
+
clients_exports.makeClients({
|
|
71378
|
+
lambda: { region: ctx.input.region },
|
|
71379
|
+
apigatewayv2: { region: ctx.input.region }
|
|
71380
|
+
})
|
|
71381
|
+
)
|
|
71382
|
+
);
|
|
71383
|
+
yield* addRouteToApi({
|
|
71384
|
+
apiId: ctx.apiId,
|
|
71385
|
+
region: ctx.input.region,
|
|
71386
|
+
functionArn,
|
|
71387
|
+
method: "GET",
|
|
71388
|
+
path: `${basePath}/{file+}`
|
|
71389
|
+
}).pipe(
|
|
71390
|
+
Effect_exports.provide(
|
|
71391
|
+
clients_exports.makeClients({
|
|
71392
|
+
lambda: { region: ctx.input.region },
|
|
71393
|
+
apigatewayv2: { region: ctx.input.region }
|
|
71394
|
+
})
|
|
71395
|
+
)
|
|
71396
|
+
);
|
|
71397
|
+
results.push({ exportName, url: rootUrl, functionArn });
|
|
71398
|
+
yield* Effect_exports.logInfo(` GET ${basePath} \u2192 ${handlerName} (site)`);
|
|
71399
|
+
}
|
|
71400
|
+
}
|
|
71401
|
+
return results;
|
|
71402
|
+
});
|
|
71258
71403
|
var deployProject = (input) => Effect_exports.gen(function* () {
|
|
71259
71404
|
const files = findHandlerFiles(input.patterns, input.projectDir);
|
|
71260
71405
|
if (files.length === 0) {
|
|
71261
71406
|
return yield* Effect_exports.fail(new Error(`No files match patterns: ${input.patterns.join(", ")}`));
|
|
71262
71407
|
}
|
|
71263
71408
|
yield* Effect_exports.logInfo(`Found ${files.length} file(s) matching patterns`);
|
|
71264
|
-
const { httpHandlers, tableHandlers } = discoverHandlers(files);
|
|
71409
|
+
const { httpHandlers, tableHandlers, siteHandlers } = discoverHandlers(files);
|
|
71265
71410
|
const totalHttpHandlers = httpHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
71266
71411
|
const totalTableHandlers = tableHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
71267
|
-
|
|
71412
|
+
const totalSiteHandlers = siteHandlers.reduce((acc, h) => acc + h.exports.length, 0);
|
|
71413
|
+
if (totalHttpHandlers === 0 && totalTableHandlers === 0 && totalSiteHandlers === 0) {
|
|
71268
71414
|
return yield* Effect_exports.fail(new Error("No handlers found in matched files"));
|
|
71269
71415
|
}
|
|
71270
|
-
yield* Effect_exports.logInfo(`Discovered ${totalHttpHandlers} HTTP
|
|
71416
|
+
yield* Effect_exports.logInfo(`Discovered ${totalHttpHandlers} HTTP, ${totalTableHandlers} table, ${totalSiteHandlers} site handler(s)`);
|
|
71271
71417
|
const tableNameMap = buildTableNameMap(tableHandlers, input.project, resolveStage(input.stage));
|
|
71272
71418
|
const { layerArn, external } = yield* prepareLayer({
|
|
71273
71419
|
project: input.project,
|
|
@@ -71280,7 +71426,7 @@ var deployProject = (input) => Effect_exports.gen(function* () {
|
|
|
71280
71426
|
const platformEnv = { EFF_PLATFORM_TABLE: platformTableName };
|
|
71281
71427
|
let apiId;
|
|
71282
71428
|
let apiUrl;
|
|
71283
|
-
if (totalHttpHandlers > 0) {
|
|
71429
|
+
if (totalHttpHandlers > 0 || totalSiteHandlers > 0) {
|
|
71284
71430
|
const tagCtx = {
|
|
71285
71431
|
project: input.project,
|
|
71286
71432
|
stage: resolveStage(input.stage),
|
|
@@ -71321,10 +71467,19 @@ var deployProject = (input) => Effect_exports.gen(function* () {
|
|
|
71321
71467
|
platformEnv,
|
|
71322
71468
|
platformPermissions: PLATFORM_PERMISSIONS
|
|
71323
71469
|
});
|
|
71470
|
+
const siteResults = apiId ? yield* deploySiteHandlers({
|
|
71471
|
+
handlers: siteHandlers,
|
|
71472
|
+
apiId,
|
|
71473
|
+
input,
|
|
71474
|
+
layerArn,
|
|
71475
|
+
external,
|
|
71476
|
+
platformEnv,
|
|
71477
|
+
platformPermissions: PLATFORM_PERMISSIONS
|
|
71478
|
+
}) : [];
|
|
71324
71479
|
if (apiUrl) {
|
|
71325
71480
|
yield* Effect_exports.logInfo(`Deployment complete! API: ${apiUrl}`);
|
|
71326
71481
|
}
|
|
71327
|
-
return { apiId, apiUrl, httpResults, tableResults };
|
|
71482
|
+
return { apiId, apiUrl, httpResults, tableResults, siteResults };
|
|
71328
71483
|
});
|
|
71329
71484
|
|
|
71330
71485
|
// src/cli/config.ts
|
|
@@ -71443,7 +71598,7 @@ var deployCommand = Command_exports.make(
|
|
|
71443
71598
|
stage: finalStage,
|
|
71444
71599
|
region: finalRegion
|
|
71445
71600
|
});
|
|
71446
|
-
const total = results.httpResults.length + results.tableResults.length;
|
|
71601
|
+
const total = results.httpResults.length + results.tableResults.length + results.siteResults.length;
|
|
71447
71602
|
yield* Console_exports.log(`
|
|
71448
71603
|
Deployed ${total} handler(s):`);
|
|
71449
71604
|
for (const r of results.httpResults) {
|
|
@@ -71452,6 +71607,9 @@ Deployed ${total} handler(s):`);
|
|
|
71452
71607
|
for (const r of results.tableResults) {
|
|
71453
71608
|
yield* Console_exports.log(` [table] ${r.exportName}: ${r.tableArn}`);
|
|
71454
71609
|
}
|
|
71610
|
+
for (const r of results.siteResults) {
|
|
71611
|
+
yield* Console_exports.log(` [site] ${r.exportName}: ${r.url}`);
|
|
71612
|
+
}
|
|
71455
71613
|
}),
|
|
71456
71614
|
onSome: (targetValue) => Effect_exports.gen(function* () {
|
|
71457
71615
|
if (isFilePath(targetValue)) {
|
|
@@ -71504,7 +71662,7 @@ Deployed ${tableResults.length} table handler(s):`);
|
|
|
71504
71662
|
const discovered = discoverHandlers(files);
|
|
71505
71663
|
let foundFile = null;
|
|
71506
71664
|
let foundExport = null;
|
|
71507
|
-
let
|
|
71665
|
+
let handlerType = "http";
|
|
71508
71666
|
for (const { file: file6, exports } of discovered.httpHandlers) {
|
|
71509
71667
|
for (const { exportName, config: handlerConfig } of exports) {
|
|
71510
71668
|
if (handlerConfig.name === targetValue) {
|
|
@@ -71521,7 +71679,20 @@ Deployed ${tableResults.length} table handler(s):`);
|
|
|
71521
71679
|
if (handlerConfig.name === targetValue) {
|
|
71522
71680
|
foundFile = file6;
|
|
71523
71681
|
foundExport = exportName;
|
|
71524
|
-
|
|
71682
|
+
handlerType = "table";
|
|
71683
|
+
break;
|
|
71684
|
+
}
|
|
71685
|
+
}
|
|
71686
|
+
if (foundFile) break;
|
|
71687
|
+
}
|
|
71688
|
+
}
|
|
71689
|
+
if (!foundFile) {
|
|
71690
|
+
for (const { file: file6, exports } of discovered.siteHandlers) {
|
|
71691
|
+
for (const { exportName, config: handlerConfig } of exports) {
|
|
71692
|
+
if (handlerConfig.name === targetValue) {
|
|
71693
|
+
foundFile = file6;
|
|
71694
|
+
foundExport = exportName;
|
|
71695
|
+
handlerType = "site";
|
|
71525
71696
|
break;
|
|
71526
71697
|
}
|
|
71527
71698
|
}
|
|
@@ -71541,6 +71712,11 @@ Deployed ${tableResults.length} table handler(s):`);
|
|
|
71541
71712
|
yield* Console_exports.log(` [table] ${c.name}`);
|
|
71542
71713
|
}
|
|
71543
71714
|
}
|
|
71715
|
+
for (const { exports } of discovered.siteHandlers) {
|
|
71716
|
+
for (const { config: c } of exports) {
|
|
71717
|
+
yield* Console_exports.log(` [site] ${c.name}`);
|
|
71718
|
+
}
|
|
71719
|
+
}
|
|
71544
71720
|
return;
|
|
71545
71721
|
}
|
|
71546
71722
|
yield* Console_exports.log(`Found handler "${targetValue}" in ${path9.relative(projectDir, foundFile)}`);
|
|
@@ -71552,7 +71728,7 @@ Deployed ${tableResults.length} table handler(s):`);
|
|
|
71552
71728
|
region: finalRegion,
|
|
71553
71729
|
exportName: foundExport
|
|
71554
71730
|
};
|
|
71555
|
-
if (
|
|
71731
|
+
if (handlerType === "table") {
|
|
71556
71732
|
const result = yield* deployTable(input);
|
|
71557
71733
|
yield* Console_exports.log(`
|
|
71558
71734
|
Table deployed: ${result.tableArn}`);
|