@daloyjs/core 0.17.0 → 0.29.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 +3 -3
- package/dist/adapters/bun.d.ts.map +1 -1
- package/dist/adapters/bun.js +38 -7
- package/dist/adapters/bun.js.map +1 -1
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +29 -7
- package/dist/adapters/node.js.map +1 -1
- package/dist/app.d.ts +322 -3
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +715 -29
- package/dist/app.js.map +1 -1
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +218 -1
- package/dist/cli.js.map +1 -1
- package/dist/combine.d.ts +97 -0
- package/dist/combine.d.ts.map +1 -0
- package/dist/combine.js +247 -0
- package/dist/combine.js.map +1 -0
- package/dist/compression.d.ts +127 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +368 -0
- package/dist/compression.js.map +1 -0
- package/dist/config.d.ts +97 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +132 -0
- package/dist/config.js.map +1 -0
- package/dist/conn-info.d.ts +121 -0
- package/dist/conn-info.d.ts.map +1 -0
- package/dist/conn-info.js +145 -0
- package/dist/conn-info.js.map +1 -0
- package/dist/cookie.d.ts +112 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cookie.js +185 -0
- package/dist/cookie.js.map +1 -0
- package/dist/dependency.d.ts +47 -0
- package/dist/dependency.d.ts.map +1 -0
- package/dist/dependency.js +68 -0
- package/dist/dependency.js.map +1 -0
- package/dist/etag.d.ts +48 -0
- package/dist/etag.d.ts.map +1 -0
- package/dist/etag.js +117 -0
- package/dist/etag.js.map +1 -0
- package/dist/index.d.ts +40 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/ip-restriction.d.ts +75 -0
- package/dist/ip-restriction.d.ts.map +1 -0
- package/dist/ip-restriction.js +203 -0
- package/dist/ip-restriction.js.map +1 -0
- package/dist/jwk.d.ts +82 -0
- package/dist/jwk.d.ts.map +1 -0
- package/dist/jwk.js +269 -0
- package/dist/jwk.js.map +1 -0
- package/dist/jwt.d.ts +103 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +437 -0
- package/dist/jwt.js.map +1 -0
- package/dist/load-shedding.d.ts +73 -0
- package/dist/load-shedding.d.ts.map +1 -0
- package/dist/load-shedding.js +171 -0
- package/dist/load-shedding.js.map +1 -0
- package/dist/middleware.d.ts +181 -2
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +367 -77
- package/dist/middleware.js.map +1 -1
- package/dist/multipart.d.ts +17 -0
- package/dist/multipart.d.ts.map +1 -1
- package/dist/multipart.js +117 -1
- package/dist/multipart.js.map +1 -1
- package/dist/openapi.d.ts +8 -0
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +18 -1
- package/dist/openapi.js.map +1 -1
- package/dist/security-schemes.d.ts +21 -5
- package/dist/security-schemes.d.ts.map +1 -1
- package/dist/security-schemes.js +32 -5
- package/dist/security-schemes.js.map +1 -1
- package/dist/session.d.ts +23 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +106 -86
- package/dist/session.js.map +1 -1
- package/dist/subdomains.d.ts +97 -0
- package/dist/subdomains.d.ts.map +1 -0
- package/dist/subdomains.js +157 -0
- package/dist/subdomains.js.map +1 -0
- package/dist/time-claims.d.ts +72 -0
- package/dist/time-claims.d.ts.map +1 -0
- package/dist/time-claims.js +88 -0
- package/dist/time-claims.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/websocket.d.ts +51 -34
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +162 -2
- package/dist/websocket.js.map +1 -1
- package/package.json +31 -3
package/dist/jwt.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 5 leftover: first-party `jwt()` sign + verify helpers with secure-by-default
|
|
3
|
+
* algorithm discipline and time-claim validation.
|
|
4
|
+
*
|
|
5
|
+
* The framework refuses `alg: "none"` outright, requires an explicit algorithm
|
|
6
|
+
* allowlist on the verifier (no implicit "any RS256" default), refuses
|
|
7
|
+
* symmetric algorithms (`HS*`) when the key source is a JWK / JWKS URL
|
|
8
|
+
* (the classic confused-deputy attack), refuses to issue tokens without an
|
|
9
|
+
* `exp` claim or with an `exp` exceeding the construction-time
|
|
10
|
+
* `maxLifetimeSeconds`, and validates `exp` / `nbf` / `iat` on every verify
|
|
11
|
+
* (with optional `iss` / `aud` checks) — none of which can be silenced in
|
|
12
|
+
* production under `secureDefaults`.
|
|
13
|
+
*
|
|
14
|
+
* Pair with the Wave 1 webhook HMAC helpers in `@daloyjs/core` for body-bound
|
|
15
|
+
* authentication, and with the Wave 5 `requireScopes()` middleware for
|
|
16
|
+
* scope-based authorization on top of a verified JWT.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.21.0
|
|
19
|
+
*/
|
|
20
|
+
/** Algorithms understood by the helper. SHA-1 / `none` are deliberately absent. */
|
|
21
|
+
export type JwtAlgorithm = "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "PS256" | "PS384" | "PS512" | "ES256" | "ES384" | "ES512" | "EdDSA";
|
|
22
|
+
/** Default lifetime cap when none is declared in development (`30d`). */
|
|
23
|
+
export declare const DEFAULT_JWT_MAX_LIFETIME_SECONDS: number;
|
|
24
|
+
/** Structured error thrown by every JWT helper. */
|
|
25
|
+
export declare class JwtError extends Error {
|
|
26
|
+
readonly code: string;
|
|
27
|
+
constructor(code: string, message: string);
|
|
28
|
+
}
|
|
29
|
+
/** Result of a successful verify. */
|
|
30
|
+
export interface JwtVerified {
|
|
31
|
+
readonly header: Record<string, unknown>;
|
|
32
|
+
readonly payload: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/** Key material accepted by the signer/verifier. */
|
|
35
|
+
export type JwtKeyMaterial = CryptoKey | Uint8Array | JsonWebKey;
|
|
36
|
+
/** Options for {@link createJwtSigner}. */
|
|
37
|
+
export interface JwtSignerOptions {
|
|
38
|
+
alg: JwtAlgorithm;
|
|
39
|
+
key: JwtKeyMaterial;
|
|
40
|
+
/**
|
|
41
|
+
* Maximum allowed `exp - iat` window in seconds. Required: refuse-at-
|
|
42
|
+
* construction when omitted so "default forever" is impossible.
|
|
43
|
+
*/
|
|
44
|
+
maxLifetimeSeconds: number;
|
|
45
|
+
/**
|
|
46
|
+
* Opt-out flag for issuing tokens with no `exp` claim. Refused in
|
|
47
|
+
* production under `secureDefaults` (a token that never expires is wrong
|
|
48
|
+
* in every threat model).
|
|
49
|
+
*/
|
|
50
|
+
acknowledgeNoExp?: boolean;
|
|
51
|
+
/** Override the resolved environment (defaults to `NODE_ENV`). */
|
|
52
|
+
env?: "production" | "development" | "test";
|
|
53
|
+
/** Set `secureDefaults: false` to skip the production opt-out gate. */
|
|
54
|
+
secureDefaults?: boolean;
|
|
55
|
+
/** Optional extra header fields (`kid`, `typ`, ...). `alg` is always derived. */
|
|
56
|
+
header?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
/** Options for {@link createJwtVerifier}. */
|
|
59
|
+
export interface JwtVerifierOptions {
|
|
60
|
+
/** Explicit allowlist — no implicit defaults. Must be non-empty. */
|
|
61
|
+
algorithms: JwtAlgorithm[];
|
|
62
|
+
/** Static key, or a per-token resolver that picks the key by `header.kid`. */
|
|
63
|
+
key: JwtKeyMaterial | ((header: Record<string, unknown>) => JwtKeyMaterial | Promise<JwtKeyMaterial>);
|
|
64
|
+
/** Optional issuer (string or allowlist). */
|
|
65
|
+
issuer?: string | string[];
|
|
66
|
+
/** Optional audience (string or allowlist). */
|
|
67
|
+
audience?: string | string[];
|
|
68
|
+
/** Clock skew tolerance applied to `exp` / `nbf` / `iat`. Default `0`. */
|
|
69
|
+
clockSkewSeconds?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Refuse-to-construct when the algorithm allowlist mixes symmetric (`HS*`)
|
|
72
|
+
* with a JWK / JWKS-shaped resolver. Default: `true`. This closes the
|
|
73
|
+
* documented confused-deputy attack class where a verifier configured for
|
|
74
|
+
* RS256 + JWK silently accepts an HS256 token signed with the public key.
|
|
75
|
+
*/
|
|
76
|
+
refuseSymmetricWithJwk?: boolean;
|
|
77
|
+
/** Optional injectable clock for tests. */
|
|
78
|
+
now?: () => number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create a JWT signer locked to one algorithm + key. The returned `sign()`
|
|
82
|
+
* function refuses payloads without an `exp` claim (unless
|
|
83
|
+
* `acknowledgeNoExp: true` was set at construction outside production) and
|
|
84
|
+
* refuses payloads whose `exp - (iat | now)` exceeds `maxLifetimeSeconds`.
|
|
85
|
+
*
|
|
86
|
+
* @since 0.21.0
|
|
87
|
+
*/
|
|
88
|
+
export declare function createJwtSigner(opts: JwtSignerOptions): {
|
|
89
|
+
sign(payload: Record<string, unknown>): Promise<string>;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Create a JWT verifier locked to an explicit algorithm allowlist. Validates
|
|
93
|
+
* `exp` / `nbf` / `iat` on every call (clock skew configurable). Refuses
|
|
94
|
+
* `alg: "none"` and any token whose header `alg` is not in the allowlist;
|
|
95
|
+
* refuses-at-construction when a symmetric algorithm (`HS*`) is mixed with
|
|
96
|
+
* a JWK / JWKS-shaped key source (the documented confused-deputy attack).
|
|
97
|
+
*
|
|
98
|
+
* @since 0.21.0
|
|
99
|
+
*/
|
|
100
|
+
export declare function createJwtVerifier(opts: JwtVerifierOptions): {
|
|
101
|
+
verify(token: string): Promise<JwtVerified>;
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,mFAAmF;AACnF,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,GACP,OAAO,CAAC;AA+BZ,yEAAyE;AACzE,eAAO,MAAM,gCAAgC,QAAoB,CAAC;AAElE,mDAAmD;AACnD,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,oDAAoD;AACpD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEjE,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,cAAc,CAAC;IACpB;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,GAAG,CAAC,EAAE,YAAY,GAAG,aAAa,GAAG,MAAM,CAAC;IAC5C,uEAAuE;IACvE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,6CAA6C;AAC7C,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,8EAA8E;IAC9E,GAAG,EAAE,cAAc,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IACtG,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAsKD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG;IAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CA4FnH;AA2BD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG;IAAE,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAAE,CA4F3G"}
|
package/dist/jwt.js
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 5 leftover: first-party `jwt()` sign + verify helpers with secure-by-default
|
|
3
|
+
* algorithm discipline and time-claim validation.
|
|
4
|
+
*
|
|
5
|
+
* The framework refuses `alg: "none"` outright, requires an explicit algorithm
|
|
6
|
+
* allowlist on the verifier (no implicit "any RS256" default), refuses
|
|
7
|
+
* symmetric algorithms (`HS*`) when the key source is a JWK / JWKS URL
|
|
8
|
+
* (the classic confused-deputy attack), refuses to issue tokens without an
|
|
9
|
+
* `exp` claim or with an `exp` exceeding the construction-time
|
|
10
|
+
* `maxLifetimeSeconds`, and validates `exp` / `nbf` / `iat` on every verify
|
|
11
|
+
* (with optional `iss` / `aud` checks) — none of which can be silenced in
|
|
12
|
+
* production under `secureDefaults`.
|
|
13
|
+
*
|
|
14
|
+
* Pair with the Wave 1 webhook HMAC helpers in `@daloyjs/core` for body-bound
|
|
15
|
+
* authentication, and with the Wave 5 `requireScopes()` middleware for
|
|
16
|
+
* scope-based authorization on top of a verified JWT.
|
|
17
|
+
*
|
|
18
|
+
* @since 0.21.0
|
|
19
|
+
*/
|
|
20
|
+
import { assertTemporalClaims, TemporalClaimError } from "./time-claims.js";
|
|
21
|
+
const SYMMETRIC = new Set([
|
|
22
|
+
"HS256",
|
|
23
|
+
"HS384",
|
|
24
|
+
"HS512",
|
|
25
|
+
]);
|
|
26
|
+
const ASYMMETRIC = new Set([
|
|
27
|
+
"RS256",
|
|
28
|
+
"RS384",
|
|
29
|
+
"RS512",
|
|
30
|
+
"PS256",
|
|
31
|
+
"PS384",
|
|
32
|
+
"PS512",
|
|
33
|
+
"ES256",
|
|
34
|
+
"ES384",
|
|
35
|
+
"ES512",
|
|
36
|
+
"EdDSA",
|
|
37
|
+
]);
|
|
38
|
+
const ALL_ALGS = new Set([...SYMMETRIC, ...ASYMMETRIC]);
|
|
39
|
+
/**
|
|
40
|
+
* Wave 8 — minimum byte length for an HS* HMAC secret. RFC 7518 §3.2
|
|
41
|
+
* requires HS256 keys to be at least 256 bits (32 bytes). Apply the same
|
|
42
|
+
* floor to HS384 / HS512 because shorter keys do not buy a meaningfully
|
|
43
|
+
* stronger HMAC than HS256-with-a-256-bit-key.
|
|
44
|
+
*/
|
|
45
|
+
const MIN_HS_KEY_BYTES = 32;
|
|
46
|
+
/** Default lifetime cap when none is declared in development (`30d`). */
|
|
47
|
+
export const DEFAULT_JWT_MAX_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
|
|
48
|
+
/** Structured error thrown by every JWT helper. */
|
|
49
|
+
export class JwtError extends Error {
|
|
50
|
+
code;
|
|
51
|
+
constructor(code, message) {
|
|
52
|
+
super(`[${code}] ${message}`);
|
|
53
|
+
this.name = "JwtError";
|
|
54
|
+
this.code = code;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function getCrypto() {
|
|
58
|
+
const c = globalThis.crypto;
|
|
59
|
+
if (!c?.subtle) {
|
|
60
|
+
throw new JwtError("no_webcrypto", "jwt(): WebCrypto SubtleCrypto API is unavailable on this runtime.");
|
|
61
|
+
}
|
|
62
|
+
return c;
|
|
63
|
+
}
|
|
64
|
+
function b64urlEncode(bytes) {
|
|
65
|
+
let bin = "";
|
|
66
|
+
for (let i = 0; i < bytes.length; i++)
|
|
67
|
+
bin += String.fromCharCode(bytes[i]);
|
|
68
|
+
return btoa(bin).replace(/=+$/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
69
|
+
}
|
|
70
|
+
function b64urlDecode(input) {
|
|
71
|
+
if (!/^[A-Za-z0-9_-]*$/.test(input)) {
|
|
72
|
+
throw new JwtError("invalid_token", "JWT segment is not base64url.");
|
|
73
|
+
}
|
|
74
|
+
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
75
|
+
const pad = padded.length % 4 === 0 ? "" : "=".repeat(4 - (padded.length % 4));
|
|
76
|
+
const bin = atob(padded + pad);
|
|
77
|
+
const out = new Uint8Array(bin.length);
|
|
78
|
+
for (let i = 0; i < bin.length; i++)
|
|
79
|
+
out[i] = bin.charCodeAt(i);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
const ENC = new TextEncoder();
|
|
83
|
+
const DEC = new TextDecoder();
|
|
84
|
+
function isJsonObject(v) {
|
|
85
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
86
|
+
}
|
|
87
|
+
function isJsonWebKey(v) {
|
|
88
|
+
return isJsonObject(v) && typeof v.kty === "string";
|
|
89
|
+
}
|
|
90
|
+
function isCryptoKey(v) {
|
|
91
|
+
return (typeof v === "object" &&
|
|
92
|
+
v !== null &&
|
|
93
|
+
typeof v.type === "string" &&
|
|
94
|
+
typeof v.algorithm === "object");
|
|
95
|
+
}
|
|
96
|
+
function isProductionEnv(env) {
|
|
97
|
+
if (env === "production")
|
|
98
|
+
return true;
|
|
99
|
+
if (env === "development" || env === "test")
|
|
100
|
+
return false;
|
|
101
|
+
return (typeof process !== "undefined" &&
|
|
102
|
+
typeof process.env !== "undefined" &&
|
|
103
|
+
process.env.NODE_ENV === "production");
|
|
104
|
+
}
|
|
105
|
+
function algParams(alg) {
|
|
106
|
+
switch (alg) {
|
|
107
|
+
case "HS256":
|
|
108
|
+
return { name: "HMAC", hash: "SHA-256" };
|
|
109
|
+
case "HS384":
|
|
110
|
+
return { name: "HMAC", hash: "SHA-384" };
|
|
111
|
+
case "HS512":
|
|
112
|
+
return { name: "HMAC", hash: "SHA-512" };
|
|
113
|
+
case "RS256":
|
|
114
|
+
return { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
|
|
115
|
+
case "RS384":
|
|
116
|
+
return { name: "RSASSA-PKCS1-v1_5", hash: "SHA-384" };
|
|
117
|
+
case "RS512":
|
|
118
|
+
return { name: "RSASSA-PKCS1-v1_5", hash: "SHA-512" };
|
|
119
|
+
case "PS256":
|
|
120
|
+
return { name: "RSA-PSS", hash: "SHA-256", saltLength: 32 };
|
|
121
|
+
case "PS384":
|
|
122
|
+
return { name: "RSA-PSS", hash: "SHA-384", saltLength: 48 };
|
|
123
|
+
case "PS512":
|
|
124
|
+
return { name: "RSA-PSS", hash: "SHA-512", saltLength: 64 };
|
|
125
|
+
case "ES256":
|
|
126
|
+
return { name: "ECDSA", hash: "SHA-256", namedCurve: "P-256" };
|
|
127
|
+
case "ES384":
|
|
128
|
+
return { name: "ECDSA", hash: "SHA-384", namedCurve: "P-384" };
|
|
129
|
+
case "ES512":
|
|
130
|
+
return { name: "ECDSA", hash: "SHA-512", namedCurve: "P-521" };
|
|
131
|
+
case "EdDSA":
|
|
132
|
+
return { name: "Ed25519" };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function importKey(alg, material, usage) {
|
|
136
|
+
const params = algParams(alg);
|
|
137
|
+
const c = getCrypto();
|
|
138
|
+
if (isCryptoKey(material))
|
|
139
|
+
return material;
|
|
140
|
+
if (material instanceof Uint8Array) {
|
|
141
|
+
if (!SYMMETRIC.has(alg)) {
|
|
142
|
+
throw new JwtError("invalid_key", `jwt(): raw byte keys are only supported for HS256/HS384/HS512; got ${alg}.`);
|
|
143
|
+
}
|
|
144
|
+
// Wave 8 — refuse HS-shaped secrets shorter than 32 bytes (256 bits).
|
|
145
|
+
// RFC 7518 §3.2 requires HS256 keys to be "of the same size as the hash
|
|
146
|
+
// output (for instance, 256 bits for HS256)" — anything shorter is a
|
|
147
|
+
// known-weak HMAC key that turns the signature into the bottleneck.
|
|
148
|
+
if (material.byteLength < MIN_HS_KEY_BYTES) {
|
|
149
|
+
throw new JwtError("weak_hs_secret", `jwt(): ${alg} secret must be at least ${MIN_HS_KEY_BYTES} bytes (RFC 7518 §3.2); got ${material.byteLength}.`);
|
|
150
|
+
}
|
|
151
|
+
return c.subtle.importKey("raw", material, { name: "HMAC", hash: params.hash }, false, [usage]);
|
|
152
|
+
}
|
|
153
|
+
if (isJsonWebKey(material)) {
|
|
154
|
+
const importAlgorithm = params.name === "HMAC"
|
|
155
|
+
? { name: "HMAC", hash: params.hash }
|
|
156
|
+
: params.name === "ECDSA"
|
|
157
|
+
? { name: "ECDSA", namedCurve: params.namedCurve }
|
|
158
|
+
: params.name === "RSA-PSS"
|
|
159
|
+
? { name: "RSA-PSS", hash: params.hash }
|
|
160
|
+
: params.name === "RSASSA-PKCS1-v1_5"
|
|
161
|
+
? { name: "RSASSA-PKCS1-v1_5", hash: params.hash }
|
|
162
|
+
: { name: "Ed25519" };
|
|
163
|
+
return c.subtle.importKey("jwk", material, importAlgorithm, false, [usage]);
|
|
164
|
+
}
|
|
165
|
+
throw new JwtError("invalid_key", "jwt(): unsupported key material.");
|
|
166
|
+
}
|
|
167
|
+
function buildSignAlgorithm(alg) {
|
|
168
|
+
const params = algParams(alg);
|
|
169
|
+
if (params.name === "RSA-PSS")
|
|
170
|
+
return { name: "RSA-PSS", saltLength: params.saltLength };
|
|
171
|
+
if (params.name === "ECDSA")
|
|
172
|
+
return { name: "ECDSA", hash: params.hash };
|
|
173
|
+
if (params.name === "Ed25519")
|
|
174
|
+
return { name: "Ed25519" };
|
|
175
|
+
return { name: params.name };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Create a JWT signer locked to one algorithm + key. The returned `sign()`
|
|
179
|
+
* function refuses payloads without an `exp` claim (unless
|
|
180
|
+
* `acknowledgeNoExp: true` was set at construction outside production) and
|
|
181
|
+
* refuses payloads whose `exp - (iat | now)` exceeds `maxLifetimeSeconds`.
|
|
182
|
+
*
|
|
183
|
+
* @since 0.21.0
|
|
184
|
+
*/
|
|
185
|
+
export function createJwtSigner(opts) {
|
|
186
|
+
if (!opts || typeof opts !== "object") {
|
|
187
|
+
throw new JwtError("invalid_options", "jwt(): signer options object is required.");
|
|
188
|
+
}
|
|
189
|
+
const { alg } = opts;
|
|
190
|
+
if (alg === "none") {
|
|
191
|
+
throw new JwtError("alg_none_refused", 'jwt(): alg "none" is refused.');
|
|
192
|
+
}
|
|
193
|
+
if (!ALL_ALGS.has(alg)) {
|
|
194
|
+
throw new JwtError("invalid_alg", `jwt(): unknown algorithm "${String(alg)}". Allowed: ${[...ALL_ALGS].sort().join(", ")}.`);
|
|
195
|
+
}
|
|
196
|
+
if (SYMMETRIC.has(alg) &&
|
|
197
|
+
opts.key instanceof Uint8Array &&
|
|
198
|
+
opts.key.byteLength < MIN_HS_KEY_BYTES) {
|
|
199
|
+
throw new JwtError("weak_hs_secret", `jwt(): ${alg} secret must be at least ${MIN_HS_KEY_BYTES} bytes (RFC 7518 §3.2); got ${opts.key.byteLength}.`);
|
|
200
|
+
}
|
|
201
|
+
if (typeof opts.maxLifetimeSeconds !== "number" || !Number.isFinite(opts.maxLifetimeSeconds) || opts.maxLifetimeSeconds <= 0) {
|
|
202
|
+
throw new JwtError("missing_max_lifetime", "jwt(): maxLifetimeSeconds is required and must be a positive number — a token that never expires is wrong in every threat model.");
|
|
203
|
+
}
|
|
204
|
+
if (opts.acknowledgeNoExp === true && isProductionEnv(opts.env) && opts.secureDefaults !== false) {
|
|
205
|
+
throw new JwtError("ack_no_exp_refused_in_production", "jwt(): acknowledgeNoExp: true is refused in production under secureDefaults — every issued JWT must carry an exp claim.");
|
|
206
|
+
}
|
|
207
|
+
const resolved = (async () => {
|
|
208
|
+
const key = await importKey(alg, opts.key, "sign");
|
|
209
|
+
return {
|
|
210
|
+
alg,
|
|
211
|
+
key,
|
|
212
|
+
maxLifetimeSeconds: opts.maxLifetimeSeconds,
|
|
213
|
+
allowNoExp: opts.acknowledgeNoExp === true,
|
|
214
|
+
header: opts.header && isJsonObject(opts.header) ? { ...opts.header } : {},
|
|
215
|
+
};
|
|
216
|
+
})();
|
|
217
|
+
return {
|
|
218
|
+
async sign(payload) {
|
|
219
|
+
if (!isJsonObject(payload)) {
|
|
220
|
+
throw new JwtError("invalid_payload", "jwt().sign(): payload must be a plain object.");
|
|
221
|
+
}
|
|
222
|
+
const r = await resolved;
|
|
223
|
+
const now = Math.floor(Date.now() / 1000);
|
|
224
|
+
const iat = typeof payload.iat === "number" ? payload.iat : now;
|
|
225
|
+
const expRaw = payload.exp;
|
|
226
|
+
if (expRaw === undefined) {
|
|
227
|
+
if (!r.allowNoExp) {
|
|
228
|
+
throw new JwtError("missing_exp", "jwt().sign(): payload is missing the exp claim. Set payload.exp = unix seconds, or construct the signer with acknowledgeNoExp: true outside production.");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
if (typeof expRaw !== "number" || !Number.isFinite(expRaw)) {
|
|
233
|
+
throw new JwtError("invalid_exp", "jwt().sign(): payload.exp must be a finite number of seconds since the Unix epoch.");
|
|
234
|
+
}
|
|
235
|
+
const lifetime = expRaw - iat;
|
|
236
|
+
if (lifetime > r.maxLifetimeSeconds) {
|
|
237
|
+
throw new JwtError("exp_exceeds_max_lifetime", `jwt().sign(): payload.exp - iat (${lifetime}s) exceeds maxLifetimeSeconds (${r.maxLifetimeSeconds}s).`);
|
|
238
|
+
}
|
|
239
|
+
if (lifetime <= 0) {
|
|
240
|
+
throw new JwtError("exp_in_past", "jwt().sign(): payload.exp must be strictly greater than iat.");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const header = { ...r.header, alg: r.alg, typ: "JWT" };
|
|
244
|
+
const headerB64 = b64urlEncode(ENC.encode(JSON.stringify(header)));
|
|
245
|
+
const payloadB64 = b64urlEncode(ENC.encode(JSON.stringify(payload)));
|
|
246
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
247
|
+
const sig = new Uint8Array(await getCrypto().subtle.sign(buildSignAlgorithm(r.alg), r.key, ENC.encode(signingInput)));
|
|
248
|
+
return `${signingInput}.${b64urlEncode(sig)}`;
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function looksLikeJwkSource(key) {
|
|
253
|
+
if (typeof key === "function")
|
|
254
|
+
return true; // resolver — assume JWKS
|
|
255
|
+
return isJsonWebKey(key);
|
|
256
|
+
}
|
|
257
|
+
function normalizeStringSet(value) {
|
|
258
|
+
if (value === undefined)
|
|
259
|
+
return null;
|
|
260
|
+
const arr = typeof value === "string" ? [value] : value;
|
|
261
|
+
if (!Array.isArray(arr) || arr.length === 0) {
|
|
262
|
+
throw new JwtError("invalid_string_set", "jwt(): issuer/audience option must be a non-empty string or string[].");
|
|
263
|
+
}
|
|
264
|
+
for (const v of arr) {
|
|
265
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
266
|
+
throw new JwtError("invalid_string_set", "jwt(): issuer/audience entries must be non-empty strings.");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return new Set(arr);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Create a JWT verifier locked to an explicit algorithm allowlist. Validates
|
|
273
|
+
* `exp` / `nbf` / `iat` on every call (clock skew configurable). Refuses
|
|
274
|
+
* `alg: "none"` and any token whose header `alg` is not in the allowlist;
|
|
275
|
+
* refuses-at-construction when a symmetric algorithm (`HS*`) is mixed with
|
|
276
|
+
* a JWK / JWKS-shaped key source (the documented confused-deputy attack).
|
|
277
|
+
*
|
|
278
|
+
* @since 0.21.0
|
|
279
|
+
*/
|
|
280
|
+
export function createJwtVerifier(opts) {
|
|
281
|
+
if (!opts || typeof opts !== "object") {
|
|
282
|
+
throw new JwtError("invalid_options", "jwt(): verifier options object is required.");
|
|
283
|
+
}
|
|
284
|
+
if (!Array.isArray(opts.algorithms) || opts.algorithms.length === 0) {
|
|
285
|
+
throw new JwtError("missing_algorithms", "jwt(): verifier requires an explicit, non-empty algorithms allowlist — no implicit defaults.");
|
|
286
|
+
}
|
|
287
|
+
const allow = new Set();
|
|
288
|
+
for (const alg of opts.algorithms) {
|
|
289
|
+
if (alg === "none") {
|
|
290
|
+
throw new JwtError("alg_none_refused", 'jwt(): alg "none" cannot appear in the allowlist.');
|
|
291
|
+
}
|
|
292
|
+
if (!ALL_ALGS.has(alg)) {
|
|
293
|
+
throw new JwtError("invalid_alg", `jwt(): unknown algorithm "${String(alg)}" in allowlist.`);
|
|
294
|
+
}
|
|
295
|
+
allow.add(alg);
|
|
296
|
+
}
|
|
297
|
+
const hasSym = [...allow].some((a) => SYMMETRIC.has(a));
|
|
298
|
+
if (hasSym &&
|
|
299
|
+
opts.key instanceof Uint8Array &&
|
|
300
|
+
opts.key.byteLength < MIN_HS_KEY_BYTES) {
|
|
301
|
+
throw new JwtError("weak_hs_secret", `jwt(): HS* secret must be at least ${MIN_HS_KEY_BYTES} bytes (RFC 7518 §3.2); got ${opts.key.byteLength}.`);
|
|
302
|
+
}
|
|
303
|
+
if (hasSym &&
|
|
304
|
+
opts.refuseSymmetricWithJwk !== false &&
|
|
305
|
+
looksLikeJwkSource(opts.key)) {
|
|
306
|
+
throw new JwtError("sym_with_jwk_refused", "jwt(): symmetric algorithms (HS*) mixed with a JWK / JWKS key source are refused — this is the documented JWKS confused-deputy attack. Use asymmetric algorithms (RS/PS/ES/EdDSA), or pass refuseSymmetricWithJwk: false to override (not recommended).");
|
|
307
|
+
}
|
|
308
|
+
if (opts.clockSkewSeconds !== undefined) {
|
|
309
|
+
if (typeof opts.clockSkewSeconds !== "number" || !Number.isFinite(opts.clockSkewSeconds) || opts.clockSkewSeconds < 0) {
|
|
310
|
+
throw new JwtError("invalid_clock_skew", "jwt(): clockSkewSeconds must be a non-negative finite number.");
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const issuers = normalizeStringSet(opts.issuer);
|
|
314
|
+
const audiences = normalizeStringSet(opts.audience);
|
|
315
|
+
const keyCache = new Map();
|
|
316
|
+
async function resolveKey(header) {
|
|
317
|
+
const algRaw = header.alg;
|
|
318
|
+
if (typeof algRaw !== "string" || !allow.has(algRaw)) {
|
|
319
|
+
throw new JwtError("alg_not_allowed", `jwt(): token alg "${String(algRaw)}" is not in the allowlist.`);
|
|
320
|
+
}
|
|
321
|
+
const alg = algRaw;
|
|
322
|
+
const material = typeof opts.key === "function" ? await opts.key(header) : opts.key;
|
|
323
|
+
if (typeof opts.key !== "function") {
|
|
324
|
+
const cacheKey = alg;
|
|
325
|
+
const cached = keyCache.get(cacheKey);
|
|
326
|
+
if (cached)
|
|
327
|
+
return cached;
|
|
328
|
+
const imported = await importKey(alg, material, "verify");
|
|
329
|
+
keyCache.set(cacheKey, imported);
|
|
330
|
+
return imported;
|
|
331
|
+
}
|
|
332
|
+
return importKey(alg, material, "verify");
|
|
333
|
+
}
|
|
334
|
+
const resolved = {
|
|
335
|
+
algorithms: allow,
|
|
336
|
+
resolveKey,
|
|
337
|
+
issuers,
|
|
338
|
+
audiences,
|
|
339
|
+
clockSkewSeconds: opts.clockSkewSeconds ?? 0,
|
|
340
|
+
now: opts.now ?? (() => Math.floor(Date.now() / 1000)),
|
|
341
|
+
};
|
|
342
|
+
return {
|
|
343
|
+
async verify(token) {
|
|
344
|
+
return verifyInternal(token, resolved);
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
async function verifyInternal(token, r) {
|
|
349
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
350
|
+
throw new JwtError("invalid_token", "jwt(): token must be a non-empty string.");
|
|
351
|
+
}
|
|
352
|
+
const parts = token.split(".");
|
|
353
|
+
if (parts.length !== 3) {
|
|
354
|
+
throw new JwtError("invalid_token", "jwt(): token must have three dot-separated segments.");
|
|
355
|
+
}
|
|
356
|
+
const [headerB64, payloadB64, sigB64] = parts;
|
|
357
|
+
let header;
|
|
358
|
+
let payload;
|
|
359
|
+
try {
|
|
360
|
+
header = JSON.parse(DEC.decode(b64urlDecode(headerB64)));
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
if (err instanceof JwtError)
|
|
364
|
+
throw err;
|
|
365
|
+
throw new JwtError("invalid_token", "jwt(): header is not valid JSON.");
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
payload = JSON.parse(DEC.decode(b64urlDecode(payloadB64)));
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
if (err instanceof JwtError)
|
|
372
|
+
throw err;
|
|
373
|
+
throw new JwtError("invalid_token", "jwt(): payload is not valid JSON.");
|
|
374
|
+
}
|
|
375
|
+
if (!isJsonObject(header) || !isJsonObject(payload)) {
|
|
376
|
+
throw new JwtError("invalid_token", "jwt(): header and payload must be JSON objects.");
|
|
377
|
+
}
|
|
378
|
+
const algRaw = header.alg;
|
|
379
|
+
if (algRaw === "none") {
|
|
380
|
+
throw new JwtError("alg_none_refused", 'jwt(): token alg "none" is refused.');
|
|
381
|
+
}
|
|
382
|
+
if (typeof algRaw !== "string" || !r.algorithms.has(algRaw)) {
|
|
383
|
+
throw new JwtError("alg_not_allowed", `jwt(): token alg "${String(algRaw)}" is not in the allowlist.`);
|
|
384
|
+
}
|
|
385
|
+
const alg = algRaw;
|
|
386
|
+
const key = await r.resolveKey(header);
|
|
387
|
+
const sig = b64urlDecode(sigB64);
|
|
388
|
+
const signingInput = ENC.encode(`${headerB64}.${payloadB64}`);
|
|
389
|
+
let ok;
|
|
390
|
+
try {
|
|
391
|
+
ok = await getCrypto().subtle.verify(buildSignAlgorithm(alg), key, sig, signingInput);
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
throw new JwtError("invalid_signature", "jwt(): signature verification threw.");
|
|
395
|
+
}
|
|
396
|
+
if (!ok) {
|
|
397
|
+
throw new JwtError("invalid_signature", "jwt(): signature verification failed.");
|
|
398
|
+
}
|
|
399
|
+
const now = r.now();
|
|
400
|
+
try {
|
|
401
|
+
assertTemporalClaims(payload, {
|
|
402
|
+
now,
|
|
403
|
+
clockSkewSeconds: r.clockSkewSeconds,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
if (err instanceof TemporalClaimError) {
|
|
408
|
+
throw new JwtError(err.code, `jwt(): ${err.message}`);
|
|
409
|
+
}
|
|
410
|
+
throw err;
|
|
411
|
+
}
|
|
412
|
+
if (r.issuers) {
|
|
413
|
+
const iss = payload.iss;
|
|
414
|
+
if (typeof iss !== "string" || !r.issuers.has(iss)) {
|
|
415
|
+
throw new JwtError("invalid_issuer", "jwt(): payload.iss does not match the expected issuer(s).");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (r.audiences) {
|
|
419
|
+
const aud = payload.aud;
|
|
420
|
+
if (typeof aud === "string") {
|
|
421
|
+
if (!r.audiences.has(aud)) {
|
|
422
|
+
throw new JwtError("invalid_audience", "jwt(): payload.aud does not match the expected audience(s).");
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (Array.isArray(aud)) {
|
|
426
|
+
const matched = aud.some((a) => typeof a === "string" && r.audiences.has(a));
|
|
427
|
+
if (!matched) {
|
|
428
|
+
throw new JwtError("invalid_audience", "jwt(): payload.aud does not match the expected audience(s).");
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
throw new JwtError("invalid_audience", "jwt(): payload.aud is missing or not a string / string[].");
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return { header, payload };
|
|
436
|
+
}
|
|
437
|
+
//# sourceMappingURL=jwt.js.map
|
package/dist/jwt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wave 4 leftover: `loadShedding()` primitive.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight Node-process pressure monitor that returns `Hooks` for
|
|
5
|
+
* `app.use(...)`. When any of the configured thresholds is exceeded,
|
|
6
|
+
* every incoming request is short-circuited with `503 Service Unavailable`
|
|
7
|
+
* carrying a `Retry-After` header. First-party answer to
|
|
8
|
+
* `@fastify/under-pressure`. Defaults are off for heap/RSS thresholds
|
|
9
|
+
* (deployment-specific) and conservative for everything else (event-loop
|
|
10
|
+
* delay at 1 s, event-loop utilization at 0.98). The monitor samples at
|
|
11
|
+
* `sampleIntervalMs` (default 1 s). Graceful no-op on runtimes without
|
|
12
|
+
* `node:perf_hooks` (Workers / Edge / Fastly Compute).
|
|
13
|
+
*
|
|
14
|
+
* @since 0.20.0
|
|
15
|
+
*/
|
|
16
|
+
import type { Hooks } from "./types.js";
|
|
17
|
+
/** Configuration accepted by {@link loadShedding}. Every field is optional. */
|
|
18
|
+
export interface LoadSheddingOptions {
|
|
19
|
+
/** Max tolerated event-loop delay (ms). Default `1000`. `0` disables. */
|
|
20
|
+
maxEventLoopDelayMs?: number;
|
|
21
|
+
/** Max tolerated `heapUsed` bytes. Off by default. */
|
|
22
|
+
maxHeapUsedBytes?: number;
|
|
23
|
+
/** Max tolerated `rss` bytes. Off by default. */
|
|
24
|
+
maxRssBytes?: number;
|
|
25
|
+
/** Max tolerated event-loop utilization (`[0, 1]`). Default `0.98`. `0` disables. */
|
|
26
|
+
maxEventLoopUtilization?: number;
|
|
27
|
+
/** Sampling interval (ms). Default `1000`; clamped to at least `100`. */
|
|
28
|
+
sampleIntervalMs?: number;
|
|
29
|
+
/** `Retry-After` seconds on the shedded `503`. Default `10`. */
|
|
30
|
+
retryAfterSeconds?: number;
|
|
31
|
+
/** Optional custom check; truthy return value becomes a shed reason. */
|
|
32
|
+
healthCheck?: () => string | undefined | Promise<string | undefined>;
|
|
33
|
+
/** Interval for `healthCheck` (ms). Defaults to `sampleIntervalMs`. */
|
|
34
|
+
healthCheckIntervalMs?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Snapshot returned by the internal pressure sampler. Exposed for tests.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export interface LoadSheddingSnapshot {
|
|
42
|
+
eventLoopDelayMs: number;
|
|
43
|
+
heapUsedBytes: number;
|
|
44
|
+
rssBytes: number;
|
|
45
|
+
eventLoopUtilization: number;
|
|
46
|
+
reason?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build a Hooks bundle that refuses requests with `503` when the configured
|
|
50
|
+
* process-pressure thresholds are exceeded.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { App, loadShedding } from "@daloyjs/core";
|
|
55
|
+
*
|
|
56
|
+
* const app = new App();
|
|
57
|
+
* app.use(loadShedding({
|
|
58
|
+
* maxEventLoopDelayMs: 500,
|
|
59
|
+
* maxHeapUsedBytes: 1.5 * 1024 ** 3,
|
|
60
|
+
* retryAfterSeconds: 5,
|
|
61
|
+
* }));
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function loadShedding(opts?: LoadSheddingOptions): Hooks;
|
|
65
|
+
/**
|
|
66
|
+
* Read-only accessor for tests + observability plugins that want to know
|
|
67
|
+
* what the latest pressure sample looked like. Returns `undefined` if the
|
|
68
|
+
* given hooks object was not produced by {@link loadShedding}.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export declare const LOAD_SHEDDING_MARKER: unique symbol;
|
|
73
|
+
//# sourceMappingURL=load-shedding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-shedding.d.ts","sourceRoot":"","sources":["../src/load-shedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACrE,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IAAG,gBAAgB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAAE;AAiB3J;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,KAAK,CAkIlE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAAO,MAEzC,CAAC"}
|