unjwt 0.8.4 → 0.8.5
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 -9
- package/dist/_chunks/{_import-key.mjs → algorithms.mjs} +194 -399
- package/dist/_chunks/{_codec-jwe.d.mts → codec-jwe.d.mts} +40 -9
- package/dist/_chunks/{_codec-jws.mjs → codec-jws.mjs} +133 -80
- package/dist/_chunks/cookie.mjs +50 -0
- package/dist/_chunks/digest.mjs +10 -3
- package/dist/_chunks/error.d.mts +103 -0
- package/dist/_chunks/export.mjs +97 -0
- package/dist/_chunks/index.d.mts +5 -103
- package/dist/_chunks/index2.d.mts +206 -12
- package/dist/_chunks/index3.d.mts +12 -109
- package/dist/_chunks/{jwe.d.mts → index4.d.mts} +2 -1
- package/dist/_chunks/{jws.d.mts → index5.d.mts} +2 -1
- package/dist/_chunks/index6.d.mts +113 -0
- package/dist/_chunks/jwe.mjs +205 -72
- package/dist/{core → _chunks}/jwk.mjs +210 -495
- package/dist/_chunks/jws.mjs +280 -193
- package/dist/_chunks/jwt.d.mts +2 -1
- package/dist/_chunks/{_jwt-internal.mjs → jwt.mjs} +110 -54
- package/dist/_chunks/libs/unsecure.d.mts +35 -25
- package/dist/_chunks/libs/unsecure.mjs +101 -268
- package/dist/_chunks/{_internal.d.mts → limits.d.mts} +1 -1
- package/dist/_chunks/payload.mjs +23 -0
- package/dist/_chunks/wrap.mjs +209 -0
- package/dist/adapters/elysia/index.d.mts +4 -4
- package/dist/adapters/elysia/index.mjs +4 -3
- package/dist/adapters/h3/index.d.mts +13 -13
- package/dist/adapters/h3/index.mjs +8 -11
- package/dist/adapters/h3v1/index.d.mts +5 -5
- package/dist/adapters/h3v1/index.mjs +6 -9
- package/dist/core/internal.mjs +1 -1
- package/dist/core/{jwe.d.mts → jwe/index.d.mts} +5 -4
- package/dist/core/jwe/index.mjs +5 -0
- package/dist/core/{jwk.d.mts → jwk/index.d.mts} +5 -4
- package/dist/core/jwk/index.mjs +7 -0
- package/dist/core/{jws.d.mts → jws/index.d.mts} +5 -4
- package/dist/core/jws/index.mjs +5 -0
- package/dist/dpop/index.d.mts +73 -0
- package/dist/dpop/index.mjs +296 -0
- package/dist/error.d.mts +3 -0
- package/dist/error.mjs +20 -0
- package/dist/index.d.mts +10 -9
- package/dist/index.mjs +7 -4
- package/dist/session/index.d.mts +4 -4
- package/dist/session/index.mjs +6 -47
- package/dist/utils/index.d.mts +7 -0
- package/dist/{core/utils → utils}/index.mjs +4 -3
- package/package.json +21 -10
- package/dist/_chunks/jwk.d.mts +0 -205
- package/dist/core/jwe.mjs +0 -5
- package/dist/core/jws.mjs +0 -5
- package/dist/core/utils/index.d.mts +0 -6
- /package/dist/_chunks/{_cookie-attributes.d.mts → cookie-attributes.d.mts} +0 -0
|
@@ -1,43 +1,86 @@
|
|
|
1
|
-
import { UnsecureError,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
constructor(message, code, cause) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = "JWTError";
|
|
8
|
-
this.code = code;
|
|
9
|
-
this.cause = cause;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
function isJWTError(error, code) {
|
|
13
|
-
if (!(error instanceof JWTError)) return false;
|
|
14
|
-
return code === void 0 || error.code === code;
|
|
1
|
+
import { UnsecureError, safeJsonParse, textEncoder } from "./libs/unsecure.mjs";
|
|
2
|
+
import { JWTError } from "../error.mjs";
|
|
3
|
+
function base64UrlStringify(data) {
|
|
4
|
+
return encode(bytesOf(data, "Base64Url"), "base64url");
|
|
15
5
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
return code === void 0 || error.code === code;
|
|
6
|
+
function base64Stringify(data) {
|
|
7
|
+
return encode(bytesOf(data, "Base64"), "base64");
|
|
19
8
|
}
|
|
20
|
-
function
|
|
21
|
-
return
|
|
9
|
+
function base64UrlParse(text, options) {
|
|
10
|
+
return parse(text, URL, false, "Base64Url", options);
|
|
22
11
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
...options,
|
|
26
|
-
alphabet: "base64url",
|
|
27
|
-
padding: false
|
|
28
|
-
};
|
|
29
|
-
return typeof input === "string" ? base64Parse$1(input, parseOptions) : base64Parse$1(input, parseOptions);
|
|
12
|
+
function base64Parse(text, options) {
|
|
13
|
+
return parse(text, STD, true, "Base64", options);
|
|
30
14
|
}
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
const URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
16
|
+
const STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
17
|
+
const fatalDecoder = /* @__PURE__ */ new TextDecoder("utf-8", {
|
|
18
|
+
ignoreBOM: true,
|
|
19
|
+
fatal: true
|
|
20
|
+
});
|
|
21
|
+
function bytesOf(data, label) {
|
|
22
|
+
if (typeof data === "string") return textEncoder.encode(data);
|
|
23
|
+
if (data instanceof Uint8Array) return data;
|
|
24
|
+
throw new UnsecureError("INVALID_TYPE", `${label}.stringify: expected a string or Uint8Array.`);
|
|
25
|
+
}
|
|
26
|
+
function encode(bytes, name) {
|
|
27
|
+
const buffer = globalThis.Buffer;
|
|
28
|
+
if (typeof buffer?.from === "function") return buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString(name);
|
|
29
|
+
if ("toBase64" in Uint8Array.prototype) return (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes : bytes.slice()).toBase64({
|
|
30
|
+
alphabet: name,
|
|
31
|
+
omitPadding: name === "base64url"
|
|
32
|
+
});
|
|
33
|
+
const alphabet = name === "base64url" ? URL : STD;
|
|
34
|
+
const padded = name === "base64";
|
|
35
|
+
let out = "";
|
|
36
|
+
let i = 0;
|
|
37
|
+
for (; i + 2 < bytes.length; i += 3) {
|
|
38
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2];
|
|
39
|
+
out += alphabet[n >>> 18] + alphabet[n >>> 12 & 63] + alphabet[n >>> 6 & 63] + alphabet[n & 63];
|
|
40
|
+
}
|
|
41
|
+
const rem = bytes.length - i;
|
|
42
|
+
if (rem === 1) {
|
|
43
|
+
const n = bytes[i] << 16;
|
|
44
|
+
out += alphabet[n >>> 18] + alphabet[n >>> 12 & 63] + (padded ? "==" : "");
|
|
45
|
+
} else if (rem === 2) {
|
|
46
|
+
const n = bytes[i] << 16 | bytes[i + 1] << 8;
|
|
47
|
+
out += alphabet[n >>> 18] + alphabet[n >>> 12 & 63] + alphabet[n >>> 6 & 63] + (padded ? "=" : "");
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function parse(text, alphabet, padded, label, options) {
|
|
52
|
+
if (typeof text !== "string") throw new UnsecureError("INVALID_TYPE", `${label}.parse: expected a string, got ${typeof text}.`);
|
|
53
|
+
const wantString = options?.returnAs === void 0 || options.returnAs === "string";
|
|
54
|
+
let end = text.length;
|
|
55
|
+
while (end > 0 && text.charCodeAt(end - 1) === 61) end--;
|
|
56
|
+
const rem = end % 4;
|
|
57
|
+
if (rem === 1) throw malformed(label, `${end} symbols cannot encode whole bytes.`);
|
|
58
|
+
const padNeeded = padded && rem !== 0 ? 4 - rem : 0;
|
|
59
|
+
if (text.length - end !== padNeeded) throw malformed(label, `expected ${padNeeded} "=" padding characters, found ${text.length - end}.`);
|
|
60
|
+
const out = new Uint8Array(end * 6 >>> 3);
|
|
61
|
+
let acc = 0;
|
|
62
|
+
let held = 0;
|
|
63
|
+
let j = 0;
|
|
64
|
+
for (let i = 0; i < end; i++) {
|
|
65
|
+
const value = alphabet.indexOf(text[i]);
|
|
66
|
+
if (value < 0) throw malformed(label, `invalid character ${JSON.stringify(text[i])} at index ${i}.`);
|
|
67
|
+
acc = acc << 6 | value;
|
|
68
|
+
held += 6;
|
|
69
|
+
if (held >= 8) {
|
|
70
|
+
held -= 8;
|
|
71
|
+
out[j++] = acc >>> held & 255;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (held !== 0 && (acc & (1 << held) - 1) !== 0) throw malformed(label, "the last symbol sets bits past the final byte.");
|
|
75
|
+
if (!wantString) return out;
|
|
76
|
+
try {
|
|
77
|
+
return fatalDecoder.decode(out);
|
|
78
|
+
} catch {
|
|
79
|
+
throw malformed(label, "decoded bytes are not valid UTF-8.");
|
|
80
|
+
}
|
|
33
81
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
...options,
|
|
37
|
-
alphabet: "base64",
|
|
38
|
-
padding: true
|
|
39
|
-
};
|
|
40
|
-
return typeof input === "string" ? base64Parse$1(input, parseOptions) : base64Parse$1(input, parseOptions);
|
|
82
|
+
function malformed(label, detail) {
|
|
83
|
+
return new UnsecureError("MALFORMED", `${label}.parse: ${detail}`);
|
|
41
84
|
}
|
|
42
85
|
const base64UrlEncode = base64UrlStringify;
|
|
43
86
|
const base64UrlDecode = base64UrlParse;
|
|
@@ -75,11 +118,20 @@ function isPrivateJWK(key) {
|
|
|
75
118
|
return "d" in key && typeof key.d === "string";
|
|
76
119
|
}
|
|
77
120
|
function isPublicJWK(key) {
|
|
78
|
-
if (!isJWK(key) || key.kty === "oct" ||
|
|
121
|
+
if (!isJWK(key) || key.kty === "oct" || Object.hasOwn(key, "k")) return false;
|
|
122
|
+
if ("d" in key && typeof key.d === "string") return false;
|
|
79
123
|
if (key.kty === "EC") return typeof key.x === "string" && typeof key.y === "string";
|
|
80
124
|
if (key.kty === "OKP") return typeof key.x === "string";
|
|
81
125
|
return typeof key.n === "string" && typeof key.e === "string";
|
|
82
126
|
}
|
|
127
|
+
function hasSymmetricSecret(key) {
|
|
128
|
+
return key.kty === "oct" || Object.hasOwn(key, "k");
|
|
129
|
+
}
|
|
130
|
+
const MAX_QUOTED_VALUE = 64;
|
|
131
|
+
function quoteTokenValue(value) {
|
|
132
|
+
const text = String(value);
|
|
133
|
+
return JSON.stringify(text.length > MAX_QUOTED_VALUE ? `${text.slice(0, MAX_QUOTED_VALUE)}…` : text);
|
|
134
|
+
}
|
|
83
135
|
function concatUint8Arrays(...arrays) {
|
|
84
136
|
const totalLength = arrays.reduce((length, arr) => length + arr.length, 0);
|
|
85
137
|
const result = new Uint8Array(totalLength);
|
|
@@ -155,12 +207,12 @@ function computeDurationInSeconds(duration) {
|
|
|
155
207
|
return seconds;
|
|
156
208
|
}
|
|
157
209
|
function expiredClaimMessage(exp) {
|
|
158
|
-
return `JWT "exp" is in the past (${(
|
|
210
|
+
return `JWT "exp" is in the past (${_formatNumericDate(exp)}).`;
|
|
159
211
|
}
|
|
160
212
|
function validateJwtClaims(jwtClaims, options = {}) {
|
|
161
213
|
assertClaimValidationOptions(options);
|
|
162
214
|
const clockTolerance = _normalizeClockTolerance(options.clockTolerance);
|
|
163
|
-
const currentTime =
|
|
215
|
+
const currentTime = (options.currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
164
216
|
if (options.requiredClaims !== void 0 || options.issuer !== void 0 || options.audience !== void 0 || options.subject !== void 0 || options.maxTokenAge !== void 0) {
|
|
165
217
|
const allRequiredClaims = new Set(options.requiredClaims);
|
|
166
218
|
if (options.issuer !== void 0) allRequiredClaims.add("iss");
|
|
@@ -184,20 +236,12 @@ function validateJwtClaims(jwtClaims, options = {}) {
|
|
|
184
236
|
const claimAudience = maybeArray(jwtClaims.aud || []);
|
|
185
237
|
if (!claimAudience.some((aud) => expectedAudiences.includes(aud))) throw new JWTError(`Invalid JWT "aud": expected ${expectedAudiences.join(" or ")}, got ${claimAudience.join(", ")}`, "ERR_JWT_CLAIM_INVALID");
|
|
186
238
|
}
|
|
187
|
-
if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) throw new JWTError(`JWT "nbf" is in the future (${(
|
|
188
|
-
if (typeof jwtClaims.exp === "number" && jwtClaims.exp <= currentTime - clockTolerance) throw new JWTError(expiredClaimMessage(jwtClaims.exp), "ERR_JWT_EXPIRED",
|
|
189
|
-
jti: jwtClaims.jti,
|
|
190
|
-
iat: jwtClaims.iat,
|
|
191
|
-
exp: jwtClaims.exp
|
|
192
|
-
});
|
|
239
|
+
if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) throw new JWTError(`JWT "nbf" is in the future (${_formatNumericDate(jwtClaims.nbf)}).`, "ERR_JWT_NBF");
|
|
240
|
+
if (typeof jwtClaims.exp === "number" && jwtClaims.exp <= currentTime - clockTolerance) throw new JWTError(expiredClaimMessage(jwtClaims.exp), "ERR_JWT_EXPIRED", _expiredCause(jwtClaims));
|
|
193
241
|
if (options.maxTokenAge !== void 0) {
|
|
194
242
|
if (typeof jwtClaims.iat !== "number") throw new JWTError("JWT \"iat\" must be a number when maxTokenAge is set.", "ERR_JWT_CLAIM_INVALID");
|
|
195
|
-
if (jwtClaims.iat > currentTime + clockTolerance) throw new JWTError(`JWT "iat" is in the future (${(
|
|
196
|
-
if (jwtClaims.iat < currentTime - computeDurationInSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" is older than maxTokenAge ${options.maxTokenAge}.`, "ERR_JWT_EXPIRED",
|
|
197
|
-
jti: jwtClaims.jti,
|
|
198
|
-
iat: jwtClaims.iat,
|
|
199
|
-
exp: jwtClaims.exp
|
|
200
|
-
});
|
|
243
|
+
if (jwtClaims.iat > currentTime + clockTolerance) throw new JWTError(`JWT "iat" is in the future (${_formatNumericDate(jwtClaims.iat)}).`, "ERR_JWT_CLAIM_INVALID");
|
|
244
|
+
if (jwtClaims.iat < currentTime - computeDurationInSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" is older than maxTokenAge ${options.maxTokenAge}.`, "ERR_JWT_EXPIRED", _expiredCause(jwtClaims));
|
|
201
245
|
}
|
|
202
246
|
}
|
|
203
247
|
function assertClaimValidationOptions(options) {
|
|
@@ -243,6 +287,18 @@ function _assertNumericDate(value, claim) {
|
|
|
243
287
|
if (typeof value !== "number" || !Number.isFinite(value)) throw new JWTError(`JWT "${claim}" must be a number.`, "ERR_JWT_CLAIM_INVALID");
|
|
244
288
|
if (value < 0) throw new JWTError(`JWT "${claim}" must be a non-negative NumericDate.`, "ERR_JWT_CLAIM_INVALID");
|
|
245
289
|
}
|
|
290
|
+
function _formatNumericDate(seconds) {
|
|
291
|
+
const date = /* @__PURE__ */ new Date(seconds * 1e3);
|
|
292
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : String(seconds);
|
|
293
|
+
}
|
|
294
|
+
function _expiredCause(claims) {
|
|
295
|
+
const { jti, iat, exp } = claims;
|
|
296
|
+
return {
|
|
297
|
+
jti: typeof jti === "string" ? jti : void 0,
|
|
298
|
+
iat,
|
|
299
|
+
exp
|
|
300
|
+
};
|
|
301
|
+
}
|
|
246
302
|
function applyTypDefault(header, payload) {
|
|
247
303
|
const declared = header.typ;
|
|
248
304
|
if (declared !== void 0 && typeof declared !== "string") throw new JWTError("\"typ\" must be a string.", "ERR_INVALID_OPTION");
|
|
@@ -296,7 +352,7 @@ function computeJwtTimeClaims(payload, options) {
|
|
|
296
352
|
const wantsNbf = hasNbfHint && (!("nbf" in payload) || payload.nbf === void 0);
|
|
297
353
|
const wantsIat = hasIatHint && (!("iat" in payload) || payload.iat === void 0);
|
|
298
354
|
if (!wantsExp && !wantsNbf && !wantsIat) return void 0;
|
|
299
|
-
const now = Math.
|
|
355
|
+
const now = Math.floor((currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
|
|
300
356
|
const claims = { ...payload };
|
|
301
357
|
claims.iat ||= issuedAt instanceof Date ? Math.floor(_instantOf(issuedAt, "issuedAt") / 1e3) : now;
|
|
302
358
|
if (wantsExp) {
|
|
@@ -384,8 +440,8 @@ function validateCriticalHeaders(protectedHeader, options) {
|
|
|
384
440
|
const { family, recognizedHeaders = [] } = options;
|
|
385
441
|
assertCritShape(crit, { family });
|
|
386
442
|
for (const param of crit) {
|
|
387
|
-
if (!Object.hasOwn(protectedHeader, param)) throw new JWTError(`Critical header parameter
|
|
388
|
-
if (!recognizedHeaders.includes(param) && !BASE_PROCESSED[family].includes(param)) throw new JWTError(`Unprocessed critical header parameters: ${param}`, `ERR_${family}_INVALID`);
|
|
443
|
+
if (!Object.hasOwn(protectedHeader, param)) throw new JWTError(`Critical header parameter ${quoteTokenValue(param)} is missing from the protected header.`, `ERR_${family}_INVALID`);
|
|
444
|
+
if (!recognizedHeaders.includes(param) && !BASE_PROCESSED[family].includes(param)) throw new JWTError(`Unprocessed critical header parameters: ${quoteTokenValue(param)}`, `ERR_${family}_INVALID`);
|
|
389
445
|
}
|
|
390
446
|
}
|
|
391
|
-
export {
|
|
447
|
+
export { applyTypDefault, assertClaimValidationOptions, assertCritShape, assertCryptoKey, assertPlainObject, assertStringList, base64Decode, base64Encode, base64Parse, base64Stringify, base64UrlDecode, base64UrlEncode, base64UrlParse, base64UrlStringify, computeDurationInSeconds, computeJwtTimeClaims, concatUint8Arrays, expiredClaimMessage, getPlaintextBytes, hasSymmetricSecret, isAsymmetricJWK, isCryptoKey, isCryptoKeyPair, isJWK, isJWKSet, isJWTContent, isPlainObject, isPrivateJWK, isPublicJWK, isSymmetricJWK, maybeArray, parsePayloadBytes, parsePayloadSegment, parsePayloadText, quoteTokenValue, uint32be, validateCriticalHeadersJWE, validateCriticalHeadersJWS, validateJwtClaims };
|
|
@@ -92,7 +92,8 @@ export declare function safeJsonParse<T = any>(json: string): T;
|
|
|
92
92
|
* Remove prototype-pollution vectors from a plain record in-place.
|
|
93
93
|
*
|
|
94
94
|
* Strips own properties named `__proto__`, `prototype`, and `constructor`
|
|
95
|
-
* through nested objects and arrays
|
|
95
|
+
* through nested objects and arrays — a named property hung on an array
|
|
96
|
+
* included — enumerable or not. Cycle-safe.
|
|
96
97
|
*
|
|
97
98
|
* Values are read from their property descriptors — array elements included —
|
|
98
99
|
* so a getter is never invoked: an accessor is neither traversed nor removed
|
|
@@ -114,17 +115,36 @@ export declare function safeJsonParse<T = any>(json: string): T;
|
|
|
114
115
|
* one.
|
|
115
116
|
*/
|
|
116
117
|
export declare function sanitizeObject<T extends Record<string, unknown> | undefined>(obj: T): T;
|
|
118
|
+
/** Options for {@link sanitizeObjectCopy}. */
|
|
119
|
+
interface SanitizeCopyOptions {
|
|
120
|
+
/**
|
|
121
|
+
* Constructors whose instances the copy carries by reference, untouched.
|
|
122
|
+
* Everything the copy meets must be JSON-shaped — a plain object or an
|
|
123
|
+
* array — unless it is an instance (`instanceof`, so subclasses count) of
|
|
124
|
+
* one of these. By reference means unchanged *and* unsanitized: an instance
|
|
125
|
+
* listed here is trusted as the caller holds it.
|
|
126
|
+
*
|
|
127
|
+
* `instanceof` is bound to the realm the constructor came from, which is
|
|
128
|
+
* the realm the caller's own `Date` or `Map` belong to.
|
|
129
|
+
*/
|
|
130
|
+
allowlist?: Iterable<abstract new (...args: never[]) => object>;
|
|
131
|
+
}
|
|
117
132
|
/**
|
|
118
133
|
* Return a sanitized deep copy of `obj`. The input is never mutated.
|
|
119
134
|
*
|
|
120
135
|
* Dangerous keys (`__proto__`, `prototype`, `constructor`) are stripped from
|
|
121
136
|
* every array and plain object reached. A plain object is one rooted on
|
|
122
|
-
* `
|
|
123
|
-
* `
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
137
|
+
* `null` or on an `Object.prototype` — this realm's or another's, so a value
|
|
138
|
+
* parsed inside a `vm` context or a frame is rebuilt like one parsed here;
|
|
139
|
+
* copies of both are rooted on this realm's `Object.prototype`.
|
|
140
|
+
*
|
|
141
|
+
* Anything else — a `Date`, `Map`, `Set`, typed array, `RegExp` or class
|
|
142
|
+
* instance — is refused: such a value is not the JSON shape this copy exists
|
|
143
|
+
* for, and carrying it through silently would report a sanitized copy that
|
|
144
|
+
* holds an unexamined object. Name the constructors you trust in
|
|
145
|
+
* `allowlist`, and their instances are carried into the copy by reference,
|
|
146
|
+
* or use {@link sanitizeObject} to walk a live object graph in place.
|
|
147
|
+
* Functions are values, not containers, and are carried by reference.
|
|
128
148
|
*
|
|
129
149
|
* Only own enumerable data properties are copied — the JSON shape. Accessors
|
|
130
150
|
* are skipped rather than invoked, so the copy never runs caller code; an
|
|
@@ -135,10 +155,14 @@ export declare function sanitizeObject<T extends Record<string, unknown> | undef
|
|
|
135
155
|
* Cycle-safe: circular references in the input are preserved in the output
|
|
136
156
|
* (pointing at the copied node, not the original reference).
|
|
137
157
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
158
|
+
* Primitives and `undefined` are returned unchanged.
|
|
159
|
+
*
|
|
160
|
+
* @throws {UnsecureError} `INVALID_TYPE` if an object that is neither plain,
|
|
161
|
+
* an array nor an instance of an `allowlist` entry is
|
|
162
|
+
* met at any depth, or if `allowlist` holds something
|
|
163
|
+
* that is not a constructor.
|
|
140
164
|
*/
|
|
141
|
-
export declare function sanitizeObjectCopy<T
|
|
165
|
+
export declare function sanitizeObjectCopy<T>(obj: T, options?: SanitizeCopyOptions): T;
|
|
142
166
|
/**
|
|
143
167
|
* Shared UTF-8 `TextEncoder`. Encoding holds no state between calls, so a
|
|
144
168
|
* single instance serves every caller.
|
|
@@ -156,18 +180,4 @@ export type BytesSource = ArrayBufferLike | ArrayBufferView<ArrayBufferLike>;
|
|
|
156
180
|
* payload, not a signature to swallow.
|
|
157
181
|
*/
|
|
158
182
|
export declare const textDecoder: TextDecoder;
|
|
159
|
-
|
|
160
|
-
* Decoder output shape. Omit to mirror the input (`string` in → `string` out,
|
|
161
|
-
* `Uint8Array` in → `Uint8Array` out). `"bytes"` aliases `"uint8array"`.
|
|
162
|
-
*/
|
|
163
|
-
export type DecodeReturnAs = "string" | "uint8array" | "bytes";
|
|
164
|
-
/** Shared `parse` options. */
|
|
165
|
-
export interface DecodeOptions {
|
|
166
|
-
/** Output shape. Mirrors the input type when omitted. */
|
|
167
|
-
returnAs?: DecodeReturnAs;
|
|
168
|
-
/**
|
|
169
|
-
* Tolerate malformed input (skip/normalize) instead of throwing.
|
|
170
|
-
* @default false
|
|
171
|
-
*/
|
|
172
|
-
loose?: boolean;
|
|
173
|
-
}
|
|
183
|
+
export {};
|