@daloyjs/core 0.6.0 โ 0.7.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 +44 -32
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/session.d.ts +151 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +448 -0
- package/dist/session.js.map +1 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# DaloyJS
|
|
2
2
|
|
|
3
|
-
> A **runtime-portable TypeScript web framework** with built-in **contract-first routing**, **validation**, **OpenAPI (Hey API)**, **typed client generation**, **large-scale maintainability**, and **
|
|
3
|
+
> A **runtime-portable TypeScript web framework** with built-in **contract-first routing**, **validation**, **OpenAPI (Hey API)**, **typed client generation**, **large-scale maintainability**, and **security-focused runtime plus supply-chain posture**.
|
|
4
4
|
|
|
5
5
|
DaloyJS is maintained in the GitHub organization at <https://github.com/daloyjs>; the canonical framework repository is <https://github.com/daloyjs/daloy>.
|
|
6
6
|
|
|
7
|
-
๐ **Documentation site:**
|
|
7
|
+
๐ **Documentation site:** <https://daloyjs.dev> โ a Next.js 16 + shadcn/ui + Tailwind v4 site with the landing page, getting-started guide, ORM integration guides, tutorials, security docs, and full API reference. Run it with:
|
|
8
8
|
|
|
9
9
|
```zsh
|
|
10
10
|
cd website
|
|
@@ -27,7 +27,7 @@ DaloyJS exists to be the framework you'd build if you took the best ideas from e
|
|
|
27
27
|
| **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. |
|
|
28
28
|
|
|
29
29
|
```
|
|
30
|
-
|
|
30
|
+
320/320 framework tests passing ยท 100% line + function coverage ยท clean strict TypeScript 6
|
|
31
31
|
runs on Node, Bun, Deno, Cloudflare, Vercel
|
|
32
32
|
~12.3M static-route ops/sec ยท ~1.5M dynamic-route ops/sec on M-class CPU
|
|
33
33
|
```
|
|
@@ -50,13 +50,27 @@ DaloyJS combines the wins:
|
|
|
50
50
|
1. **Explicit contracts, minimal ceremony.** One `app.route({...})` is the source of truth for validation, types, OpenAPI, the typed client, and contract tests.
|
|
51
51
|
2. **One source of truth for validation, typing, and docs** via [Standard Schema](https://github.com/standard-schema/standard-schema) โ Zod 4 / Valibot / ArkType / TypeBox all work, no lock-in.
|
|
52
52
|
3. **Portable core, optional runtime optimizations** โ the only thing the core knows is `Request โ Response`. Adapters live at the edge.
|
|
53
|
-
4. **
|
|
53
|
+
4. **Security guardrails by default โ bad defaults are bugs.** The core enforces body limits, prototype-pollution-safe JSON, path-traversal rejection, request timeouts, content-type checks, and RFC 9457 problem+json errors with prod-mode redaction. First-party middleware covers Helmet-grade headers, CORS, CSRF, rate limits, request ids, and signed-cookie sessions.
|
|
54
54
|
5. **Tooling and inspectability over magic.** `app.introspect()` is a public API; contract-test runner is built in.
|
|
55
55
|
6. **Optimize for large-team maintenance**, not only solo-dev speed. Encapsulated plugins, decorators, request ids, structured logger.
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
59
|
-
##
|
|
59
|
+
## Get started
|
|
60
|
+
|
|
61
|
+
For a new DaloyJS project, the recommended path is the official scaffolder:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pnpm create daloy@latest my-api
|
|
65
|
+
# or
|
|
66
|
+
npm create daloy@latest my-api
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`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.
|
|
70
|
+
|
|
71
|
+
See [Scaffold a project](https://daloyjs.dev/docs/scaffolder) for templates and flags.
|
|
72
|
+
|
|
73
|
+
## Install core manually
|
|
60
74
|
|
|
61
75
|
DaloyJS is distributed via **pnpm** for [supply-chain hygiene](https://pnpm.io/motivation) and backed by a hardened release pipeline โ strict isolation, content-addressable store, deterministic lockfile, no phantom dependencies, SHA-pinned CI actions, and provenance publishing.
|
|
62
76
|
|
|
@@ -83,16 +97,6 @@ Run `pnpm audit --prod` regularly (or `pnpm run audit` in this repo) โ and `pn
|
|
|
83
97
|
|
|
84
98
|
---
|
|
85
99
|
|
|
86
|
-
## Quick start
|
|
87
|
-
|
|
88
|
-
```bash
|
|
89
|
-
pnpm create daloy@latest my-api
|
|
90
|
-
# or
|
|
91
|
-
npm create daloy@latest my-api
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
See [Scaffold a project](https://daloyjs.dev/docs/scaffolder) for templates and flags.
|
|
95
|
-
|
|
96
100
|
## Hello world
|
|
97
101
|
|
|
98
102
|
```ts
|
|
@@ -102,7 +106,7 @@ import { serve } from "@daloyjs/core/node";
|
|
|
102
106
|
|
|
103
107
|
const app = new App({ bodyLimitBytes: 1024 * 1024, requestTimeoutMs: 5_000 });
|
|
104
108
|
|
|
105
|
-
//
|
|
109
|
+
// First-party security middleware โ usually three plugins in other frameworks.
|
|
106
110
|
app.use(requestId());
|
|
107
111
|
app.use(secureHeaders());
|
|
108
112
|
app.use(rateLimit({ windowMs: 60_000, max: 120 }));
|
|
@@ -177,29 +181,34 @@ import { swaggerUiHtml, htmlResponse } from "@daloyjs/core/docs";
|
|
|
177
181
|
```
|
|
178
182
|
|
|
179
183
|
Mount at `/docs` and the UI is always contract-accurate โ never stale.
|
|
180
|
-
`create-daloy@0.1.
|
|
184
|
+
`create-daloy@0.1.20` mounts Swagger UI at `/docs` and the live spec at `/openapi.json` by default.
|
|
181
185
|
|
|
182
186
|
---
|
|
183
187
|
|
|
184
|
-
## Security
|
|
188
|
+
## Security guardrails
|
|
189
|
+
|
|
190
|
+
Some protections are enforced by the `App` core whenever the relevant request
|
|
191
|
+
path is used. Others are first-party middleware so applications can choose the
|
|
192
|
+
right CORS policy, rate-limit key, CSP, session secret, or CSRF rollout for their
|
|
193
|
+
deployment.
|
|
185
194
|
|
|
186
|
-
| Threat |
|
|
195
|
+
| Threat | Built-in behavior |
|
|
187
196
|
|---|---|
|
|
188
|
-
| **Body-size DoS** |
|
|
189
|
-
| **Prototype pollution** |
|
|
190
|
-
| **Header / response splitting** |
|
|
191
|
-
| **Path traversal** |
|
|
192
|
-
| **Slow-loris / hung handlers** | `requestTimeoutMs` aborts handlers (default 30 s); Node adapter sets `requestTimeout` + `headersTimeout` + `maxHeaderSize`. |
|
|
193
|
-
| **MIME sniffing** | `secureHeaders()` sets `X-Content-Type-Options: nosniff
|
|
194
|
-
| **Clickjacking** | `X-Frame-Options: DENY` + CSP `frame-ancestors 'none'
|
|
195
|
-
| **XSS via injected scripts** |
|
|
196
|
-
| **Cross-origin leakage** | `cross-origin-opener-policy` + `cross-origin-resource-policy`
|
|
197
|
+
| **Body-size DoS** | Core-enforced streamed read with a hard cap (default 1 MiB); `Content-Length` checked first. |
|
|
198
|
+
| **Prototype pollution** | Core JSON parser strips `__proto__` / `constructor` / `prototype` via reviver. |
|
|
199
|
+
| **Header / response splitting** | Core header sanitizers reject CRLF + NUL. |
|
|
200
|
+
| **Path traversal** | Core router rejects `..` segments and `//` before walking. |
|
|
201
|
+
| **Slow-loris / hung handlers** | Core `requestTimeoutMs` aborts handlers (default 30 s); Node adapter sets `requestTimeout` + `headersTimeout` + `maxHeaderSize`. |
|
|
202
|
+
| **MIME sniffing** | First-party `secureHeaders()` sets `X-Content-Type-Options: nosniff`; scaffolded apps enable it. |
|
|
203
|
+
| **Clickjacking** | First-party `secureHeaders()` sets `X-Frame-Options: DENY` + CSP `frame-ancestors 'none'`; scaffolded apps enable it. |
|
|
204
|
+
| **XSS via injected scripts** | First-party `secureHeaders()` provides a strict CSP `default-src 'self'` baseline; scaffolded apps enable it. |
|
|
205
|
+
| **Cross-origin leakage** | First-party `secureHeaders()` sets `cross-origin-opener-policy` + `cross-origin-resource-policy` to `same-origin`; scaffolded apps enable it. |
|
|
197
206
|
| **Information disclosure (5xx)** | Production mode strips `detail` from 5xx problem+json automatically. |
|
|
198
|
-
| **Credential timing attacks** | `timingSafeEqual()` for tokens & signatures. |
|
|
199
|
-
| **Brute-force / scraping** | `rateLimit()` with token-bucket + `Retry-After
|
|
207
|
+
| **Credential timing attacks** | First-party `timingSafeEqual()` helper for tokens & signatures. |
|
|
208
|
+
| **Brute-force / scraping** | First-party `rateLimit()` with token-bucket + `Retry-After`; Node/Bun/Deno scaffolded apps enable it. |
|
|
200
209
|
| **Method confusion** | Real **405** with `Allow` header, not a misleading 404. |
|
|
201
|
-
| **CORS misconfig** |
|
|
202
|
-
| **Request correlation** |
|
|
210
|
+
| **CORS misconfig** | First-party `cors()` requires an explicit allowlist and throws for `*` with credentials. |
|
|
211
|
+
| **Request correlation** | First-party `requestId()` uses cryptographic ids; scaffolded apps enable it. |
|
|
203
212
|
| **Supply chain** | pnpm `ignore-scripts=true`, `minimum-release-age=1440`, verified store, reproducible lockfile, lockfile source verification, provenance publishing, and CI/CD hardening against cache poisoning and OIDC token abuse. |
|
|
204
213
|
|
|
205
214
|
The publish pipeline is also hardened: no `pull_request_target`, no GitHub Actions cache in CI, top-level `permissions: {}`, `step-security/harden-runner`, a separate protected `release.yml` workflow, npm trusted publishing with `--provenance`, CodeQL, OpenSSF Scorecard, zizmor workflow linting, Dependabot, and CODEOWNERS on workflow/package files. See [SECURITY.md](SECURITY.md) and the [supply-chain security docs](https://daloyjs.dev/docs/security/supply-chain).
|
|
@@ -325,6 +334,7 @@ WebSockets and HTTP/2 + HTTP/3 adapters.
|
|
|
325
334
|
**Shipped from the `0.x` extensibility track so far:**
|
|
326
335
|
|
|
327
336
|
- [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.
|
|
337
|
+
- [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.
|
|
328
338
|
|
|
329
339
|
**Shipped from `0.5.x` ("project ops") so far:**
|
|
330
340
|
|
|
@@ -332,6 +342,8 @@ WebSockets and HTTP/2 + HTTP/3 adapters.
|
|
|
332
342
|
- [x] `--minimal` flag that strips the bookstore demo and `/docs` + `/openapi.json` routes from any template
|
|
333
343
|
- [x] `daloy inspect` CLI: route table, schema summary, contract-test gate, OpenAPI dump, tag/method filters
|
|
334
344
|
- [x] Redis-backed `RateLimitStore` at `@daloyjs/core/rate-limit-redis` with `ioredisAdapter` / `nodeRedisAdapter` and a fail-open default for shared counters across replicas
|
|
345
|
+
- [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
|
|
346
|
+
- [x] Polished `create-daloy` terminal UX: DaloyJS welcome banner, arrow-key pickers, install spinner, and boxed next steps while preserving zero runtime dependencies
|
|
335
347
|
|
|
336
348
|
## License
|
|
337
349
|
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export { httpBearerScheme, httpBasicScheme, apiKeyScheme, oauth2Scheme, openIdCo
|
|
|
16
16
|
export type { ApiKeyLocation, ApiKeyScheme, ApiKeySchemeOptions, HttpBasicScheme, HttpBasicSchemeOptions, HttpBearerScheme, HttpBearerSchemeOptions, OAuth2AuthorizationCodeFlow, OAuth2ClientCredentialsFlow, OAuth2Flows, OAuth2ImplicitFlow, OAuth2PasswordFlow, OAuth2Scheme, OAuth2SchemeOptions, OpenIdConnectScheme, OpenIdConnectSchemeOptions, SecurityScheme, } from "./security-schemes.js";
|
|
17
17
|
export { discriminator, discriminatedUnion } from "./discriminator.js";
|
|
18
18
|
export type { DiscriminatorObject, DiscriminatedUnion, DiscriminatedUnionOptions, } from "./discriminator.js";
|
|
19
|
+
export { session, signValue, verifySignedValue, MemorySessionStore, } from "./session.js";
|
|
20
|
+
export type { SessionOptions, SessionCookieOptions, SessionContext, SessionRecord, SessionStore, SessionState, } from "./session.js";
|
|
19
21
|
export { fileField, multipartObject, isFileFieldSchema, isMultipartObjectSchema, } from "./multipart.js";
|
|
20
22
|
export type { FileFieldSchema, FileFieldOptions, MultipartObjectOptions, MultipartShape, UploadedFile, } from "./multipart.js";
|
|
21
23
|
export { otelTracing, TRACING_SPAN_KIND_SERVER, TRACING_SPAN_STATUS_UNSET, TRACING_SPAN_STATUS_OK, TRACING_SPAN_STATUS_ERROR, } from "./tracing.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { createLogger, noopLogger } from "./logger.js";
|
|
|
7
7
|
export { sseStream, sseResponse, ndjsonStream, ndjsonResponse, } from "./streaming.js";
|
|
8
8
|
export { httpBearerScheme, httpBasicScheme, apiKeyScheme, oauth2Scheme, openIdConnectScheme, } from "./security-schemes.js";
|
|
9
9
|
export { discriminator, discriminatedUnion } from "./discriminator.js";
|
|
10
|
+
export { session, signValue, verifySignedValue, MemorySessionStore, } from "./session.js";
|
|
10
11
|
export { fileField, multipartObject, isFileFieldSchema, isMultipartObjectSchema, } from "./multipart.js";
|
|
11
12
|
export { otelTracing, TRACING_SPAN_KIND_SERVER, TRACING_SPAN_STATUS_UNSET, TRACING_SPAN_STATUS_OK, TRACING_SPAN_STATUS_ERROR, } from "./tracing.js";
|
|
12
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAuB/B,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,IAAI,GACL,MAAM,iBAAiB,CAAC;AAWzB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EACL,SAAS,EACT,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAqB/B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAuB/B,OAAO,EACL,SAAS,EACT,eAAe,EACf,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EACL,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,QAAQ,GACT,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,aAAa,EACb,IAAI,EACJ,SAAS,EACT,MAAM,EACN,UAAU,EACV,IAAI,GACL,MAAM,iBAAiB,CAAC;AAWzB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EACL,SAAS,EACT,WAAW,EACX,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAqB/B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAOvE,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAUtB,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AASxB,OAAO,EACL,WAAW,EACX,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge-friendly session primitive.
|
|
3
|
+
*
|
|
4
|
+
* Implements signed session cookies plus a pluggable session store. The cookie
|
|
5
|
+
* format is `${sid}.${signature}` where `signature` is a URL-safe base64
|
|
6
|
+
* HMAC-SHA256 of the session id. Multiple secrets can be passed to support
|
|
7
|
+
* graceful key rotation: the first secret is always used for signing, while
|
|
8
|
+
* any of the configured secrets accept incoming cookies. Every signature
|
|
9
|
+
* comparison is timing-safe.
|
|
10
|
+
*
|
|
11
|
+
* The default {@link MemorySessionStore} is suitable for tests and single-
|
|
12
|
+
* process Node processes. Production deployments should pass a `SessionStore`
|
|
13
|
+
* backed by Redis, Cloudflare KV, Vercel KV, or any other shared store.
|
|
14
|
+
*
|
|
15
|
+
* The module is dependency-free and uses Web Crypto (`crypto.subtle`), so it
|
|
16
|
+
* works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.
|
|
17
|
+
*/
|
|
18
|
+
import type { Hooks } from "./types.js";
|
|
19
|
+
export interface SessionRecord {
|
|
20
|
+
/** Arbitrary serializable session payload. Mutating this object marks the session dirty. */
|
|
21
|
+
data: Record<string, unknown>;
|
|
22
|
+
/** Absolute expiration as ms since epoch. */
|
|
23
|
+
expiresAt: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pluggable persistence backend for sessions. All methods may be sync or async.
|
|
27
|
+
* Implementations should treat `expiresAt` in the past as "missing" and may
|
|
28
|
+
* lazily delete expired records.
|
|
29
|
+
*/
|
|
30
|
+
export interface SessionStore {
|
|
31
|
+
get(sid: string): SessionRecord | null | Promise<SessionRecord | null>;
|
|
32
|
+
set(sid: string, record: SessionRecord): void | Promise<void>;
|
|
33
|
+
destroy(sid: string): void | Promise<void>;
|
|
34
|
+
/** Optional fast-path for rolling sessions; falls back to `set()` if omitted. */
|
|
35
|
+
touch?(sid: string, expiresAt: number): void | Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export interface SessionCookieOptions {
|
|
38
|
+
/** `Strict` | `Lax` | `None`. Default: `"Lax"`. */
|
|
39
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
40
|
+
/** Default: `true`. Required when `sameSite` is `"None"` or with `__Host-` cookie names. */
|
|
41
|
+
secure?: boolean;
|
|
42
|
+
/** Default: `"/"`. */
|
|
43
|
+
path?: string;
|
|
44
|
+
/** Optional `Domain=` attribute. Cannot be combined with a `__Host-` cookie name. */
|
|
45
|
+
domain?: string;
|
|
46
|
+
/** Optional `Max-Age=` (seconds). When omitted the cookie is a session cookie. */
|
|
47
|
+
maxAgeSeconds?: number;
|
|
48
|
+
/** Emit `Partitioned` (CHIPS) for cross-site contexts. Default: `false`. */
|
|
49
|
+
partitioned?: boolean;
|
|
50
|
+
/** Default: `true`. Sessions are server-side state, never readable by JS. */
|
|
51
|
+
httpOnly?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface SessionOptions {
|
|
54
|
+
/**
|
|
55
|
+
* HMAC secret(s) used to sign session cookies. Pass an array to rotate
|
|
56
|
+
* secrets without invalidating existing sessions: the first entry is used to
|
|
57
|
+
* sign new cookies; any entry verifies incoming cookies. Each secret must be
|
|
58
|
+
* a non-empty string of at least 16 characters.
|
|
59
|
+
*/
|
|
60
|
+
secret: string | string[];
|
|
61
|
+
/** Cookie name carrying the session id. Default: `"__Host-daloy.sid"`. */
|
|
62
|
+
cookieName?: string;
|
|
63
|
+
/** Cookie attributes (see {@link SessionCookieOptions}). */
|
|
64
|
+
cookieOptions?: SessionCookieOptions;
|
|
65
|
+
/** Pluggable persistence backend. Default: a fresh in-memory store. */
|
|
66
|
+
store?: SessionStore;
|
|
67
|
+
/** Default session lifetime in seconds. Default: `86400` (1 day). */
|
|
68
|
+
ttlSeconds?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Reset the session expiration on every access. Default: `true`.
|
|
71
|
+
* Disable for fixed-duration sessions.
|
|
72
|
+
*/
|
|
73
|
+
rolling?: boolean;
|
|
74
|
+
/** Override the random session-id generator (32 bytes URL-safe by default). */
|
|
75
|
+
generator?: () => string;
|
|
76
|
+
/**
|
|
77
|
+
* Persist a brand-new session and set its cookie even when the handler did
|
|
78
|
+
* not touch it. Default: `false` (GDPR-friendly: no cookie until consent or
|
|
79
|
+
* first write). Set to `true` to issue a session id on every first request.
|
|
80
|
+
*/
|
|
81
|
+
saveUninitialized?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** Per-request session API exposed on `ctx.state.session`. */
|
|
84
|
+
export type SessionContext = {
|
|
85
|
+
/** Current session id. Refreshed by `regenerate()`. */
|
|
86
|
+
readonly id: string;
|
|
87
|
+
/** Session payload. Mutating this object marks the session dirty. */
|
|
88
|
+
readonly data: Record<string, unknown>;
|
|
89
|
+
/** Read a single payload key. */
|
|
90
|
+
get<T = unknown>(key: string): T | undefined;
|
|
91
|
+
/** Set a payload key. Marks the session dirty. */
|
|
92
|
+
set(key: string, value: unknown): void;
|
|
93
|
+
/** Delete a payload key. Marks the session dirty. */
|
|
94
|
+
delete(key: string): void;
|
|
95
|
+
/** Drop all server-side state and clear the cookie on the response. */
|
|
96
|
+
destroy(): void;
|
|
97
|
+
/** Issue a new session id (defense against fixation). Carries `data` over by default; pass `{ keepData: false }` to start fresh. */
|
|
98
|
+
regenerate(opts?: {
|
|
99
|
+
keepData?: boolean;
|
|
100
|
+
}): Promise<string>;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* In-memory `SessionStore`. Suitable for tests and single-process deployments.
|
|
104
|
+
* Expired records are dropped on access.
|
|
105
|
+
*/
|
|
106
|
+
export declare class MemorySessionStore implements SessionStore {
|
|
107
|
+
private readonly map;
|
|
108
|
+
get(sid: string): SessionRecord | null;
|
|
109
|
+
set(sid: string, record: SessionRecord): void;
|
|
110
|
+
destroy(sid: string): void;
|
|
111
|
+
touch(sid: string, expiresAt: number): void;
|
|
112
|
+
/** Test helper. Remove every record. */
|
|
113
|
+
clear(): void;
|
|
114
|
+
/** Test helper. Number of stored records (including expired). */
|
|
115
|
+
size(): number;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Session middleware. Reads (and verifies) a signed session cookie on every
|
|
119
|
+
* request, exposes a typed `ctx.state.session` API, and writes back any
|
|
120
|
+
* mutations on the response.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { App, session } from "@daloyjs/core";
|
|
125
|
+
*
|
|
126
|
+
* declare module "@daloyjs/core" {
|
|
127
|
+
* interface AppState {
|
|
128
|
+
* session: import("@daloyjs/core").SessionContext;
|
|
129
|
+
* }
|
|
130
|
+
* }
|
|
131
|
+
*
|
|
132
|
+
* const app = new App();
|
|
133
|
+
* app.use(session({ secret: process.env.SESSION_SECRET! }));
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function session(opts: SessionOptions): Hooks;
|
|
137
|
+
/**
|
|
138
|
+
* Sign an arbitrary string with HMAC-SHA256. Returns `${value}.${sig}` where
|
|
139
|
+
* `sig` is URL-safe base64. Useful for building custom signed cookies or
|
|
140
|
+
* tokens that do not need a session store.
|
|
141
|
+
*/
|
|
142
|
+
export declare function signValue(value: string, secret: string): Promise<string>;
|
|
143
|
+
/**
|
|
144
|
+
* Verify a `signValue()`-produced string. Returns the original value when the
|
|
145
|
+
* signature checks out, otherwise `null`. Constant-time on the signature.
|
|
146
|
+
*/
|
|
147
|
+
export declare function verifySignedValue(signed: string, secret: string | string[]): Promise<string | null>;
|
|
148
|
+
export type SessionState = {
|
|
149
|
+
session: SessionContext;
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge-friendly session primitive.
|
|
3
|
+
*
|
|
4
|
+
* Implements signed session cookies plus a pluggable session store. The cookie
|
|
5
|
+
* format is `${sid}.${signature}` where `signature` is a URL-safe base64
|
|
6
|
+
* HMAC-SHA256 of the session id. Multiple secrets can be passed to support
|
|
7
|
+
* graceful key rotation: the first secret is always used for signing, while
|
|
8
|
+
* any of the configured secrets accept incoming cookies. Every signature
|
|
9
|
+
* comparison is timing-safe.
|
|
10
|
+
*
|
|
11
|
+
* The default {@link MemorySessionStore} is suitable for tests and single-
|
|
12
|
+
* process Node processes. Production deployments should pass a `SessionStore`
|
|
13
|
+
* backed by Redis, Cloudflare KV, Vercel KV, or any other shared store.
|
|
14
|
+
*
|
|
15
|
+
* The module is dependency-free and uses Web Crypto (`crypto.subtle`), so it
|
|
16
|
+
* works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.
|
|
17
|
+
*/
|
|
18
|
+
import { timingSafeEqual } from "./security.js";
|
|
19
|
+
const DEFAULT_COOKIE_NAME = "__Host-daloy.sid";
|
|
20
|
+
const STATE_KEY = "session";
|
|
21
|
+
const STATE_INTERNAL = "__sessionInternal";
|
|
22
|
+
// ---------- Implementation ----------
|
|
23
|
+
const COOKIE_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
24
|
+
const enc = new TextEncoder();
|
|
25
|
+
function getSubtle() {
|
|
26
|
+
const c = globalThis.crypto;
|
|
27
|
+
if (!c?.subtle) {
|
|
28
|
+
throw new Error("session(): Web Crypto (crypto.subtle) is required. Provide a polyfill in environments without it.");
|
|
29
|
+
}
|
|
30
|
+
return c.subtle;
|
|
31
|
+
}
|
|
32
|
+
function bytesToBase64Url(bytes) {
|
|
33
|
+
let bin = "";
|
|
34
|
+
for (let i = 0; i < bytes.length; i++)
|
|
35
|
+
bin += String.fromCharCode(bytes[i]);
|
|
36
|
+
// btoa is defined on every supported runtime.
|
|
37
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
38
|
+
}
|
|
39
|
+
function makeSigner(secret) {
|
|
40
|
+
if (typeof secret !== "string" || secret.length < 16) {
|
|
41
|
+
throw new Error("session(): each secret must be a string of at least 16 characters.");
|
|
42
|
+
}
|
|
43
|
+
let keyPromise = null;
|
|
44
|
+
const getKey = () => {
|
|
45
|
+
if (!keyPromise) {
|
|
46
|
+
keyPromise = getSubtle().importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
47
|
+
}
|
|
48
|
+
return keyPromise;
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
async sign(value) {
|
|
52
|
+
const key = await getKey();
|
|
53
|
+
const sig = new Uint8Array(await getSubtle().sign("HMAC", key, enc.encode(value)));
|
|
54
|
+
return bytesToBase64Url(sig);
|
|
55
|
+
},
|
|
56
|
+
async verify(value, signature) {
|
|
57
|
+
const expected = await this.sign(value);
|
|
58
|
+
return timingSafeEqual(expected, signature);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function generateSessionId() {
|
|
63
|
+
const c = globalThis.crypto;
|
|
64
|
+
if (!c?.getRandomValues) {
|
|
65
|
+
throw new Error("session(): Web Crypto getRandomValues is required for the default id generator.");
|
|
66
|
+
}
|
|
67
|
+
const buf = new Uint8Array(32);
|
|
68
|
+
c.getRandomValues(buf);
|
|
69
|
+
return bytesToBase64Url(buf);
|
|
70
|
+
}
|
|
71
|
+
function validateCookieSegment(kind, value) {
|
|
72
|
+
if (/[;\r\n\0]/.test(value))
|
|
73
|
+
throw new Error(`session(): cookieOptions.${kind} contains an invalid character.`);
|
|
74
|
+
}
|
|
75
|
+
function validateCookieOptions(cookieName, opts) {
|
|
76
|
+
if (!COOKIE_NAME_RE.test(cookieName))
|
|
77
|
+
throw new Error("session(): cookieName is not a valid cookie name.");
|
|
78
|
+
if (opts.sameSite !== "Strict" && opts.sameSite !== "Lax" && opts.sameSite !== "None") {
|
|
79
|
+
throw new Error('session(): cookieOptions.sameSite must be "Strict", "Lax", or "None".');
|
|
80
|
+
}
|
|
81
|
+
if (!opts.path.startsWith("/"))
|
|
82
|
+
throw new Error('session(): cookieOptions.path must start with "/".');
|
|
83
|
+
validateCookieSegment("path", opts.path);
|
|
84
|
+
if (opts.domain)
|
|
85
|
+
validateCookieSegment("domain", opts.domain);
|
|
86
|
+
if (!Number.isInteger(opts.maxAgeSeconds) || opts.maxAgeSeconds < 0) {
|
|
87
|
+
throw new Error("session(): cookieOptions.maxAgeSeconds must be a non-negative integer.");
|
|
88
|
+
}
|
|
89
|
+
if (cookieName.startsWith("__Host-")) {
|
|
90
|
+
if (!opts.secure || opts.path !== "/" || opts.domain) {
|
|
91
|
+
throw new Error('session(): "__Host-" cookie names require secure: true, path: "/", and no domain. ' +
|
|
92
|
+
"Pass an explicit cookieName or relax cookieOptions to use a non-prefixed cookie.");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (opts.sameSite === "None" && !opts.secure) {
|
|
96
|
+
throw new Error('session(): cookieOptions.sameSite: "None" requires secure: true.');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function readCookie(header, name) {
|
|
100
|
+
if (!header)
|
|
101
|
+
return null;
|
|
102
|
+
const parts = header.split(";");
|
|
103
|
+
for (let i = 0; i < parts.length; i++) {
|
|
104
|
+
const part = parts[i];
|
|
105
|
+
const eq = part.indexOf("=");
|
|
106
|
+
if (eq < 0)
|
|
107
|
+
continue;
|
|
108
|
+
const k = part.slice(0, eq).trim();
|
|
109
|
+
if (k === name) {
|
|
110
|
+
const v = part.slice(eq + 1).trim();
|
|
111
|
+
try {
|
|
112
|
+
return decodeURIComponent(v);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return v;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function buildSetCookie(name, value, opts) {
|
|
122
|
+
let s = `${name}=${encodeURIComponent(value)}`;
|
|
123
|
+
s += `; Path=${opts.path}`;
|
|
124
|
+
s += `; SameSite=${opts.sameSite}`;
|
|
125
|
+
if (opts.secure)
|
|
126
|
+
s += "; Secure";
|
|
127
|
+
if (opts.httpOnly)
|
|
128
|
+
s += "; HttpOnly";
|
|
129
|
+
if (opts.domain)
|
|
130
|
+
s += `; Domain=${opts.domain}`;
|
|
131
|
+
if (opts.maxAgeSeconds > 0)
|
|
132
|
+
s += `; Max-Age=${opts.maxAgeSeconds}`;
|
|
133
|
+
if (opts.partitioned)
|
|
134
|
+
s += "; Partitioned";
|
|
135
|
+
return s;
|
|
136
|
+
}
|
|
137
|
+
function buildClearCookie(name, opts) {
|
|
138
|
+
let s = `${name}=`;
|
|
139
|
+
s += `; Path=${opts.path}`;
|
|
140
|
+
s += `; SameSite=${opts.sameSite}`;
|
|
141
|
+
if (opts.secure)
|
|
142
|
+
s += "; Secure";
|
|
143
|
+
if (opts.httpOnly)
|
|
144
|
+
s += "; HttpOnly";
|
|
145
|
+
if (opts.domain)
|
|
146
|
+
s += `; Domain=${opts.domain}`;
|
|
147
|
+
s += "; Max-Age=0";
|
|
148
|
+
if (opts.partitioned)
|
|
149
|
+
s += "; Partitioned";
|
|
150
|
+
return s;
|
|
151
|
+
}
|
|
152
|
+
function markDirty(internal) {
|
|
153
|
+
internal.dirty = true;
|
|
154
|
+
}
|
|
155
|
+
function makeSessionContext(id, data, internal, regenerate) {
|
|
156
|
+
const proxy = new Proxy(data, {
|
|
157
|
+
set(target, key, value) {
|
|
158
|
+
target[key] = value;
|
|
159
|
+
markDirty(internal);
|
|
160
|
+
return true;
|
|
161
|
+
},
|
|
162
|
+
deleteProperty(target, key) {
|
|
163
|
+
const had = key in target;
|
|
164
|
+
delete target[key];
|
|
165
|
+
if (had)
|
|
166
|
+
markDirty(internal);
|
|
167
|
+
return true;
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
let currentId = id;
|
|
171
|
+
return {
|
|
172
|
+
get id() {
|
|
173
|
+
return currentId;
|
|
174
|
+
},
|
|
175
|
+
get data() {
|
|
176
|
+
return proxy;
|
|
177
|
+
},
|
|
178
|
+
get(key) {
|
|
179
|
+
return data[key];
|
|
180
|
+
},
|
|
181
|
+
set(key, value) {
|
|
182
|
+
data[key] = value;
|
|
183
|
+
markDirty(internal);
|
|
184
|
+
},
|
|
185
|
+
delete(key) {
|
|
186
|
+
if (key in data) {
|
|
187
|
+
delete data[key];
|
|
188
|
+
markDirty(internal);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
destroy() {
|
|
192
|
+
for (const k of Object.keys(data))
|
|
193
|
+
delete data[k];
|
|
194
|
+
internal.destroyed = true;
|
|
195
|
+
internal.dirty = true;
|
|
196
|
+
},
|
|
197
|
+
async regenerate(opts) {
|
|
198
|
+
const keepData = opts?.keepData !== false;
|
|
199
|
+
const next = await regenerate(keepData);
|
|
200
|
+
currentId = next;
|
|
201
|
+
return next;
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// ---------- Stores ----------
|
|
206
|
+
/**
|
|
207
|
+
* In-memory `SessionStore`. Suitable for tests and single-process deployments.
|
|
208
|
+
* Expired records are dropped on access.
|
|
209
|
+
*/
|
|
210
|
+
export class MemorySessionStore {
|
|
211
|
+
map = new Map();
|
|
212
|
+
get(sid) {
|
|
213
|
+
const rec = this.map.get(sid);
|
|
214
|
+
if (!rec)
|
|
215
|
+
return null;
|
|
216
|
+
if (rec.expiresAt <= Date.now()) {
|
|
217
|
+
this.map.delete(sid);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
return rec;
|
|
221
|
+
}
|
|
222
|
+
set(sid, record) {
|
|
223
|
+
this.map.set(sid, record);
|
|
224
|
+
}
|
|
225
|
+
destroy(sid) {
|
|
226
|
+
this.map.delete(sid);
|
|
227
|
+
}
|
|
228
|
+
touch(sid, expiresAt) {
|
|
229
|
+
const rec = this.map.get(sid);
|
|
230
|
+
if (rec)
|
|
231
|
+
rec.expiresAt = expiresAt;
|
|
232
|
+
}
|
|
233
|
+
/** Test helper. Remove every record. */
|
|
234
|
+
clear() {
|
|
235
|
+
this.map.clear();
|
|
236
|
+
}
|
|
237
|
+
/** Test helper. Number of stored records (including expired). */
|
|
238
|
+
size() {
|
|
239
|
+
return this.map.size;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// ---------- Middleware ----------
|
|
243
|
+
/**
|
|
244
|
+
* Session middleware. Reads (and verifies) a signed session cookie on every
|
|
245
|
+
* request, exposes a typed `ctx.state.session` API, and writes back any
|
|
246
|
+
* mutations on the response.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* import { App, session } from "@daloyjs/core";
|
|
251
|
+
*
|
|
252
|
+
* declare module "@daloyjs/core" {
|
|
253
|
+
* interface AppState {
|
|
254
|
+
* session: import("@daloyjs/core").SessionContext;
|
|
255
|
+
* }
|
|
256
|
+
* }
|
|
257
|
+
*
|
|
258
|
+
* const app = new App();
|
|
259
|
+
* app.use(session({ secret: process.env.SESSION_SECRET! }));
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export function session(opts) {
|
|
263
|
+
const cookieName = opts.cookieName ?? DEFAULT_COOKIE_NAME;
|
|
264
|
+
const secrets = Array.isArray(opts.secret) ? opts.secret : [opts.secret];
|
|
265
|
+
if (secrets.length === 0)
|
|
266
|
+
throw new Error("session(): at least one secret is required.");
|
|
267
|
+
const signers = secrets.map(makeSigner);
|
|
268
|
+
const cookieOverrides = opts.cookieOptions ?? {};
|
|
269
|
+
const cookieOpts = {
|
|
270
|
+
sameSite: cookieOverrides.sameSite ?? "Lax",
|
|
271
|
+
secure: cookieOverrides.secure ?? true,
|
|
272
|
+
path: cookieOverrides.path ?? "/",
|
|
273
|
+
domain: cookieOverrides.domain ?? "",
|
|
274
|
+
maxAgeSeconds: cookieOverrides.maxAgeSeconds ?? 0,
|
|
275
|
+
partitioned: cookieOverrides.partitioned ?? false,
|
|
276
|
+
httpOnly: cookieOverrides.httpOnly ?? true,
|
|
277
|
+
};
|
|
278
|
+
validateCookieOptions(cookieName, cookieOpts);
|
|
279
|
+
const ttlSeconds = opts.ttlSeconds ?? 86_400;
|
|
280
|
+
if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {
|
|
281
|
+
throw new Error("session(): ttlSeconds must be a positive integer.");
|
|
282
|
+
}
|
|
283
|
+
const ttlMs = ttlSeconds * 1000;
|
|
284
|
+
const rolling = opts.rolling !== false;
|
|
285
|
+
const store = opts.store ?? new MemorySessionStore();
|
|
286
|
+
const generator = opts.generator ?? generateSessionId;
|
|
287
|
+
const saveUninitialized = opts.saveUninitialized === true;
|
|
288
|
+
return {
|
|
289
|
+
async beforeHandle(ctx) {
|
|
290
|
+
const internal = {
|
|
291
|
+
cookieName,
|
|
292
|
+
cookieOpts,
|
|
293
|
+
store,
|
|
294
|
+
signers,
|
|
295
|
+
ttlMs,
|
|
296
|
+
rolling,
|
|
297
|
+
generator,
|
|
298
|
+
saveUninitialized,
|
|
299
|
+
originalId: null,
|
|
300
|
+
hadCookie: false,
|
|
301
|
+
activeId: null,
|
|
302
|
+
dirty: false,
|
|
303
|
+
destroyed: false,
|
|
304
|
+
regenerated: false,
|
|
305
|
+
created: false,
|
|
306
|
+
};
|
|
307
|
+
const raw = readCookie(ctx.request.headers.get("cookie"), cookieName);
|
|
308
|
+
internal.hadCookie = raw !== null;
|
|
309
|
+
let data = {};
|
|
310
|
+
let id = null;
|
|
311
|
+
if (raw) {
|
|
312
|
+
const dot = raw.lastIndexOf(".");
|
|
313
|
+
if (dot > 0 && dot < raw.length - 1) {
|
|
314
|
+
const candidateId = raw.slice(0, dot);
|
|
315
|
+
const sig = raw.slice(dot + 1);
|
|
316
|
+
for (const signer of signers) {
|
|
317
|
+
// eslint-disable-next-line no-await-in-loop
|
|
318
|
+
if (await signer.verify(candidateId, sig)) {
|
|
319
|
+
const rec = await store.get(candidateId);
|
|
320
|
+
if (rec && rec.expiresAt > Date.now()) {
|
|
321
|
+
id = candidateId;
|
|
322
|
+
data = rec.data ? { ...rec.data } : {};
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (!id) {
|
|
330
|
+
id = generator();
|
|
331
|
+
if (!id)
|
|
332
|
+
throw new Error("session(): generator returned an empty id.");
|
|
333
|
+
internal.created = true;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
internal.originalId = id;
|
|
337
|
+
}
|
|
338
|
+
internal.activeId = id;
|
|
339
|
+
const regenerate = async (keepData) => {
|
|
340
|
+
if (internal.activeId && internal.originalId === internal.activeId) {
|
|
341
|
+
await store.destroy(internal.activeId);
|
|
342
|
+
}
|
|
343
|
+
else if (internal.activeId && internal.activeId !== internal.originalId) {
|
|
344
|
+
// We rotated mid-request previously; throw away the unsaved id.
|
|
345
|
+
}
|
|
346
|
+
const next = generator();
|
|
347
|
+
if (!next)
|
|
348
|
+
throw new Error("session(): generator returned an empty id.");
|
|
349
|
+
if (!keepData) {
|
|
350
|
+
for (const k of Object.keys(data))
|
|
351
|
+
delete data[k];
|
|
352
|
+
}
|
|
353
|
+
internal.activeId = next;
|
|
354
|
+
internal.regenerated = true;
|
|
355
|
+
internal.dirty = true;
|
|
356
|
+
internal.destroyed = false;
|
|
357
|
+
internal.created = false;
|
|
358
|
+
internal.originalId = null;
|
|
359
|
+
return next;
|
|
360
|
+
};
|
|
361
|
+
const sessionCtx = makeSessionContext(id, data, internal, regenerate);
|
|
362
|
+
const state = ctx.state;
|
|
363
|
+
state[STATE_KEY] = sessionCtx;
|
|
364
|
+
state[STATE_INTERNAL] = internal;
|
|
365
|
+
},
|
|
366
|
+
async onSend(res, ctx) {
|
|
367
|
+
if (!ctx)
|
|
368
|
+
return undefined;
|
|
369
|
+
const state = ctx.state;
|
|
370
|
+
const internal = state[STATE_INTERNAL];
|
|
371
|
+
if (!internal)
|
|
372
|
+
return undefined;
|
|
373
|
+
if (internal.destroyed) {
|
|
374
|
+
if (internal.originalId)
|
|
375
|
+
await store.destroy(internal.originalId);
|
|
376
|
+
// Clear stale or malformed client cookies too, not only verified sessions.
|
|
377
|
+
if (internal.hadCookie) {
|
|
378
|
+
res.headers.append("set-cookie", buildClearCookie(internal.cookieName, internal.cookieOpts));
|
|
379
|
+
}
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
const sessionCtx = state[STATE_KEY];
|
|
383
|
+
const data = sessionCtx.data;
|
|
384
|
+
const sid = internal.activeId;
|
|
385
|
+
const expiresAt = Date.now() + internal.ttlMs;
|
|
386
|
+
// A brand-new, untouched session is a no-op unless saveUninitialized is on.
|
|
387
|
+
const initialized = internal.dirty || internal.regenerated || internal.originalId !== null;
|
|
388
|
+
if (!initialized && !internal.saveUninitialized)
|
|
389
|
+
return undefined;
|
|
390
|
+
const mustPersist = internal.dirty || internal.created || internal.regenerated;
|
|
391
|
+
if (mustPersist) {
|
|
392
|
+
await store.set(sid, { data: { ...data }, expiresAt });
|
|
393
|
+
}
|
|
394
|
+
else if (internal.rolling) {
|
|
395
|
+
if (store.touch)
|
|
396
|
+
await store.touch(sid, expiresAt);
|
|
397
|
+
else
|
|
398
|
+
await store.set(sid, { data: { ...data }, expiresAt });
|
|
399
|
+
}
|
|
400
|
+
if (internal.regenerated && internal.originalId && internal.originalId !== sid) {
|
|
401
|
+
await store.destroy(internal.originalId);
|
|
402
|
+
}
|
|
403
|
+
// Only refresh the cookie when something actually changed, the session
|
|
404
|
+
// was just created, or rolling sessions need a sliding expiration.
|
|
405
|
+
const mustWriteCookie = internal.created || internal.regenerated || internal.rolling;
|
|
406
|
+
if (!mustWriteCookie)
|
|
407
|
+
return undefined;
|
|
408
|
+
const signer = internal.signers[0];
|
|
409
|
+
const sig = await signer.sign(sid);
|
|
410
|
+
res.headers.append("set-cookie", buildSetCookie(internal.cookieName, `${sid}.${sig}`, internal.cookieOpts));
|
|
411
|
+
return undefined;
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
// ---------- Low-level signing helpers (re-exported for advanced use) ----------
|
|
416
|
+
/**
|
|
417
|
+
* Sign an arbitrary string with HMAC-SHA256. Returns `${value}.${sig}` where
|
|
418
|
+
* `sig` is URL-safe base64. Useful for building custom signed cookies or
|
|
419
|
+
* tokens that do not need a session store.
|
|
420
|
+
*/
|
|
421
|
+
export async function signValue(value, secret) {
|
|
422
|
+
if (value.includes(".")) {
|
|
423
|
+
throw new Error("signValue(): value must not contain '.'");
|
|
424
|
+
}
|
|
425
|
+
const signer = makeSigner(secret);
|
|
426
|
+
const sig = await signer.sign(value);
|
|
427
|
+
return `${value}.${sig}`;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Verify a `signValue()`-produced string. Returns the original value when the
|
|
431
|
+
* signature checks out, otherwise `null`. Constant-time on the signature.
|
|
432
|
+
*/
|
|
433
|
+
export async function verifySignedValue(signed, secret) {
|
|
434
|
+
const dot = signed.lastIndexOf(".");
|
|
435
|
+
if (dot <= 0 || dot >= signed.length - 1)
|
|
436
|
+
return null;
|
|
437
|
+
const value = signed.slice(0, dot);
|
|
438
|
+
const sig = signed.slice(dot + 1);
|
|
439
|
+
const secrets = Array.isArray(secret) ? secret : [secret];
|
|
440
|
+
for (const s of secrets) {
|
|
441
|
+
const signer = makeSigner(s);
|
|
442
|
+
// eslint-disable-next-line no-await-in-loop
|
|
443
|
+
if (await signer.verify(value, sig))
|
|
444
|
+
return value;
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daloyjs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "DaloyJS is a runtime-portable, contract-first TypeScript web framework with built-in OpenAPI (Hey API), typed client generation, large-scale maintainability, and security-first defaults. Hono-grade portability, Elysia-grade DX, FastAPI-grade docs, Fastify-grade ops โ distributed via pnpm.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/daloyjs/daloy.git"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://
|
|
13
|
+
"homepage": "https://daloyjs.dev",
|
|
14
14
|
"bugs": {
|
|
15
15
|
"url": "https://github.com/daloyjs/daloy/issues"
|
|
16
16
|
},
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
"./cli": {
|
|
85
85
|
"types": "./dist/cli.d.ts",
|
|
86
86
|
"import": "./dist/cli.js"
|
|
87
|
+
},
|
|
88
|
+
"./session": {
|
|
89
|
+
"types": "./dist/session.d.ts",
|
|
90
|
+
"import": "./dist/session.js"
|
|
87
91
|
}
|
|
88
92
|
},
|
|
89
93
|
"files": [
|