@daloyjs/core 0.7.4 → 0.8.2

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 (67) hide show
  1. package/README.md +33 -63
  2. package/dist/adapters/bun.d.ts +26 -4
  3. package/dist/adapters/bun.d.ts.map +1 -1
  4. package/dist/adapters/bun.js +16 -4
  5. package/dist/adapters/bun.js.map +1 -1
  6. package/dist/adapters/cloudflare.d.ts +21 -4
  7. package/dist/adapters/cloudflare.d.ts.map +1 -1
  8. package/dist/adapters/cloudflare.js.map +1 -1
  9. package/dist/adapters/deno.d.ts +25 -2
  10. package/dist/adapters/deno.d.ts.map +1 -1
  11. package/dist/adapters/deno.js +39 -7
  12. package/dist/adapters/deno.js.map +1 -1
  13. package/dist/adapters/fastly.d.ts +23 -0
  14. package/dist/adapters/fastly.d.ts.map +1 -0
  15. package/dist/adapters/fastly.js +11 -0
  16. package/dist/adapters/fastly.js.map +1 -0
  17. package/dist/adapters/lambda.d.ts +67 -0
  18. package/dist/adapters/lambda.d.ts.map +1 -0
  19. package/dist/adapters/lambda.js +117 -0
  20. package/dist/adapters/lambda.js.map +1 -0
  21. package/dist/adapters/node.d.ts +7 -0
  22. package/dist/adapters/node.d.ts.map +1 -1
  23. package/dist/adapters/node.js +17 -4
  24. package/dist/adapters/node.js.map +1 -1
  25. package/dist/adapters/vercel.d.ts +35 -7
  26. package/dist/adapters/vercel.d.ts.map +1 -1
  27. package/dist/adapters/vercel.js +22 -1
  28. package/dist/adapters/vercel.js.map +1 -1
  29. package/dist/app.d.ts +235 -8
  30. package/dist/app.d.ts.map +1 -1
  31. package/dist/app.js +222 -8
  32. package/dist/app.js.map +1 -1
  33. package/dist/client.d.ts +27 -0
  34. package/dist/client.d.ts.map +1 -1
  35. package/dist/client.js +27 -0
  36. package/dist/client.js.map +1 -1
  37. package/dist/errors.d.ts +169 -3
  38. package/dist/errors.d.ts.map +1 -1
  39. package/dist/errors.js +143 -0
  40. package/dist/errors.js.map +1 -1
  41. package/dist/logger.d.ts +26 -0
  42. package/dist/logger.d.ts.map +1 -1
  43. package/dist/logger.js +26 -0
  44. package/dist/logger.js.map +1 -1
  45. package/dist/middleware.d.ts +143 -0
  46. package/dist/middleware.d.ts.map +1 -1
  47. package/dist/middleware.js +147 -4
  48. package/dist/middleware.js.map +1 -1
  49. package/dist/openapi.d.ts +32 -0
  50. package/dist/openapi.d.ts.map +1 -1
  51. package/dist/openapi.js +33 -2
  52. package/dist/openapi.js.map +1 -1
  53. package/dist/schema.d.ts +33 -2
  54. package/dist/schema.d.ts.map +1 -1
  55. package/dist/schema.js +33 -2
  56. package/dist/schema.js.map +1 -1
  57. package/dist/security-schemes.d.ts +75 -5
  58. package/dist/security-schemes.d.ts.map +1 -1
  59. package/dist/security-schemes.js +75 -5
  60. package/dist/security-schemes.js.map +1 -1
  61. package/dist/security.d.ts +73 -4
  62. package/dist/security.d.ts.map +1 -1
  63. package/dist/security.js +73 -4
  64. package/dist/security.js.map +1 -1
  65. package/dist/types.d.ts +195 -5
  66. package/dist/types.d.ts.map +1 -1
  67. package/package.json +9 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ DaloyJS exists to be the framework you'd build if you took the best ideas from e
24
24
  | **Supply-chain-hardened installs and publishing** | [pnpm](https://pnpm.io/motivation) + hardened CI/CD | `ignore-scripts`, release-age cooldown, explicit build allowlist, SHA-pinned actions, isolated OIDC publish with provenance. |
25
25
 
26
26
  ```
27
- 320/320 framework tests passing · 100% line + function coverage · clean strict TypeScript 6
27
+ framework test suite passing · 100% line + function coverage · clean strict TypeScript 6
28
28
  runs on Node, Bun, Deno, Cloudflare, Vercel
29
29
  ~12.3M static-route ops/sec · ~1.5M dynamic-route ops/sec on M-class CPU
30
30
  ```
@@ -61,9 +61,12 @@ For a new DaloyJS project, the recommended path is the official scaffolder:
61
61
  pnpm create daloy@latest my-api
62
62
  # or
63
63
  npm create daloy@latest my-api
64
+
65
+ # add GitHub Actions + governance files for a company repo
66
+ pnpm create daloy@latest my-api --with-ci --code-owner @acme/security
64
67
  ```
65
68
 
66
- `create-daloy` gives you a working project structure, runtime template selection, docs routes, OpenAPI wiring, and production-oriented defaults without copying code out of the README.
69
+ `create-daloy` gives you a working project structure, runtime template selection, docs routes, OpenAPI wiring, production-oriented defaults, and an optional hardened GitHub security bundle without copying code out of the README.
67
70
 
68
71
  See [Scaffold a project](https://daloyjs.dev/docs/scaffolder) for templates and flags.
69
72
 
@@ -178,7 +181,7 @@ import { swaggerUiHtml, htmlResponse } from "@daloyjs/core/docs";
178
181
  ```
179
182
 
180
183
  Mount at `/docs` and the UI is always contract-accurate — never stale.
181
- `create-daloy@0.1.20` mounts Swagger UI at `/docs` and the live spec at `/openapi.json` by default.
184
+ `create-daloy@0.2.0` mounts Swagger UI at `/docs` and the live spec at `/openapi.json` by default.
182
185
 
183
186
  ---
184
187
 
@@ -262,11 +265,17 @@ await app.ready();
262
265
  ## Multi-runtime
263
266
 
264
267
  ```ts
265
- import { serve } from "@daloyjs/core/node"; // Node
266
- import { serve } from "@daloyjs/core/bun"; // Bun
267
- import { serve } from "@daloyjs/core/deno"; // Deno
268
+ import { serve } from "@daloyjs/core/node"; // Node (Heroku, Railway, Render, Fly.io, any PaaS)
269
+ import { serve } from "@daloyjs/core/bun"; // Bun
270
+ import { serve } from "@daloyjs/core/deno"; // Deno
268
271
  import { toFetchHandler } from "@daloyjs/core/cloudflare"; // Cloudflare Workers
269
- import { toEdgeHandler } from "@daloyjs/core/vercel"; // Vercel Edge / Next.js
272
+ import {
273
+ toFetchHandler as toVercelFetchHandler,
274
+ toRouteHandlers,
275
+ toWebHandler,
276
+ } from "@daloyjs/core/vercel"; // Vercel Node / Edge / Next.js / Netlify Edge
277
+ import { installFastlyListener } from "@daloyjs/core/fastly"; // Fastly Compute
278
+ import { toLambdaHandler } from "@daloyjs/core/lambda"; // AWS Lambda / Netlify Functions / Lambda Function URLs
270
279
  ```
271
280
 
272
281
  The core only ever sees `Request → Response`. Adapters live at the edge.
@@ -285,62 +294,23 @@ The core only ever sees `Request → Response`. Adapters live at the edge.
285
294
 
286
295
  ---
287
296
 
288
- ## Status & roadmap
289
-
290
- Full, versioned plan: [ROADMAP.md](./ROADMAP.md).
291
-
292
- **Implemented (v0.1):**
293
-
294
- - [x] Trie router with static fast path + 405 with `Allow` + traversal guard
295
- - [x] Contract-first `app.route()`, groups, encapsulated plugins, decorators
296
- - [x] Standard Schema validation (Zod 4 / Valibot / ArkType / TypeBox)
297
- - [x] Problem+json error model with prod-mode redaction
298
- - [x] OpenAPI 3.1 generator (built-in)
299
- - [x] In-process test client + contract-test runner
300
- - [x] In-process typed client factory + Hey API codegen integration (`pnpm gen`)
301
- - [x] Node / Bun / Deno / Cloudflare / Vercel adapters
302
- - [x] Security: body limits, content-type allowlist, prototype-pollution-safe JSON, path-traversal rejection, request timeout, header injection guards
303
- - [x] Security middleware: `secureHeaders` / `cors` / `rateLimit` / `requestId` / `bearerAuth` / `timing` / `timingSafeEqual`
304
- - [x] Pluggable structured logger + request id propagation
305
- - [x] Graceful shutdown
306
- - [x] `app.onClose()` lifecycle hook + augmentable `AppState` for plugin-typed context
307
- - [x] Mock mode
308
- - [x] Scalar + Swagger UI handlers
309
- - [x] **pnpm-first distribution with hardened `.npmrc`**
310
- - [x] **Lockfile source verification for git/non-registry tarball dependencies**
311
- - [x] **100% line + function coverage** enforced by `pnpm coverage`
312
-
313
- **Current (`0.2.x` follow-up — see [ROADMAP.md](./ROADMAP.md) for the full plan):**
314
-
315
- - [x] `onSend` hook for response transformation
316
- - [x] GitHub Actions CI for install, typecheck, tests, coverage, build, and audit
317
- - [x] `SECURITY.md` and vulnerability disclosure process
318
- - [x] `pnpm create daloy` project scaffolder (Node + Vercel Edge + Cloudflare templates)
319
- - [x] Docs site discoverability pass: page metadata, sitemap, robots, OpenGraph image, ORM guides
320
- - [x] Streaming response helpers: SSE + NDJSON with backpressure-safe writers (`sseStream` / `sseResponse` / `ndjsonStream` / `ndjsonResponse`)
321
- - [x] OpenAPI extras: `securitySchemes` builders (`httpBearerScheme` / `httpBasicScheme` / `apiKeyScheme` / `oauth2Scheme` / `openIdConnectScheme`), top-level `webhooks`, per-operation `callbacks`, and `discriminator` / `discriminatedUnion` helpers
322
- - [x] OpenTelemetry-compatible tracing hook (`otelTracing`) with HTTP semantic-convention attributes and per-request `SERVER` spans
323
- - [x] CSRF helper (`csrf`) with double-submit-cookie pattern, timing-safe verification, and `__Host-` cookie defaults
324
- - [x] Multipart/form-data ergonomics: `fileField` + `multipartObject` (per-file size and MIME caps, OpenAPI-aware emission) and `AppOptions.multipart` defense-in-depth caps
325
- - [ ] Branch coverage push to `>= 98%`
326
- - [ ] Release checklist and publishing docs cleanup
327
-
328
- **On deck (`0.3.x` and beyond):**
329
- WebSockets and HTTP/2 + HTTP/3 adapters.
330
-
331
- **Shipped from the `0.x` extensibility track so far:**
332
-
333
- - [x] Plugin lifecycle events: `app.onPluginInstalled((info) => ...)` fires once per `register()` (sync or async), and `app.onShutdown(({ reason, timeoutMs }) => ...)` fires at the start of `app.shutdown()` before in-flight requests drain. `onClose()` still runs after drain for resource cleanup.
334
- - [x] Edge-friendly session middleware: `session({ secret, store })` exposes `ctx.state.session` (`get` / `set` / `delete` / `regenerate` / `destroy`) backed by a signed `__Host-` cookie (HMAC-SHA256, key rotation) and a pluggable `SessionStore` (`MemorySessionStore` ships in-process; KV/Redis stores plug in directly). Standalone `signValue` / `verifySignedValue` helpers are exported for ad-hoc cookie/token signing.
335
-
336
- **Shipped from `0.5.x` ("project ops") so far:**
337
-
338
- - [x] Bun and Deno scaffolder templates (`bun-basic`, `deno-basic`)
339
- - [x] `--minimal` flag that strips the bookstore demo and `/docs` + `/openapi.json` routes from any template
340
- - [x] `daloy inspect` CLI: route table, schema summary, contract-test gate, OpenAPI dump, tag/method filters
341
- - [x] Redis-backed `RateLimitStore` at `@daloyjs/core/rate-limit-redis` with `ioredisAdapter` / `nodeRedisAdapter` and a fail-open default for shared counters across replicas
342
- - [x] AI-agent helper files (`AGENTS.md` + `SKILL.md`) shipped in every `create-daloy` template so Copilot/Claude/Cursor/Codex have project context out of the box
343
- - [x] Polished `create-daloy` terminal UX: DaloyJS welcome banner, arrow-key pickers, install spinner, and boxed next steps while preserving zero runtime dependencies
297
+ ## Status
298
+
299
+ DaloyJS is in **public preview** (`0.x`). The public API may still change between minor versions; deprecations will get at least one minor cycle once `1.0.0` ships. The framework is already in use for production trials — every release ships with **100% line + function test coverage**, strict TypeScript, OpenSSF Scorecard, CodeQL, zizmor workflow linting, and npm provenance.
300
+
301
+ What works today, at a glance:
302
+
303
+ - Contract-first routing, Standard Schema validation (Zod 4 / Valibot / ArkType / TypeBox), and OpenAPI 3.1 from a single source of truth.
304
+ - Adapters for Node (Heroku/Railway/Render/Fly.io), Bun, Deno, Cloudflare Workers, Vercel Node / Edge / Next.js / Netlify Edge, Fastly Compute, and AWS Lambda / Netlify Functions / Lambda Function URLs.
305
+ - Built-in security primitives (body limits, prototype-pollution-safe JSON, path-traversal guard, request timeouts, header injection guards) plus first-party middleware (`secureHeaders`, `cors`, `rateLimit`, `requestId`, `bearerAuth`, `csrf`, `session`, `timing` / `timingSafeEqual`).
306
+ - Streaming helpers (SSE + NDJSON), multipart ergonomics, OpenTelemetry-compatible tracing, signed-cookie sessions with pluggable stores, and a Redis-backed rate-limit store at `@daloyjs/core/rate-limit-redis`.
307
+ - In-process test client (`app.request()`), contract-test runner, in-process typed client, and Hey API codegen via `pnpm gen`.
308
+ - `pnpm create daloy` scaffolder with Node, Bun, Deno, Cloudflare Worker, and Vercel Edge templates, plus optional `--with-ci` GitHub Actions / Dependabot / CODEOWNERS / SECURITY.md hardening.
309
+ - Plugin encapsulation, decorators, structured logging, request-id propagation, lifecycle events (`onPluginInstalled`, `onShutdown`, `onClose`), and graceful shutdown.
310
+ - Integration guides for transactional email providers AWS SES, SendGrid, Resend, Postmark, Mailgun, and Mailtrap — with a common `EmailSender` plugin pattern and runtime-compatibility matrix.
311
+ - Authentication & authorization guides for AWS Cognito, Microsoft Entra ID (MSAL), Auth0, Okta, and Clerk — with a common bearer-auth plugin, scope/role enforcement, and runtime-compatibility matrix.
312
+
313
+ Roadmap, version-by-version plan, and shipped/in-progress checklists live in [ROADMAP.md](./ROADMAP.md).
344
314
 
345
315
  ## License
346
316
 
@@ -1,16 +1,38 @@
1
1
  /**
2
2
  * Bun adapter — `Bun.serve` already speaks web-standard fetch,
3
- * so this is the smallest possible wrapper.
3
+ * so this is the smallest possible wrapper. The adapter passes through the
4
+ * commonly-needed modern `Bun.serve` options (`idleTimeout`, `tls`,
5
+ * `development`, `unix`) and exposes the server's `url` for ergonomic logging.
4
6
  */
5
7
  import type { App } from "../app.js";
8
+ export interface BunTLSOptions {
9
+ /** PEM certificate. */
10
+ cert: string;
11
+ /** PEM private key. */
12
+ key: string;
13
+ /** Optional passphrase for the key. */
14
+ passphrase?: string;
15
+ /** Optional CA bundle. */
16
+ ca?: string;
17
+ }
6
18
  export interface BunServeOptions {
7
19
  port?: number;
8
20
  hostname?: string;
9
21
  /** Maximum request body bytes (Bun-level cap). Default: 16 MiB. */
10
22
  maxRequestBodySize?: number;
23
+ /** Seconds before an idle connection is closed. Default: Bun default (10). */
24
+ idleTimeout?: number;
25
+ /** When true, Bun enables development-mode error pages and verbose output. */
26
+ development?: boolean;
27
+ /** Optional unix socket path; when set, TCP `port`/`hostname` are not passed to Bun. */
28
+ unix?: string;
29
+ /** When supplied, Bun.serve listens on HTTPS. */
30
+ tls?: BunTLSOptions;
11
31
  }
12
- export declare function serve(app: App, opts?: BunServeOptions): {
13
- stop: () => Promise<void>;
32
+ export interface BunServerHandle {
14
33
  port: number;
15
- };
34
+ url: URL | undefined;
35
+ stop: () => Promise<void>;
36
+ }
37
+ export declare function serve(app: App, opts?: BunServeOptions): BunServerHandle;
16
38
  //# sourceMappingURL=bun.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../../src/adapters/bun.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,eAAoB,GAAG;IAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA0BvG"}
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../../src/adapters/bun.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC;IACrB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,eAAoB,GAAG,eAAe,CAoC3E"}
@@ -2,9 +2,7 @@ export function serve(app, opts = {}) {
2
2
  const Bun = globalThis.Bun;
3
3
  if (!Bun?.serve)
4
4
  throw new Error("Bun runtime not detected");
5
- const server = Bun.serve({
6
- port: opts.port ?? 3000,
7
- hostname: opts.hostname ?? "0.0.0.0",
5
+ const cfg = {
8
6
  maxRequestBodySize: opts.maxRequestBodySize ?? 16 * 1024 * 1024,
9
7
  fetch: (req) => app.fetch(req),
10
8
  error: (err) => new Response(JSON.stringify({
@@ -13,9 +11,23 @@ export function serve(app, opts = {}) {
13
11
  status: 500,
14
12
  detail: err.message,
15
13
  }), { status: 500, headers: { "content-type": "application/problem+json" } }),
16
- });
14
+ };
15
+ if (opts.unix === undefined) {
16
+ cfg.port = opts.port ?? 3000;
17
+ cfg.hostname = opts.hostname ?? "0.0.0.0";
18
+ }
19
+ if (opts.idleTimeout !== undefined)
20
+ cfg.idleTimeout = opts.idleTimeout;
21
+ if (opts.development !== undefined)
22
+ cfg.development = opts.development;
23
+ if (opts.unix !== undefined)
24
+ cfg.unix = opts.unix;
25
+ if (opts.tls)
26
+ cfg.tls = opts.tls;
27
+ const server = Bun.serve(cfg);
17
28
  return {
18
29
  port: server.port,
30
+ url: server.url,
19
31
  stop: async () => {
20
32
  await app.shutdown();
21
33
  server.stop(true);
@@ -1 +1 @@
1
- {"version":3,"file":"bun.js","sourceRoot":"","sources":["../../src/adapters/bun.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,KAAK,CAAC,GAAQ,EAAE,OAAwB,EAAE;IACxD,MAAM,GAAG,GAAI,UAAkB,CAAC,GAAG,CAAC;IACpC,IAAI,CAAC,GAAG,EAAE,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;QACpC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI;QAC/D,KAAK,EAAE,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;QACvC,KAAK,EAAE,CAAC,GAAU,EAAE,EAAE,CACpB,IAAI,QAAQ,CACV,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,qCAAqC;YAC3C,KAAK,EAAE,uBAAuB;YAC9B,MAAM,EAAE,GAAG;YACX,MAAM,EAAE,GAAG,CAAC,OAAO;SACpB,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,EAAE,CACzE;KACJ,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"bun.js","sourceRoot":"","sources":["../../src/adapters/bun.ts"],"names":[],"mappings":"AAwCA,MAAM,UAAU,KAAK,CAAC,GAAQ,EAAE,OAAwB,EAAE;IACxD,MAAM,GAAG,GAAI,UAAmI,CAAC,GAAG,CAAC;IACrJ,IAAI,CAAC,GAAG,EAAE,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAE7D,MAAM,GAAG,GAA4B;QACnC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI;QAC/D,KAAK,EAAE,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;QACvC,KAAK,EAAE,CAAC,GAAU,EAAE,EAAE,CACpB,IAAI,QAAQ,CACV,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,qCAAqC;YAC3C,KAAK,EAAE,uBAAuB;YAC9B,MAAM,EAAE,GAAG;YACX,MAAM,EAAE,GAAG,CAAC,OAAO;SACpB,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,EAAE,CACzE;KACJ,CAAC;IACF,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAC7B,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,SAAS,CAAC;IAC5C,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACvE,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACvE,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IAClD,IAAI,IAAI,CAAC,GAAG;QAAE,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IAEjC,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,11 +1,28 @@
1
1
  /**
2
2
  * Cloudflare Workers / generic fetch handler adapter.
3
3
  *
4
- * Usage:
4
+ * Cloudflare Workers expect the module's default export to expose a `fetch`
5
+ * property whose value is the `(request, env, ctx) => Response` function.
6
+ * `toFetchHandler` returns that exact shape, so the recommended usage is:
7
+ *
8
+ * import { toFetchHandler } from "@daloyjs/core/cloudflare";
9
+ * import { app } from "./server.js";
5
10
  * export default toFetchHandler(app);
11
+ *
12
+ * Do NOT wrap the result again (e.g. `export default { fetch: toFetchHandler(app) }`),
13
+ * that nests the object and breaks the Workers runtime.
14
+ *
15
+ * The generic accepts the Worker's `Env` type when you want stronger typing
16
+ * against bindings, e.g. `toFetchHandler<MyEnv>(app)`.
6
17
  */
7
18
  import type { App } from "../app.js";
8
- export declare function toFetchHandler(app: App): {
9
- fetch: (req: Request, env?: unknown, ctx?: unknown) => Promise<Response>;
10
- };
19
+ export interface ExportedFetchHandler<Env = unknown> {
20
+ fetch: (request: Request, env?: Env, ctx?: ExecutionContextLike) => Promise<Response>;
21
+ }
22
+ interface ExecutionContextLike {
23
+ waitUntil?: (promise: Promise<unknown>) => void;
24
+ passThroughOnException?: () => void;
25
+ }
26
+ export declare function toFetchHandler<Env = unknown>(app: App): ExportedFetchHandler<Env>;
27
+ export {};
11
28
  //# sourceMappingURL=cloudflare.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG;IAAE,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAAE,CAIrH"}
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,oBAAoB,CAAC,GAAG,GAAG,OAAO;IACjD,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACvF;AAED,UAAU,oBAAoB;IAC5B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAChD,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,wBAAgB,cAAc,CAAC,GAAG,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAIjF"}
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,cAAc,CAAC,GAAQ;IACrC,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;KAC/B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/adapters/cloudflare.ts"],"names":[],"mappings":"AA4BA,MAAM,UAAU,cAAc,CAAgB,GAAQ;IACpD,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;KAC/B,CAAC;AACJ,CAAC"}
@@ -1,12 +1,35 @@
1
1
  /**
2
2
  * Deno adapter — `Deno.serve` is web-standard fetch.
3
+ *
4
+ * Supports the modern `Deno.serve(options, handler)` signature: HTTPS via
5
+ * `cert`/`key`, an `onListen` callback, an `onError` hook, and signal-based
6
+ * graceful shutdown (also wired to SIGTERM/SIGINT when `handleSignals` is
7
+ * left at the default).
3
8
  */
4
9
  import type { App } from "../app.js";
5
10
  export interface DenoServeOptions {
6
11
  port?: number;
7
12
  hostname?: string;
13
+ /** Optional external signal that triggers graceful shutdown. */
14
+ signal?: AbortSignal;
15
+ /** Optional TLS certificate (PEM). When supplied together with `key`, serves HTTPS. */
16
+ cert?: string;
17
+ /** Optional TLS private key (PEM). Pairs with `cert`. */
18
+ key?: string;
19
+ /** Invoked once the server is listening. */
20
+ onListen?: (info: {
21
+ hostname: string;
22
+ port: number;
23
+ }) => void;
24
+ /** Invoked when the fetch handler itself throws. Must return a fallback Response. */
25
+ onError?: (err: unknown) => Response | Promise<Response>;
26
+ /** Listen for SIGTERM/SIGINT and shut down. Default: true. */
27
+ handleSignals?: boolean;
28
+ /** Drain timeout for graceful shutdown. Default: 10000. */
29
+ shutdownTimeoutMs?: number;
8
30
  }
9
- export declare function serve(app: App, opts?: DenoServeOptions): {
31
+ export interface DenoServerHandle {
10
32
  shutdown: () => Promise<void>;
11
- };
33
+ }
34
+ export declare function serve(app: App, opts?: DenoServeOptions): DenoServerHandle;
12
35
  //# sourceMappingURL=deno.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../../src/adapters/deno.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,gBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAa9F"}
1
+ {"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../../src/adapters/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9D,qFAAqF;IACrF,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzD,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,gBAAqB,GAAG,gBAAgB,CAoD7E"}
@@ -1,13 +1,45 @@
1
1
  export function serve(app, opts = {}) {
2
2
  const D = globalThis.Deno;
3
- if (!D?.serve)
3
+ const denoServe = D?.serve;
4
+ if (!denoServe)
4
5
  throw new Error("Deno runtime not detected");
5
- const server = D.serve({ port: opts.port ?? 3000, hostname: opts.hostname ?? "0.0.0.0" }, (req) => app.fetch(req));
6
- return {
7
- shutdown: async () => {
8
- await app.shutdown();
9
- await server.shutdown?.();
10
- },
6
+ const controller = new AbortController();
7
+ const init = {
8
+ port: opts.port ?? 3000,
9
+ hostname: opts.hostname ?? "0.0.0.0",
10
+ signal: controller.signal,
11
11
  };
12
+ if (opts.cert && opts.key) {
13
+ init.cert = opts.cert;
14
+ init.key = opts.key;
15
+ }
16
+ if (opts.onListen)
17
+ init.onListen = opts.onListen;
18
+ if (opts.onError)
19
+ init.onError = opts.onError;
20
+ const server = denoServe(init, (req) => app.fetch(req));
21
+ const onSignal = () => {
22
+ void shutdown();
23
+ };
24
+ if (opts.handleSignals !== false && typeof D?.addSignalListener === "function") {
25
+ D.addSignalListener("SIGTERM", onSignal);
26
+ D.addSignalListener("SIGINT", onSignal);
27
+ }
28
+ opts.signal?.addEventListener("abort", onSignal, { once: true });
29
+ let stopped = false;
30
+ const shutdown = async () => {
31
+ if (stopped)
32
+ return;
33
+ stopped = true;
34
+ controller.abort();
35
+ opts.signal?.removeEventListener("abort", onSignal);
36
+ if (opts.handleSignals !== false && typeof D?.removeSignalListener === "function") {
37
+ D.removeSignalListener("SIGTERM", onSignal);
38
+ D.removeSignalListener("SIGINT", onSignal);
39
+ }
40
+ await app.shutdown(opts.shutdownTimeoutMs ?? 10_000);
41
+ await server.shutdown?.();
42
+ };
43
+ return { shutdown };
12
44
  }
13
45
  //# sourceMappingURL=deno.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"deno.js","sourceRoot":"","sources":["../../src/adapters/deno.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,KAAK,CAAC,GAAQ,EAAE,OAAyB,EAAE;IACzD,MAAM,CAAC,GAAI,UAAkB,CAAC,IAAI,CAAC;IACnC,IAAI,CAAC,CAAC,EAAE,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CACpB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS,EAAE,EACjE,CAAC,GAAY,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CACjC,CAAC;IACF,OAAO;QACL,QAAQ,EAAE,KAAK,IAAI,EAAE;YACnB,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"deno.js","sourceRoot":"","sources":["../../src/adapters/deno.ts"],"names":[],"mappings":"AAiCA,MAAM,UAAU,KAAK,CAAC,GAAQ,EAAE,OAAyB,EAAE;IACzD,MAAM,CAAC,GAAI,UAMT,CAAC,IAAI,CAAC;IACR,MAAM,SAAS,GAAG,CAAC,EAAE,KAER,CAAC;IACd,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAE7D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,IAAI,GAA4B;QACpC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;QACpC,MAAM,EAAE,UAAU,CAAC,MAAM;KAC1B,CAAC;IACF,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACtB,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ;QAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IACjD,IAAI,IAAI,CAAC,OAAO;QAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAE9C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAExD,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC;IACF,IAAI,IAAI,CAAC,aAAa,KAAK,KAAK,IAAI,OAAO,CAAC,EAAE,iBAAiB,KAAK,UAAU,EAAE,CAAC;QAC/E,CAAC,CAAC,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAEjE,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACpD,IAAI,IAAI,CAAC,aAAa,KAAK,KAAK,IAAI,OAAO,CAAC,EAAE,oBAAoB,KAAK,UAAU,EAAE,CAAC;YAClF,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC5C,CAAC,CAAC,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,IAAI,MAAM,CAAC,CAAC;QACrD,MAAM,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC5B,CAAC,CAAC;IAEF,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Fastly Compute adapter.
3
+ *
4
+ * Fastly Compute uses the standard `fetch` event model, so the adapter is a
5
+ * one-liner wrapper around {@link App.fetch}. Two usage styles are supported:
6
+ *
7
+ * // 1. Event listener style (Fastly Compute @ Edge JS default):
8
+ * import { installFastlyListener } from "@daloyjs/core/fastly";
9
+ * installFastlyListener(app);
10
+ *
11
+ * // 2. Plain function style (composable with other handlers):
12
+ * import { toFastlyHandler } from "@daloyjs/core/fastly";
13
+ * const handler = toFastlyHandler(app);
14
+ * addEventListener("fetch", (event) => event.respondWith(handler(event.request)));
15
+ *
16
+ * Caveats: Fastly Compute does not expose `node:*` modules or full WHATWG
17
+ * streams; avoid Node-only middleware (Node session store, Redis client,
18
+ * multipart helpers that rely on `node:stream`).
19
+ */
20
+ import type { App } from "../app.js";
21
+ export declare function toFastlyHandler(app: App): (req: Request) => Promise<Response>;
22
+ export declare function installFastlyListener(app: App): void;
23
+ //# sourceMappingURL=fastly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastly.d.ts","sourceRoot":"","sources":["../../src/adapters/fastly.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAE7E;AAOD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAMpD"}
@@ -0,0 +1,11 @@
1
+ export function toFastlyHandler(app) {
2
+ return (req) => app.fetch(req);
3
+ }
4
+ export function installFastlyListener(app) {
5
+ const g = globalThis;
6
+ if (typeof g.addEventListener !== "function") {
7
+ throw new Error("Fastly Compute runtime not detected: globalThis.addEventListener is missing");
8
+ }
9
+ g.addEventListener("fetch", (event) => event.respondWith(app.fetch(event.request)));
10
+ }
11
+ //# sourceMappingURL=fastly.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastly.js","sourceRoot":"","sources":["../../src/adapters/fastly.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,eAAe,CAAC,GAAQ;IACtC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AAOD,MAAM,UAAU,qBAAqB,CAAC,GAAQ;IAC5C,MAAM,CAAC,GAAG,UAAwG,CAAC;IACnH,IAAI,OAAO,CAAC,CAAC,gBAAgB,KAAK,UAAU,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IACjG,CAAC;IACD,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC"}
@@ -0,0 +1,67 @@
1
+ import type { App } from "../app.js";
2
+ /**
3
+ * AWS Lambda / Netlify Functions adapter.
4
+ *
5
+ * Supports API Gateway HTTP API (payload format v2.0), Lambda Function URLs,
6
+ * API Gateway REST API (payload format v1.0), and Netlify Functions.
7
+ *
8
+ * // handler.ts (Netlify Functions / Lambda @ Function URL)
9
+ * import { toLambdaHandler } from "@daloyjs/core/lambda";
10
+ * import { app } from "./server.js";
11
+ * export const handler = toLambdaHandler(app);
12
+ *
13
+ * The adapter performs no Node-only I/O, so it is safe in any runtime that
14
+ * provides the standard `Request`/`Response`/`atob`/`btoa` globals.
15
+ */
16
+ export interface LambdaEventV1 {
17
+ version?: "1.0" | string;
18
+ path?: string;
19
+ httpMethod?: string;
20
+ headers?: Record<string, string | undefined>;
21
+ multiValueHeaders?: Record<string, string[] | undefined>;
22
+ queryStringParameters?: Record<string, string | undefined> | null;
23
+ multiValueQueryStringParameters?: Record<string, string[] | undefined> | null;
24
+ requestContext?: {
25
+ domainName?: string;
26
+ path?: string;
27
+ };
28
+ body?: string;
29
+ isBase64Encoded?: boolean;
30
+ }
31
+ export interface LambdaEventV2 {
32
+ version?: string;
33
+ rawPath?: string;
34
+ rawQueryString?: string;
35
+ headers?: Record<string, string | undefined>;
36
+ cookies?: string[];
37
+ requestContext?: {
38
+ http?: {
39
+ method?: string;
40
+ path?: string;
41
+ };
42
+ domainName?: string;
43
+ };
44
+ body?: string;
45
+ isBase64Encoded?: boolean;
46
+ }
47
+ export type LambdaEvent = LambdaEventV1 | LambdaEventV2;
48
+ export interface LambdaResponseV1 {
49
+ statusCode: number;
50
+ headers: Record<string, string>;
51
+ multiValueHeaders?: Record<string, string[]>;
52
+ cookies?: never;
53
+ body: string;
54
+ isBase64Encoded: boolean;
55
+ }
56
+ export interface LambdaResponseV2 {
57
+ statusCode: number;
58
+ headers: Record<string, string>;
59
+ cookies?: string[];
60
+ multiValueHeaders?: never;
61
+ body: string;
62
+ isBase64Encoded: boolean;
63
+ }
64
+ export type LambdaResponse = LambdaResponseV1 | LambdaResponseV2;
65
+ export type LambdaHandler = (event: LambdaEvent) => Promise<LambdaResponse>;
66
+ export declare function toLambdaHandler(app: App): LambdaHandler;
67
+ //# sourceMappingURL=lambda.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambda.d.ts","sourceRoot":"","sources":["../../src/adapters/lambda.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACzD,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IAClE,+BAA+B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;IAC9E,cAAc,CAAC,EAAE;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE;QACf,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,iBAAiB,CAAC,EAAE,KAAK,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAI5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,aAAa,CAMvD"}
@@ -0,0 +1,117 @@
1
+ const TEXT_TYPE_RE = /^(text\/|application\/(json|xml|javascript|x-www-form-urlencoded|.*\+json|.*\+xml))/i;
2
+ export function toLambdaHandler(app) {
3
+ return async (event) => {
4
+ const request = eventToRequest(event);
5
+ const response = await app.fetch(request);
6
+ return responseToLambda(response, isV2Event(event));
7
+ };
8
+ }
9
+ function eventToRequest(event) {
10
+ const headers = new Headers();
11
+ for (const [k, v] of Object.entries(event.headers ?? {})) {
12
+ if (v === undefined)
13
+ continue;
14
+ headers.set(k, v);
15
+ }
16
+ if ("multiValueHeaders" in event) {
17
+ for (const [k, values] of Object.entries(event.multiValueHeaders ?? {})) {
18
+ if (!values?.length)
19
+ continue;
20
+ headers.set(k, k.toLowerCase() === "cookie" ? values.join("; ") : values.join(", "));
21
+ }
22
+ }
23
+ if ("cookies" in event && event.cookies?.length)
24
+ headers.set("cookie", event.cookies.join("; "));
25
+ const method = isV2Event(event) ? event.requestContext?.http?.method ?? "GET" : event.httpMethod ?? "GET";
26
+ const rawPath = isV2Event(event)
27
+ ? event.rawPath ?? event.requestContext?.http?.path ?? "/"
28
+ : event.path ?? event.requestContext?.path ?? "/";
29
+ const host = headers.get("host") ?? event.requestContext?.domainName ?? "localhost";
30
+ const proto = headers.get("x-forwarded-proto") ?? "https";
31
+ const rawQueryString = isV2Event(event) ? event.rawQueryString ?? "" : queryStringForV1(event);
32
+ const qs = rawQueryString ? `?${rawQueryString}` : "";
33
+ const path = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
34
+ const url = `${proto}://${host}${path}${qs}`;
35
+ const init = { method, headers };
36
+ if (method !== "GET" && method !== "HEAD" && event.body != null) {
37
+ init.body = event.isBase64Encoded
38
+ ? base64ToBytes(event.body)
39
+ : event.body;
40
+ }
41
+ return new Request(url, init);
42
+ }
43
+ async function responseToLambda(res, useV2Response) {
44
+ const headers = {};
45
+ const getSetCookie = res.headers.getSetCookie;
46
+ const cookies = typeof getSetCookie === "function"
47
+ ? getSetCookie.call(res.headers)
48
+ : cookieFallback(res.headers);
49
+ res.headers.forEach((value, key) => {
50
+ if (key.toLowerCase() === "set-cookie")
51
+ return;
52
+ headers[key] = value;
53
+ });
54
+ const contentType = res.headers.get("content-type") ?? "";
55
+ const isText = TEXT_TYPE_RE.test(contentType);
56
+ let body = "";
57
+ let isBase64Encoded = false;
58
+ if (res.body) {
59
+ const buf = new Uint8Array(await res.arrayBuffer());
60
+ if (buf.length > 0) {
61
+ if (isText) {
62
+ body = new TextDecoder().decode(buf);
63
+ }
64
+ else {
65
+ body = bytesToBase64(buf);
66
+ isBase64Encoded = true;
67
+ }
68
+ }
69
+ }
70
+ const out = {
71
+ statusCode: res.status,
72
+ headers,
73
+ body,
74
+ isBase64Encoded,
75
+ };
76
+ if (!cookies.length)
77
+ return out;
78
+ if (useV2Response)
79
+ return { ...out, cookies };
80
+ return { ...out, multiValueHeaders: { "set-cookie": cookies } };
81
+ }
82
+ function isV2Event(event) {
83
+ const requestContext = event.requestContext;
84
+ return event.version === "2.0" || "rawPath" in event || "rawQueryString" in event || !!requestContext?.http;
85
+ }
86
+ function queryStringForV1(event) {
87
+ const values = new URLSearchParams();
88
+ for (const [key, list] of Object.entries(event.multiValueQueryStringParameters ?? {})) {
89
+ for (const value of list ?? [])
90
+ values.append(key, value);
91
+ }
92
+ if (values.size > 0)
93
+ return values.toString();
94
+ for (const [key, value] of Object.entries(event.queryStringParameters ?? {})) {
95
+ if (value !== undefined)
96
+ values.append(key, value);
97
+ }
98
+ return values.toString();
99
+ }
100
+ function cookieFallback(headers) {
101
+ const cookie = headers.get("set-cookie");
102
+ return cookie ? [cookie] : [];
103
+ }
104
+ function base64ToBytes(b64) {
105
+ const binary = atob(b64);
106
+ const bytes = new Uint8Array(binary.length);
107
+ for (let i = 0; i < binary.length; i++)
108
+ bytes[i] = binary.charCodeAt(i);
109
+ return bytes;
110
+ }
111
+ function bytesToBase64(bytes) {
112
+ let binary = "";
113
+ for (let i = 0; i < bytes.length; i++)
114
+ binary += String.fromCharCode(bytes[i]);
115
+ return btoa(binary);
116
+ }
117
+ //# sourceMappingURL=lambda.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../src/adapters/lambda.ts"],"names":[],"mappings":"AAsEA,MAAM,YAAY,GAAG,sFAAsF,CAAC;AAE5G,MAAM,UAAU,eAAe,CAAC,GAAQ;IACtC,OAAO,KAAK,EAAE,KAAK,EAAE,EAAE;QACrB,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,OAAO,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAkB;IACxC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC,KAAK,SAAS;YAAE,SAAS;QAC9B,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,mBAAmB,IAAI,KAAK,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE,CAAC;YACxE,IAAI,CAAC,MAAM,EAAE,MAAM;gBAAE,SAAS;YAC9B,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IACD,IAAI,SAAS,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,MAAM;QAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjG,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC;IAC1G,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC;QAC9B,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,IAAI,GAAG;QAC1D,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,cAAc,EAAE,IAAI,IAAI,GAAG,CAAC;IACpD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,cAAc,EAAE,UAAU,IAAI,WAAW,CAAC;IACpF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,OAAO,CAAC;IAC1D,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC/F,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;IAC/D,MAAM,GAAG,GAAG,GAAG,KAAK,MAAM,IAAI,GAAG,IAAI,GAAG,EAAE,EAAE,CAAC;IAE7C,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC9C,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,eAAe;YAC/B,CAAC,CAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAc;YACzC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,GAAa,EAAE,aAAsB;IACnE,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAI,GAAG,CAAC,OAAuD,CAAC,YAAY,CAAC;IAC/F,MAAM,OAAO,GAAa,OAAO,YAAY,KAAK,UAAU;QAC1D,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;QAChC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACjC,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,YAAY;YAAE,OAAO;QAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE9C,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACpD,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvC,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;gBAC1B,eAAe,GAAG,IAAI,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG;QACV,UAAU,EAAE,GAAG,CAAC,MAAM;QACtB,OAAO;QACP,IAAI;QACJ,eAAe;KAChB,CAAC;IACF,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC;IAChC,IAAI,aAAa;QAAE,OAAO,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC;IAC9C,OAAO,EAAE,GAAG,GAAG,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,SAAS,CAAC,KAAkB;IACnC,MAAM,cAAc,GAAG,KAAK,CAAC,cAAgD,CAAC;IAC9E,OAAO,KAAK,CAAC,OAAO,KAAK,KAAK,IAAI,SAAS,IAAI,KAAK,IAAI,gBAAgB,IAAI,KAAK,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC;AAC9G,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAoB;IAC5C,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,+BAA+B,IAAI,EAAE,CAAC,EAAE,CAAC;QACtF,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,EAAE;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,EAAE,CAAC;QAC7E,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,cAAc,CAAC,OAAgB;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,KAAiB;IACtC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;IAChF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}