effortless-aws 0.32.0 → 0.32.1

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 CHANGED
@@ -13,15 +13,8 @@ npm install effortless-aws
13
13
  ```typescript
14
14
  import { defineApi } from "effortless-aws";
15
15
 
16
- export const hello = defineApi({
17
- basePath: "/hello",
18
- get: {
19
- "/": async () => ({
20
- status: 200,
21
- body: { message: "Hello!" },
22
- }),
23
- },
24
- });
16
+ export const hello = defineApi({ basePath: "/hello" })
17
+ .get("/", async ({ ok }) => ok({ message: "Hello!" }));
25
18
  ```
26
19
 
27
20
  ## Handlers
@@ -39,9 +32,9 @@ export const hello = defineApi({
39
32
  ## Features
40
33
 
41
34
  - **Infrastructure from code** — export a handler, get the AWS resources
42
- - **Typed everything** — `defineTable<Order>` gives you typed `put()`, typed `deps.orders.get()`, typed `record.new`
43
- - **Cross-handler deps** — `deps: { orders }` auto-wires IAM and injects a typed `TableClient`
44
- - **SSM params** — `param("stripe-key")` fetches from Parameter Store at cold start
35
+ - **Typed everything** — `defineTable<Order>()` gives you typed `put()`, typed `deps.orders.get()`, typed `record.new`
36
+ - **Cross-handler deps** — `.deps(() => ({ orders }))` auto-wires IAM and injects a typed `TableClient`
37
+ - **SSM params** — `.config(({ defineSecret }) => ...)` fetches secrets from Parameter Store at cold start
45
38
  - **Static files** — `static: ["templates/*.ejs"]` bundles files into the Lambda ZIP
46
39
  - **Cold start caching** — `setup` factory runs once per cold start, cached across invocations
47
40
 
@@ -48,9 +48,37 @@ var unauthorized = () => ({
48
48
  headers: { "Content-Type": "application/json" },
49
49
  body: JSON.stringify({ error: "Unauthorized" })
50
50
  });
51
- var findRoute = (routes, method, relativePath) => routes.find(
52
- (r) => r.path === relativePath && (r.method === method || r.method === "GET" && method === "HEAD")
53
- );
51
+ var extractParamNames = (pattern) => {
52
+ const names = [];
53
+ pattern.replace(/\{(\w+)\}/g, (_, name) => {
54
+ names.push(name);
55
+ return "";
56
+ });
57
+ return names;
58
+ };
59
+ var patternToRegex = (pattern) => {
60
+ const escaped = pattern.replace(
61
+ /[.*+?^${}()|[\]\\]/g,
62
+ (ch) => ch === "{" || ch === "}" ? ch : `\\${ch}`
63
+ );
64
+ const withParams = escaped.replace(/\{(\w+)\}/g, "([^/]+)");
65
+ return new RegExp(`^${withParams}$`);
66
+ };
67
+ var findRoute = (routes, method, relativePath) => {
68
+ for (const r of routes) {
69
+ if (!(r.method === method || r.method === "GET" && method === "HEAD")) continue;
70
+ if (r.path === relativePath) return { entry: r, params: {} };
71
+ const regex = patternToRegex(r.path);
72
+ const match = relativePath.match(regex);
73
+ if (match) {
74
+ const names = extractParamNames(r.path);
75
+ const params = {};
76
+ for (let i = 0; i < names.length; i++) params[names[i]] = decodeURIComponent(match[i + 1]);
77
+ return { entry: r, params };
78
+ }
79
+ }
80
+ return void 0;
81
+ };
54
82
  var wrapApi = (handler) => {
55
83
  const rt = createHandlerRuntime(handler, "api", handler.__spec.lambda?.logLevel ?? "info", () => ({ ok, fail }));
56
84
  const basePath = handler.__spec.basePath;
@@ -85,9 +113,21 @@ var wrapApi = (handler) => {
85
113
  const method = event.requestContext?.http?.method ?? event.httpMethod ?? "GET";
86
114
  const path = event.requestContext?.http?.path ?? event.path ?? "/";
87
115
  const headers = event.headers ?? {};
88
- const query = event.queryStringParameters ?? {};
89
- const params = event.pathParameters ?? {};
90
116
  const body = parseBody(event.body, event.isBase64Encoded ?? false);
117
+ const logInput = { method, path, query: event.queryStringParameters ?? {}, body };
118
+ const relativePath = extractRelativePath(path);
119
+ if (!relativePath) {
120
+ rt.logExecution(startTime, logInput, { status: 404 });
121
+ return notFound();
122
+ }
123
+ const routeMatch = findRoute(routes, method, relativePath);
124
+ if (!routeMatch) {
125
+ rt.logExecution(startTime, logInput, { status: 404 });
126
+ return notFound();
127
+ }
128
+ const { entry, params: routeParams } = routeMatch;
129
+ const query = event.queryStringParameters ?? {};
130
+ const params = { ...event.pathParameters ?? {}, ...routeParams };
91
131
  const merged = {
92
132
  ...query,
93
133
  ...typeof body === "object" && body !== null ? body : {},
@@ -102,17 +142,6 @@ var wrapApi = (handler) => {
102
142
  body,
103
143
  rawBody: event.body
104
144
  };
105
- const logInput = { method, path, query, body };
106
- const relativePath = extractRelativePath(req.path);
107
- if (!relativePath) {
108
- rt.logExecution(startTime, logInput, { status: 404 });
109
- return notFound();
110
- }
111
- const entry = findRoute(routes, req.method, relativePath);
112
- if (!entry) {
113
- rt.logExecution(startTime, logInput, { status: 404 });
114
- return notFound();
115
- }
116
145
  const cookieHeader = req.headers["cookie"] ?? req.headers["Cookie"] ?? "";
117
146
  let authCookie;
118
147
  if (cookieHeader) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effortless-aws",
3
- "version": "0.32.0",
3
+ "version": "0.32.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Code-first AWS Lambda framework. Export handlers, deploy with one command.",