@daloyjs/core 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- 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 +5 -1
package/README.md
CHANGED
|
@@ -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
|
```
|
|
@@ -325,6 +325,7 @@ WebSockets and HTTP/2 + HTTP/3 adapters.
|
|
|
325
325
|
**Shipped from the `0.x` extensibility track so far:**
|
|
326
326
|
|
|
327
327
|
- [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.
|
|
328
|
+
- [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
329
|
|
|
329
330
|
**Shipped from `0.5.x` ("project ops") so far:**
|
|
330
331
|
|
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.0",
|
|
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": {
|
|
@@ -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": [
|