@daloyjs/core 1.0.0-beta.6 → 1.0.0-rc.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.
Files changed (99) hide show
  1. package/README.md +2 -2
  2. package/dist/adapters/bun.d.ts +13 -1
  3. package/dist/adapters/bun.js +8 -1
  4. package/dist/adapters/cloudflare.d.ts +7 -1
  5. package/dist/adapters/cloudflare.js +6 -1
  6. package/dist/adapters/deno.d.ts +11 -1
  7. package/dist/adapters/deno.js +8 -1
  8. package/dist/adapters/fastly.d.ts +12 -2
  9. package/dist/adapters/fastly.js +12 -2
  10. package/dist/adapters/lambda.d.ts +37 -1
  11. package/dist/adapters/lambda.js +6 -1
  12. package/dist/adapters/node.d.ts +12 -1
  13. package/dist/adapters/node.js +223 -12
  14. package/dist/adapters/vercel.d.ts +13 -1
  15. package/dist/adapters/vercel.js +12 -1
  16. package/dist/app.d.ts +78 -17
  17. package/dist/app.js +444 -86
  18. package/dist/banner.d.ts +6 -0
  19. package/dist/banner.js +6 -0
  20. package/dist/cli.d.ts +35 -0
  21. package/dist/cli.js +23 -1
  22. package/dist/combine.d.ts +8 -0
  23. package/dist/combine.js +8 -0
  24. package/dist/compression.d.ts +3 -0
  25. package/dist/compression.js +3 -0
  26. package/dist/config.d.ts +4 -0
  27. package/dist/config.js +4 -0
  28. package/dist/conn-info.d.ts +35 -2
  29. package/dist/conn-info.js +35 -2
  30. package/dist/contract.d.ts +2 -0
  31. package/dist/contract.js +2 -0
  32. package/dist/cookie.d.ts +12 -0
  33. package/dist/cookie.js +12 -0
  34. package/dist/dependency.d.ts +4 -0
  35. package/dist/dependency.js +3 -0
  36. package/dist/discriminator.d.ts +13 -0
  37. package/dist/discriminator.js +23 -1
  38. package/dist/docs.d.ts +78 -0
  39. package/dist/docs.js +21 -0
  40. package/dist/errors.d.ts +16 -0
  41. package/dist/errors.js +14 -0
  42. package/dist/etag.d.ts +2 -0
  43. package/dist/etag.js +2 -0
  44. package/dist/fetch-guard.d.ts +7 -0
  45. package/dist/fetch-guard.js +7 -0
  46. package/dist/fetch-resilience.d.ts +4 -0
  47. package/dist/fetch-resilience.js +4 -0
  48. package/dist/http-signatures.d.ts +32 -0
  49. package/dist/http-signatures.js +30 -0
  50. package/dist/index.d.ts +1 -1
  51. package/dist/ip-restriction.d.ts +40 -3
  52. package/dist/ip-restriction.js +35 -3
  53. package/dist/jwk.d.ts +12 -1
  54. package/dist/jwk.js +6 -0
  55. package/dist/jwt.d.ts +14 -0
  56. package/dist/jwt.js +10 -0
  57. package/dist/load-shedding.d.ts +3 -0
  58. package/dist/load-shedding.js +3 -0
  59. package/dist/logger.d.ts +13 -0
  60. package/dist/logger.js +3 -0
  61. package/dist/mcp.d.ts +152 -10
  62. package/dist/mcp.js +223 -19
  63. package/dist/middleware.d.ts +68 -0
  64. package/dist/middleware.js +17 -0
  65. package/dist/mtls.d.ts +19 -2
  66. package/dist/mtls.js +12 -2
  67. package/dist/multipart.d.ts +42 -5
  68. package/dist/multipart.js +41 -5
  69. package/dist/openapi.d.ts +15 -9
  70. package/dist/openapi.js +6 -9
  71. package/dist/rate-limit-redis.d.ts +21 -2
  72. package/dist/rate-limit-redis.js +17 -2
  73. package/dist/safe-redirect.d.ts +6 -0
  74. package/dist/safe-redirect.js +6 -0
  75. package/dist/sbom.cdx.json +9 -9
  76. package/dist/sbom.spdx.json +5 -5
  77. package/dist/scheduler.d.ts +4 -0
  78. package/dist/schema.d.ts +25 -0
  79. package/dist/security-schemes.d.ts +50 -0
  80. package/dist/security-schemes.js +6 -0
  81. package/dist/security.d.ts +33 -0
  82. package/dist/security.js +28 -10
  83. package/dist/session.d.ts +34 -5
  84. package/dist/session.js +31 -5
  85. package/dist/streaming.d.ts +19 -0
  86. package/dist/streaming.js +16 -0
  87. package/dist/subdomains.d.ts +4 -0
  88. package/dist/subdomains.js +4 -0
  89. package/dist/time-claims.d.ts +22 -2
  90. package/dist/time-claims.js +6 -0
  91. package/dist/tracing.d.ts +12 -0
  92. package/dist/tracing.js +6 -0
  93. package/dist/types.d.ts +70 -1
  94. package/dist/waf.js +21 -1
  95. package/dist/webhook-delivery.d.ts +6 -0
  96. package/dist/webhook-delivery.js +5 -0
  97. package/dist/websocket.d.ts +137 -4
  98. package/dist/websocket.js +105 -4
  99. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ export { validate, isStandardSchema } from "./schema.js";
20
20
  export { diffOpenAPI, hasBreakingChanges } from "./openapi-diff.js";
21
21
  export type { ChangeSeverity, OpenAPIChange, OpenAPIDiffResult } from "./openapi-diff.js";
22
22
  export { MCP_DEFAULT_MAX_BODY_BYTES, MCP_PROTOCOL_VERSION, MCP_PROTOCOL_VERSIONS, McpToolError, createMcpHandler, mcpRoutes, } from "./mcp.js";
23
- export type { McpContent, McpEmbeddedResourceContent, McpHandler, McpHandlerOptions, McpImageContent, McpJsonObject, McpJsonRpcId, McpJsonSchema, McpJsonValue, McpPrompt, McpPromptArgument, McpPromptDefinition, McpPromptMessage, McpPromptResult, McpRequestContext, McpResource, McpResourceContents, McpResourceDefinition, McpServerInfo, McpTextContent, McpTool, McpToolHandler, McpToolResult, } from "./mcp.js";
23
+ export type { McpContent, McpEmbeddedResourceContent, McpHandler, McpHandlerOptions, McpIcon, McpImageContent, McpJsonObject, McpJsonRpcId, McpJsonSchema, McpJsonValue, McpPrompt, McpPromptArgument, McpPromptDefinition, McpPromptMessage, McpPromptResult, McpRequestContext, McpResource, McpResourceContents, McpResourceDefinition, McpResourceTemplate, McpResourceTemplateDefinition, McpServerInfo, McpTextContent, McpTool, McpToolAnnotations, McpToolHandler, McpToolResult, } from "./mcp.js";
24
24
  export { readBodyLimited, safeJsonParse, isForbiddenObjectKey, sanitizeHeaderName, sanitizeHeaderValue, timingSafeEqual, randomId, assertNoDuplicateSingletonHeaders, assertNoReservedInternalHeaders, assertHeaderCountWithinLimit, DEFAULT_MAX_HEADER_COUNT, RESERVED_INBOUND_HEADER_PREFIXES, SMUGGLING_SINGLETON_HEADERS, verifyWebhookSignature, signWebhookPayload, WEBHOOK_DEFAULT_TOLERANCE_SECONDS, assertStrongSecret, MIN_PROD_SECRET_BYTES, WEAK_SECRET_STRINGS, sanitizeFilename, assertSafeRelativePath, hasMongoOperatorKeys, assertNoMongoOperators, } from "./security.js";
25
25
  export type { WebhookHmacAlgorithm } from "./security.js";
26
26
  export { requestId, secureHeaders, SECURE_HEADERS_MARKER, cors, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, rateLimit, loginThrottle, timing, bearerAuth, basicAuth, csrf, CSRF_HOOK_MARKER, fetchMetadata, requireScopes, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, _resetSharedRateLimitStoresForTests, } from "./middleware.js";
@@ -53,13 +53,18 @@ export interface IpRestrictionOptions {
53
53
  }
54
54
  /** @internal Parsed IP address (shared with `fetchGuard()`). */
55
55
  export interface ParsedIp {
56
+ /** Big-endian address bytes: 4 bytes for IPv4, 16 for IPv6. */
56
57
  bytes: Uint8Array;
58
+ /** Address family: `4` for IPv4, `6` for IPv6. */
57
59
  family: 4 | 6;
58
60
  }
59
61
  /** @internal Compiled CIDR matcher (shared with `fetchGuard()`). */
60
62
  export interface IpMatcher {
63
+ /** Address family the matcher applies to: `4` or `6`. */
61
64
  family: 4 | 6;
65
+ /** CIDR prefix length in bits (0-32 for IPv4, 0-128 for IPv6). */
62
66
  prefix: number;
67
+ /** Network address bytes with all host bits masked to zero. */
63
68
  bytes: Uint8Array;
64
69
  }
65
70
  /**
@@ -80,12 +85,44 @@ export interface IpMatcher {
80
85
  * On reject the middleware throws a {@link ForbiddenError}, which Daloy
81
86
  * renders as RFC 9457 `application/problem+json`.
82
87
  *
88
+ * @param opts Allow/deny lists plus IP-resolution options; see
89
+ * {@link IpRestrictionOptions}. Deny matches always win over allow.
90
+ * @returns A {@link Hooks} object whose `beforeHandle` enforces the lists,
91
+ * failing closed (403) when the client IP cannot be resolved or parsed.
92
+ * @throws Error at setup time when neither `allow` nor `deny` is provided,
93
+ * or when a pattern is not a valid IP/CIDR.
83
94
  * @since 0.19.0
84
95
  */
85
96
  export declare function ipRestriction(opts: IpRestrictionOptions): Hooks;
86
- /** @internal */
97
+ /**
98
+ * Test whether a parsed IP falls inside a compiled CIDR matcher, comparing
99
+ * only the matcher's prefix bits. IPv4-mapped IPv6 addresses
100
+ * (`::ffff:a.b.c.d`) are normalized so they match IPv4 matchers.
101
+ *
102
+ * @param ip Parsed client address from {@link parseIp}.
103
+ * @param m Compiled matcher from {@link compileCidrMatcher}.
104
+ * @returns `true` when the address is within the matcher's range.
105
+ * @internal
106
+ */
87
107
  export declare function matchesMatcher(ip: ParsedIp, m: IpMatcher): boolean;
88
- /** @internal */
108
+ /**
109
+ * Compile an IP or CIDR pattern (e.g. `"10.0.0.0/8"`, `"::1"`) into an
110
+ * {@link IpMatcher}. A bare address gets a full-length prefix (/32 or /128);
111
+ * host bits beyond the prefix are masked to zero.
112
+ *
113
+ * @param input IPv4/IPv6 address, optionally with a `/prefix` suffix.
114
+ * @returns The compiled matcher used by {@link matchesMatcher}.
115
+ * @throws Error when the address or CIDR prefix is invalid.
116
+ * @internal
117
+ */
89
118
  export declare function compileCidrMatcher(input: string): IpMatcher;
90
- /** @internal */
119
+ /**
120
+ * Parse an IPv4 or IPv6 address string into raw bytes. Supports IPv6 `::`
121
+ * compression and IPv4-mapped tails (`::ffff:1.2.3.4`).
122
+ *
123
+ * @param input Address string; surrounding whitespace is trimmed.
124
+ * @returns The parsed address, or `undefined` when the input is not a valid
125
+ * IP (callers treat unparseable addresses as a rejection, failing closed).
126
+ * @internal
127
+ */
91
128
  export declare function parseIp(input: string): ParsedIp | undefined;
@@ -26,6 +26,12 @@ import { ForbiddenError } from "./errors.js";
26
26
  * On reject the middleware throws a {@link ForbiddenError}, which Daloy
27
27
  * renders as RFC 9457 `application/problem+json`.
28
28
  *
29
+ * @param opts Allow/deny lists plus IP-resolution options; see
30
+ * {@link IpRestrictionOptions}. Deny matches always win over allow.
31
+ * @returns A {@link Hooks} object whose `beforeHandle` enforces the lists,
32
+ * failing closed (403) when the client IP cannot be resolved or parsed.
33
+ * @throws Error at setup time when neither `allow` nor `deny` is provided,
34
+ * or when a pattern is not a valid IP/CIDR.
29
35
  * @since 0.19.0
30
36
  */
31
37
  export function ipRestriction(opts) {
@@ -64,7 +70,16 @@ function forwardedIpResolver(ctx) {
64
70
  return forwarded.split(",")[0]?.trim();
65
71
  return headers.get("x-real-ip") ?? undefined;
66
72
  }
67
- /** @internal */
73
+ /**
74
+ * Test whether a parsed IP falls inside a compiled CIDR matcher, comparing
75
+ * only the matcher's prefix bits. IPv4-mapped IPv6 addresses
76
+ * (`::ffff:a.b.c.d`) are normalized so they match IPv4 matchers.
77
+ *
78
+ * @param ip Parsed client address from {@link parseIp}.
79
+ * @param m Compiled matcher from {@link compileCidrMatcher}.
80
+ * @returns `true` when the address is within the matcher's range.
81
+ * @internal
82
+ */
68
83
  export function matchesMatcher(ip, m) {
69
84
  const candidate = normalizeFamily(ip, m.family);
70
85
  if (!candidate)
@@ -83,7 +98,16 @@ export function matchesMatcher(ip, m) {
83
98
  const mask = 0xff << (8 - remaining);
84
99
  return ((candidate[fullBytes] ^ expected[fullBytes]) & mask) === 0;
85
100
  }
86
- /** @internal */
101
+ /**
102
+ * Compile an IP or CIDR pattern (e.g. `"10.0.0.0/8"`, `"::1"`) into an
103
+ * {@link IpMatcher}. A bare address gets a full-length prefix (/32 or /128);
104
+ * host bits beyond the prefix are masked to zero.
105
+ *
106
+ * @param input IPv4/IPv6 address, optionally with a `/prefix` suffix.
107
+ * @returns The compiled matcher used by {@link matchesMatcher}.
108
+ * @throws Error when the address or CIDR prefix is invalid.
109
+ * @internal
110
+ */
87
111
  export function compileCidrMatcher(input) {
88
112
  let addr = input;
89
113
  let prefixStr;
@@ -135,7 +159,15 @@ function applyPrefixMask(bytes, prefix) {
135
159
  }
136
160
  return out;
137
161
  }
138
- /** @internal */
162
+ /**
163
+ * Parse an IPv4 or IPv6 address string into raw bytes. Supports IPv6 `::`
164
+ * compression and IPv4-mapped tails (`::ffff:1.2.3.4`).
165
+ *
166
+ * @param input Address string; surrounding whitespace is trimmed.
167
+ * @returns The parsed address, or `undefined` when the input is not a valid
168
+ * IP (callers treat unparseable addresses as a rejection, failing closed).
169
+ * @internal
170
+ */
139
171
  export function parseIp(input) {
140
172
  const trimmed = input.trim();
141
173
  if (trimmed.includes(":"))
package/dist/jwk.d.ts CHANGED
@@ -4,6 +4,7 @@ import type { BaseContext, Hooks } from "./types.js";
4
4
  export type JwkAlgorithm = Exclude<JwtAlgorithm, "HS256" | "HS384" | "HS512">;
5
5
  /** Minimal JWKS document shape (RFC 7517 §5). */
6
6
  export interface JwkSet {
7
+ /** Public JWKs; each entry should carry a `kid` so tokens can select their key. */
7
8
  keys: JsonWebKey[];
8
9
  }
9
10
  /**
@@ -13,7 +14,11 @@ export interface JwkSet {
13
14
  export type JwkSource = JwkSet | string | (() => JwkSet | Promise<JwkSet>);
14
15
  /** Per-request payload-revalidation hook. */
15
16
  export type JwkVerifyHook = (payload: Record<string, unknown>, ctx: BaseContext<any, any>) => boolean | void | Promise<boolean | void>;
16
- /** Options for {@link jwk}. */
17
+ /**
18
+ * Options for {@link jwk}: the JWKS source and asymmetric algorithm allowlist
19
+ * are required; issuer / audience / clock-skew checks, JWKS fetch caching,
20
+ * and a per-request revalidation hook are opt-in.
21
+ */
17
22
  export interface JwkOptions {
18
23
  /** JWKS source (object, URL, or resolver). */
19
24
  jwks: JwkSource;
@@ -91,6 +96,12 @@ export interface JwkOptions {
91
96
  * });
92
97
  * ```
93
98
  *
99
+ * @param opts - JWKS source, algorithm allowlist, and claim checks; see
100
+ * {@link JwkOptions}.
101
+ * @returns A {@link Hooks} object to pass to `app.use()` or a route's `hooks`;
102
+ * failed auth yields a `401` problem+json with `WWW-Authenticate`.
103
+ * @throws {Error} at construction for missing options, an empty or
104
+ * symmetric-containing allowlist, invalid TTLs, or a malformed realm.
94
105
  * @since 0.22.0
95
106
  */
96
107
  export declare function jwk(opts: JwkOptions): Hooks;
package/dist/jwk.js CHANGED
@@ -161,6 +161,12 @@ function makeJwksLoader(source, fetchImpl, ttlSeconds, maxStaleSeconds) {
161
161
  * });
162
162
  * ```
163
163
  *
164
+ * @param opts - JWKS source, algorithm allowlist, and claim checks; see
165
+ * {@link JwkOptions}.
166
+ * @returns A {@link Hooks} object to pass to `app.use()` or a route's `hooks`;
167
+ * failed auth yields a `401` problem+json with `WWW-Authenticate`.
168
+ * @throws {Error} at construction for missing options, an empty or
169
+ * symmetric-containing allowlist, invalid TTLs, or a malformed realm.
164
170
  * @since 0.22.0
165
171
  */
166
172
  export function jwk(opts) {
package/dist/jwt.d.ts CHANGED
@@ -23,19 +23,24 @@ export type JwtAlgorithm = "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS
23
23
  export declare const DEFAULT_JWT_MAX_LIFETIME_SECONDS: number;
24
24
  /** Structured error thrown by every JWT helper. */
25
25
  export declare class JwtError extends Error {
26
+ /** Stable machine-readable error code (e.g. `"alg_none_refused"`, `"weak_hs_secret"`). */
26
27
  readonly code: string;
27
28
  constructor(code: string, message: string);
28
29
  }
29
30
  /** Result of a successful verify. */
30
31
  export interface JwtVerified {
32
+ /** Decoded JOSE header (`alg`, `kid`, `typ`, ...). */
31
33
  readonly header: Record<string, unknown>;
34
+ /** Decoded claims payload after signature and time-claim checks passed. */
32
35
  readonly payload: Record<string, unknown>;
33
36
  }
34
37
  /** Key material accepted by the signer/verifier. */
35
38
  export type JwtKeyMaterial = CryptoKey | Uint8Array | JsonWebKey;
36
39
  /** Options for {@link createJwtSigner}. */
37
40
  export interface JwtSignerOptions {
41
+ /** Signing algorithm. `"none"` is always refused. */
38
42
  alg: JwtAlgorithm;
43
+ /** Signing key. HS* secrets shorter than 32 bytes and RSA keys under 2048 bits are refused. */
39
44
  key: JwtKeyMaterial;
40
45
  /**
41
46
  * Maximum allowed `exp - iat` window in seconds. Required: refuse-at-
@@ -98,6 +103,10 @@ export interface JwtVerifierOptions {
98
103
  * `acknowledgeNoExp: true` was set at construction outside production) and
99
104
  * refuses payloads whose `exp - (iat | now)` exceeds `maxLifetimeSeconds`.
100
105
  *
106
+ * @param opts - Algorithm, key, and lifetime policy; see {@link JwtSignerOptions}.
107
+ * @returns An object whose `sign(payload)` resolves to the compact JWS string.
108
+ * @throws {JwtError} for `alg: "none"`, unknown algorithms, weak keys, a
109
+ * missing/invalid `maxLifetimeSeconds`, or `acknowledgeNoExp` in production.
101
110
  * @since 0.21.0
102
111
  */
103
112
  export declare function createJwtSigner(opts: JwtSignerOptions): {
@@ -110,6 +119,11 @@ export declare function createJwtSigner(opts: JwtSignerOptions): {
110
119
  * refuses-at-construction when a symmetric algorithm (`HS*`) is mixed with
111
120
  * a JWK / JWKS-shaped key source (the documented confused-deputy attack).
112
121
  *
122
+ * @param opts - Allowlist, key source, and claim checks; see {@link JwtVerifierOptions}.
123
+ * @returns An object whose `verify(token)` resolves to the decoded
124
+ * {@link JwtVerified} or rejects with {@link JwtError}.
125
+ * @throws {JwtError} at construction for an empty/invalid allowlist, `"none"`
126
+ * in the allowlist, weak HS* secrets, or HS* mixed with a JWK source.
113
127
  * @since 0.21.0
114
128
  */
115
129
  export declare function createJwtVerifier(opts: JwtVerifierOptions): {
package/dist/jwt.js CHANGED
@@ -60,6 +60,7 @@ const MIN_RSA_KEY_BITS = 2048;
60
60
  export const DEFAULT_JWT_MAX_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
61
61
  /** Structured error thrown by every JWT helper. */
62
62
  export class JwtError extends Error {
63
+ /** Stable machine-readable error code (e.g. `"alg_none_refused"`, `"weak_hs_secret"`). */
63
64
  code;
64
65
  constructor(code, message) {
65
66
  super(`[${code}] ${message}`);
@@ -228,6 +229,10 @@ function buildSignAlgorithm(alg) {
228
229
  * `acknowledgeNoExp: true` was set at construction outside production) and
229
230
  * refuses payloads whose `exp - (iat | now)` exceeds `maxLifetimeSeconds`.
230
231
  *
232
+ * @param opts - Algorithm, key, and lifetime policy; see {@link JwtSignerOptions}.
233
+ * @returns An object whose `sign(payload)` resolves to the compact JWS string.
234
+ * @throws {JwtError} for `alg: "none"`, unknown algorithms, weak keys, a
235
+ * missing/invalid `maxLifetimeSeconds`, or `acknowledgeNoExp` in production.
231
236
  * @since 0.21.0
232
237
  */
233
238
  export function createJwtSigner(opts) {
@@ -326,6 +331,11 @@ function normalizeStringSet(value) {
326
331
  * refuses-at-construction when a symmetric algorithm (`HS*`) is mixed with
327
332
  * a JWK / JWKS-shaped key source (the documented confused-deputy attack).
328
333
  *
334
+ * @param opts - Allowlist, key source, and claim checks; see {@link JwtVerifierOptions}.
335
+ * @returns An object whose `verify(token)` resolves to the decoded
336
+ * {@link JwtVerified} or rejects with {@link JwtError}.
337
+ * @throws {JwtError} at construction for an empty/invalid allowlist, `"none"`
338
+ * in the allowlist, weak HS* secrets, or HS* mixed with a JWK source.
329
339
  * @since 0.21.0
330
340
  */
331
341
  export function createJwtVerifier(opts) {
@@ -65,6 +65,9 @@ export interface LoadSheddingSnapshot {
65
65
  * retryAfterSeconds: 5,
66
66
  * }));
67
67
  * ```
68
+ *
69
+ * @param opts - Pressure thresholds (event-loop delay default 1000 ms, ELU default 0.98, optional heap/RSS byte caps), sampling interval, `Retry-After` seconds, and an optional custom `healthCheck`.
70
+ * @returns A {@link Hooks} bundle that sheds requests with `503` + `Retry-After` while pressure thresholds are exceeded.
68
71
  */
69
72
  export declare function loadShedding(opts?: LoadSheddingOptions): Hooks;
70
73
  /**
@@ -37,6 +37,9 @@ async function tryLoadPerfHooks() {
37
37
  * retryAfterSeconds: 5,
38
38
  * }));
39
39
  * ```
40
+ *
41
+ * @param opts - Pressure thresholds (event-loop delay default 1000 ms, ELU default 0.98, optional heap/RSS byte caps), sampling interval, `Retry-After` seconds, and an optional custom `healthCheck`.
42
+ * @returns A {@link Hooks} bundle that sheds requests with `503` + `Retry-After` while pressure thresholds are exceeded.
40
43
  */
41
44
  export function loadShedding(opts = {}) {
42
45
  const maxDelay = opts.maxEventLoopDelayMs ?? 1000;
package/dist/logger.d.ts CHANGED
@@ -10,13 +10,21 @@ export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
10
10
  * thin adapter; the default implementation is {@link createLogger}.
11
11
  */
12
12
  export interface Logger {
13
+ /** Minimum level emitted; records below this level are dropped. */
13
14
  level: LogLevel;
15
+ /** Log at `trace` level. Pass a fields object plus optional message, or a message string. */
14
16
  trace(obj: object | string, msg?: string): void;
17
+ /** Log at `debug` level. Pass a fields object plus optional message, or a message string. */
15
18
  debug(obj: object | string, msg?: string): void;
19
+ /** Log at `info` level. Pass a fields object plus optional message, or a message string. */
16
20
  info(obj: object | string, msg?: string): void;
21
+ /** Log at `warn` level. Pass a fields object plus optional message, or a message string. */
17
22
  warn(obj: object | string, msg?: string): void;
23
+ /** Log at `error` level. Pass a fields object plus optional message, or a message string. */
18
24
  error(obj: object | string, msg?: string): void;
25
+ /** Log at `fatal` level. Pass a fields object plus optional message, or a message string. */
19
26
  fatal(obj: object | string, msg?: string): void;
27
+ /** Return a derived logger whose records always include `bindings` merged into each record. */
20
28
  child(bindings: Record<string, unknown>): Logger;
21
29
  }
22
30
  /**
@@ -63,7 +71,9 @@ export interface LoggerRedactionOptions {
63
71
  export declare const DEFAULT_REDACT_KEYS: readonly string[];
64
72
  /** Options for {@link createLogger}. */
65
73
  export interface ConsoleLoggerOptions {
74
+ /** Minimum level to emit. Defaults to `"info"`. */
66
75
  level?: LogLevel;
76
+ /** Fields merged into every record emitted by this logger and its children. */
67
77
  bindings?: Record<string, unknown>;
68
78
  /** Where to write. Defaults to process.stdout.write or console.log. */
69
79
  write?: (line: string) => void;
@@ -89,6 +99,9 @@ interface ResolvedRedaction {
89
99
  * `cfg.redactJwt` is on) with `cfg.censor`. Exported for direct use by
90
100
  * custom logger implementations that want the same defaults.
91
101
  *
102
+ * @param record - Log record to redact. Mutated in place (cycle-safe, depth-capped).
103
+ * @param cfg - Resolved redaction settings (key set, censor, JWT/credential toggles, max depth).
104
+ * @returns The same `record` object, for chaining.
92
105
  * @since 0.15.0
93
106
  */
94
107
  export declare function redactRecord(record: Record<string, unknown>, cfg: ResolvedRedaction): Record<string, unknown>;
package/dist/logger.js CHANGED
@@ -123,6 +123,9 @@ function redactString(value, cfg) {
123
123
  * `cfg.redactJwt` is on) with `cfg.censor`. Exported for direct use by
124
124
  * custom logger implementations that want the same defaults.
125
125
  *
126
+ * @param record - Log record to redact. Mutated in place (cycle-safe, depth-capped).
127
+ * @param cfg - Resolved redaction settings (key set, censor, JWT/credential toggles, max depth).
128
+ * @returns The same `record` object, for chaining.
126
129
  * @since 0.15.0
127
130
  */
128
131
  export function redactRecord(record, cfg) {
package/dist/mcp.d.ts CHANGED
@@ -52,6 +52,22 @@ export type McpJsonSchema = McpJsonObject;
52
52
  * @since 1.0.0
53
53
  */
54
54
  export type McpJsonRpcId = string | number | null;
55
+ /**
56
+ * Icon metadata clients may render next to a server, tool, resource, or
57
+ * prompt (MCP 2025-11-25, SEP-973).
58
+ *
59
+ * @since 1.0.0
60
+ */
61
+ export interface McpIcon {
62
+ /** Icon URL. Prefer `https:` or `data:` URIs that clients can fetch safely. */
63
+ src: string;
64
+ /** Optional icon media type, e.g. `"image/png"`. */
65
+ mimeType?: string;
66
+ /** Optional pixel sizes the icon is available in, e.g. `["48x48"]`. */
67
+ sizes?: string[];
68
+ /** Optional theme the icon is designed for. */
69
+ theme?: "light" | "dark";
70
+ }
55
71
  /**
56
72
  * Identity block returned from the MCP `initialize` handshake.
57
73
  *
@@ -64,6 +80,12 @@ export interface McpServerInfo {
64
80
  title?: string;
65
81
  /** Server version surfaced to clients for debugging and compatibility. */
66
82
  version: string;
83
+ /** Optional human-readable server description (MCP 2025-11-25). */
84
+ description?: string;
85
+ /** Optional homepage URL for this server (MCP 2025-11-25). */
86
+ websiteUrl?: string;
87
+ /** Optional icons clients may display for this server (MCP 2025-11-25). */
88
+ icons?: McpIcon[];
67
89
  }
68
90
  /**
69
91
  * Per-request context passed to tool, resource, and prompt handlers.
@@ -74,9 +96,10 @@ export interface McpRequestContext {
74
96
  /** The original HTTP request received by the DaloyJS route. */
75
97
  request: Request;
76
98
  /**
77
- * Protocol version selected for this call. Before `initialize`, this is the
78
- * version from the `MCP-Protocol-Version` header when present, otherwise the
79
- * handler's preferred protocol version.
99
+ * Protocol version selected for this call. `initialize` negotiates it from
100
+ * `params.protocolVersion`; other calls take the `MCP-Protocol-Version`
101
+ * header, falling back to `2025-03-26` (the spec's assumption for
102
+ * headerless requests) when supported, otherwise the preferred version.
80
103
  */
81
104
  protocolVersion: string;
82
105
  /** JSON-RPC id for request/response correlation. */
@@ -90,7 +113,9 @@ export interface McpRequestContext {
90
113
  * @since 1.0.0
91
114
  */
92
115
  export interface McpTextContent {
116
+ /** Discriminator literal identifying this block as text. */
93
117
  type: "text";
118
+ /** The plain-text payload of the block. */
94
119
  text: string;
95
120
  }
96
121
  /**
@@ -102,8 +127,11 @@ export interface McpTextContent {
102
127
  * @since 1.0.0
103
128
  */
104
129
  export interface McpImageContent {
130
+ /** Discriminator literal identifying this block as an image. */
105
131
  type: "image";
132
+ /** Base64-encoded image bytes. */
106
133
  data: string;
134
+ /** Image media type, e.g. `"image/png"`. */
107
135
  mimeType: string;
108
136
  }
109
137
  /**
@@ -112,7 +140,9 @@ export interface McpImageContent {
112
140
  * @since 1.0.0
113
141
  */
114
142
  export interface McpEmbeddedResourceContent {
143
+ /** Discriminator literal identifying this block as an embedded resource. */
115
144
  type: "resource";
145
+ /** The embedded resource contents (uri plus text or base64 blob). */
116
146
  resource: McpResourceContents;
117
147
  }
118
148
  /**
@@ -131,13 +161,36 @@ export type McpContent = McpTextContent | McpImageContent | McpEmbeddedResourceC
131
161
  * @since 1.0.0
132
162
  */
133
163
  export interface McpToolResult {
134
- /** Human or model-readable content blocks returned to the MCP client. */
135
- content: McpContent[];
164
+ /**
165
+ * Human or model-readable content blocks returned to the MCP client. When
166
+ * omitted, {@link createMcpHandler} backfills a text block serializing
167
+ * `structuredContent` so pre-2025-06-18 clients still receive output.
168
+ */
169
+ content?: McpContent[];
136
170
  /** Optional structured payload for clients that can consume typed output. */
137
171
  structuredContent?: McpJsonObject;
138
172
  /** Set to `true` for domain/tool errors the model may recover from. */
139
173
  isError?: boolean;
140
174
  }
175
+ /**
176
+ * Behavioral hints a tool can advertise to MCP clients. Hints are untrusted
177
+ * metadata for UX decisions (confirmation prompts, badges); clients must not
178
+ * rely on them for security decisions.
179
+ *
180
+ * @since 1.0.0
181
+ */
182
+ export interface McpToolAnnotations {
183
+ /** Human-readable title for the tool. */
184
+ title?: string;
185
+ /** Hint that the tool does not modify its environment. */
186
+ readOnlyHint?: boolean;
187
+ /** Hint that the tool may perform destructive updates. */
188
+ destructiveHint?: boolean;
189
+ /** Hint that repeated calls with the same arguments have no extra effect. */
190
+ idempotentHint?: boolean;
191
+ /** Hint that the tool interacts with external entities. */
192
+ openWorldHint?: boolean;
193
+ }
141
194
  /**
142
195
  * Handler for a single MCP tool.
143
196
  *
@@ -172,6 +225,16 @@ export interface McpTool<TArgs extends Record<string, unknown> = Record<string,
172
225
  description: string;
173
226
  /** JSON Schema for `params.arguments`. */
174
227
  inputSchema: McpJsonSchema;
228
+ /**
229
+ * Optional JSON Schema describing `structuredContent` in tool results
230
+ * (MCP 2025-06-18). When set, handlers should return `structuredContent`
231
+ * matching it.
232
+ */
233
+ outputSchema?: McpJsonSchema;
234
+ /** Optional behavioral hints for clients. */
235
+ annotations?: McpToolAnnotations;
236
+ /** Optional icons clients may display for this tool (MCP 2025-11-25). */
237
+ icons?: McpIcon[];
175
238
  /** Execute the tool with untrusted JSON arguments. */
176
239
  handler: McpToolHandler<TArgs>;
177
240
  }
@@ -191,6 +254,8 @@ export interface McpResource {
191
254
  description?: string;
192
255
  /** MIME type returned by `resources/read`, such as `"application/json"`. */
193
256
  mimeType?: string;
257
+ /** Optional icons clients may display for this resource (MCP 2025-11-25). */
258
+ icons?: McpIcon[];
194
259
  }
195
260
  /**
196
261
  * Resource payload returned from `resources/read`.
@@ -228,6 +293,51 @@ export interface McpResourceDefinition extends McpResource {
228
293
  */
229
294
  read: (ctx: McpRequestContext) => McpResourceContents | McpResourceContents[] | Promise<McpResourceContents | McpResourceContents[]>;
230
295
  }
296
+ /**
297
+ * Resource template metadata returned from `resources/templates/list`.
298
+ *
299
+ * @since 1.0.0
300
+ */
301
+ export interface McpResourceTemplate {
302
+ /**
303
+ * URI template for this resource family, e.g. `"daloy://records/{id}"`.
304
+ * DaloyJS supports simple `{name}` variables (RFC 6570 level 1); each
305
+ * variable matches one URI segment (no `/`).
306
+ */
307
+ uriTemplate: string;
308
+ /** Stable template name. */
309
+ name: string;
310
+ /** Optional human-readable title. */
311
+ title?: string;
312
+ /** Optional description shown by clients. */
313
+ description?: string;
314
+ /** MIME type of resources produced by this template. */
315
+ mimeType?: string;
316
+ /** Optional icons clients may display for this template (MCP 2025-11-25). */
317
+ icons?: McpIcon[];
318
+ }
319
+ /**
320
+ * Definition of a parameterized MCP resource template.
321
+ *
322
+ * Templates answer `resources/read` for URIs that match `uriTemplate` but are
323
+ * not listed as concrete resources. Template variables arrive as raw URI
324
+ * segment strings; validate them before touching databases or files.
325
+ *
326
+ * @since 1.0.0
327
+ */
328
+ export interface McpResourceTemplateDefinition extends McpResourceTemplate {
329
+ /**
330
+ * Read a resource instantiated from this template for `resources/read`.
331
+ *
332
+ * @param uri - The full resource URI requested by the client.
333
+ * @param variables - Template variable values extracted from `uri`.
334
+ * @param ctx - Request metadata and the original HTTP request.
335
+ * @returns One or more content entries for this resource.
336
+ * @throws {McpToolError} for caller-correctable failures such as an unknown
337
+ * record id; these become JSON-RPC invalid-params errors.
338
+ */
339
+ read: (uri: string, variables: Record<string, string>, ctx: McpRequestContext) => McpResourceContents | McpResourceContents[] | Promise<McpResourceContents | McpResourceContents[]>;
340
+ }
231
341
  /**
232
342
  * Argument metadata for an MCP prompt.
233
343
  *
@@ -253,8 +363,14 @@ export interface McpPrompt {
253
363
  title?: string;
254
364
  /** Optional prompt description. */
255
365
  description?: string;
256
- /** Prompt arguments clients may supply to `prompts/get`. */
366
+ /**
367
+ * Prompt arguments clients may supply to `prompts/get`. Arguments marked
368
+ * `required: true` are enforced by {@link createMcpHandler}: a `prompts/get`
369
+ * call missing one fails with a JSON-RPC invalid-params error.
370
+ */
257
371
  arguments?: McpPromptArgument[];
372
+ /** Optional icons clients may display for this prompt (MCP 2025-11-25). */
373
+ icons?: McpIcon[];
258
374
  }
259
375
  /**
260
376
  * Message returned from `prompts/get`.
@@ -326,8 +442,25 @@ export interface McpHandlerOptions {
326
442
  tools?: readonly McpTool[];
327
443
  /** Readable resources exposed through `resources/list` and `resources/read`. */
328
444
  resources?: readonly McpResourceDefinition[];
445
+ /**
446
+ * Parameterized resource templates exposed through
447
+ * `resources/templates/list` and matched by `resources/read` when a URI is
448
+ * not a listed concrete resource.
449
+ */
450
+ resourceTemplates?: readonly McpResourceTemplateDefinition[];
329
451
  /** Reusable prompts exposed through `prompts/list` and `prompts/get`. */
330
452
  prompts?: readonly McpPromptDefinition[];
453
+ /**
454
+ * Extra `Origin` header values allowed on MCP requests, e.g.
455
+ * `"https://app.example.com"` (or the literal `"null"` for opaque origins).
456
+ *
457
+ * The MCP Streamable HTTP spec requires servers to validate `Origin` to
458
+ * prevent DNS rebinding attacks. DaloyJS always allows requests without an
459
+ * `Origin` header (non-browser MCP clients), same-origin requests, and
460
+ * loopback origins (`localhost`, `*.localhost`, `127.0.0.1`, `[::1]`); every
461
+ * other origin is rejected with `403` unless listed here.
462
+ */
463
+ allowedOrigins?: readonly string[];
331
464
  /** Accepted MCP protocol versions. Defaults to {@link MCP_PROTOCOL_VERSIONS}. */
332
465
  protocolVersions?: readonly string[];
333
466
  /**
@@ -364,10 +497,16 @@ export type McpHandler = (request: Request) => Promise<Response>;
364
497
  *
365
498
  * The handler implements the server side of MCP over one HTTP endpoint:
366
499
  * `initialize`, `ping`, `tools/list`, `tools/call`, `resources/list`,
367
- * `resources/read`, `prompts/list`, and `prompts/get`. It accepts JSON-RPC
368
- * requests over `POST`, acknowledges notifications with `202`, validates the
369
- * `MCP-Protocol-Version` header, bounds request bodies, and returns JSON-RPC
370
- * errors for malformed input.
500
+ * `resources/templates/list`, `resources/read`, `prompts/list`, and
501
+ * `prompts/get`. It accepts JSON-RPC requests over `POST`, acknowledges
502
+ * notifications with `202`, validates the `MCP-Protocol-Version` header,
503
+ * bounds request bodies, enforces required prompt arguments, and returns
504
+ * JSON-RPC errors for malformed input.
505
+ *
506
+ * Security: per the Streamable HTTP spec's DNS-rebinding guidance, every
507
+ * request bearing an `Origin` header is validated. Same-origin and loopback
508
+ * origins pass; anything else is rejected with `403` unless listed in
509
+ * {@link McpHandlerOptions.allowedOrigins}.
371
510
  *
372
511
  * It intentionally does not spawn stdio servers, manage OAuth metadata, keep
373
512
  * durable sessions, or open server-initiated SSE streams. Use DaloyJS
@@ -378,6 +517,9 @@ export type McpHandler = (request: Request) => Promise<Response>;
378
517
  * @param options - Server identity, capabilities, limits, and response headers.
379
518
  * @returns A Fetch-compatible request handler suitable for {@link mcpRoutes}
380
519
  * or for direct use in any web-standard runtime.
520
+ * @throws {TypeError} at construction for invalid serverInfo, protocol
521
+ * versions, body limits, duplicate names/URIs, malformed `allowedOrigins`
522
+ * entries, or unsupported URI template expressions.
381
523
  *
382
524
  * @example
383
525
  * ```ts