@daloyjs/core 0.7.5 → 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 +21 -10
  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.
@@ -292,14 +301,16 @@ DaloyJS is in **public preview** (`0.x`). The public API may still change betwee
292
301
  What works today, at a glance:
293
302
 
294
303
  - Contract-first routing, Standard Schema validation (Zod 4 / Valibot / ArkType / TypeBox), and OpenAPI 3.1 from a single source of truth.
295
- - Adapters for Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.
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.
296
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`).
297
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`.
298
307
  - In-process test client (`app.request()`), contract-test runner, in-process typed client, and Hey API codegen via `pnpm gen`.
299
- - `pnpm create daloy` scaffolder with Node, Bun, Deno, Cloudflare Worker, and Vercel Edge templates.
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.
300
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.
301
312
 
302
- Roadmap, version-by-version plan, and shipped/in-progress checklists live in [ROADMAP.md](./ROADMAP.md). Release history and rationale lives in [PROJECT_HISTORY.md](./PROJECT_HISTORY.md).
313
+ Roadmap, version-by-version plan, and shipped/in-progress checklists live in [ROADMAP.md](./ROADMAP.md).
303
314
 
304
315
  ## License
305
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":"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"}
@@ -15,6 +15,13 @@ export interface NodeServerOptions {
15
15
  handleSignals?: boolean;
16
16
  /** Maximum HTTP header size bytes (DoS protection). Default: 16 KiB. */
17
17
  maxHeaderBytes?: number;
18
+ /**
19
+ * When true, honor `x-forwarded-proto` and `x-forwarded-host` headers when
20
+ * constructing the request URL. Enable this only when running behind a
21
+ * trusted reverse proxy (e.g. a TLS-terminating load balancer); otherwise
22
+ * clients can spoof the scheme/host. Default: false.
23
+ */
24
+ trustProxy?: boolean;
18
25
  }
19
26
  export interface NodeServerHandle {
20
27
  server: Server;
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/adapters/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA2D,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEjG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CAmD9E"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/adapters/node.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAA2D,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEjG,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CAoD9E"}
@@ -5,9 +5,10 @@
5
5
  import { createServer } from "node:http";
6
6
  import { Readable } from "node:stream";
7
7
  export function serve(app, opts = {}) {
8
+ const trustProxy = opts.trustProxy === true;
8
9
  const server = createServer({ maxHeaderSize: opts.maxHeaderBytes ?? 16 * 1024 }, async (req, res) => {
9
10
  try {
10
- const request = await toWebRequest(req);
11
+ const request = await toWebRequest(req, trustProxy);
11
12
  const response = await app.fetch(request);
12
13
  await sendWebResponse(response, res);
13
14
  }
@@ -50,9 +51,12 @@ export function serve(app, opts = {}) {
50
51
  }
51
52
  return { server, port, close };
52
53
  }
53
- async function toWebRequest(req) {
54
- const host = req.headers.host ?? "localhost";
55
- const url = `http://${host}${req.url ?? "/"}`;
54
+ async function toWebRequest(req, trustProxy) {
55
+ const forwardedHost = trustProxy ? firstHeader(req.headers["x-forwarded-host"]) : undefined;
56
+ const host = forwardedHost ?? req.headers.host ?? "localhost";
57
+ const forwardedProto = trustProxy ? firstHeader(req.headers["x-forwarded-proto"]) : undefined;
58
+ const proto = forwardedProto ?? (req.socket.encrypted ? "https" : "http");
59
+ const url = `${proto}://${host}${req.url ?? "/"}`;
56
60
  const headers = new Headers();
57
61
  for (const [k, v] of Object.entries(req.headers)) {
58
62
  if (v === undefined)
@@ -70,6 +74,15 @@ async function toWebRequest(req) {
70
74
  }
71
75
  return new Request(url, init);
72
76
  }
77
+ function firstHeader(v) {
78
+ if (v === undefined)
79
+ return undefined;
80
+ const raw = Array.isArray(v) ? v[0] : v;
81
+ if (raw === undefined)
82
+ return undefined;
83
+ const comma = raw.indexOf(",");
84
+ return (comma === -1 ? raw : raw.slice(0, comma)).trim() || undefined;
85
+ }
73
86
  async function sendWebResponse(res, out) {
74
87
  out.statusCode = res.status;
75
88
  res.headers.forEach((v, k) => out.setHeader(k, v));