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.
Files changed (53) hide show
  1. package/README.md +3 -9
  2. package/dist/_chunks/{_import-key.mjs → algorithms.mjs} +194 -399
  3. package/dist/_chunks/{_codec-jwe.d.mts → codec-jwe.d.mts} +40 -9
  4. package/dist/_chunks/{_codec-jws.mjs → codec-jws.mjs} +133 -80
  5. package/dist/_chunks/cookie.mjs +50 -0
  6. package/dist/_chunks/digest.mjs +10 -3
  7. package/dist/_chunks/error.d.mts +103 -0
  8. package/dist/_chunks/export.mjs +97 -0
  9. package/dist/_chunks/index.d.mts +5 -103
  10. package/dist/_chunks/index2.d.mts +206 -12
  11. package/dist/_chunks/index3.d.mts +12 -109
  12. package/dist/_chunks/{jwe.d.mts → index4.d.mts} +2 -1
  13. package/dist/_chunks/{jws.d.mts → index5.d.mts} +2 -1
  14. package/dist/_chunks/index6.d.mts +113 -0
  15. package/dist/_chunks/jwe.mjs +205 -72
  16. package/dist/{core → _chunks}/jwk.mjs +210 -495
  17. package/dist/_chunks/jws.mjs +280 -193
  18. package/dist/_chunks/jwt.d.mts +2 -1
  19. package/dist/_chunks/{_jwt-internal.mjs → jwt.mjs} +110 -54
  20. package/dist/_chunks/libs/unsecure.d.mts +35 -25
  21. package/dist/_chunks/libs/unsecure.mjs +101 -268
  22. package/dist/_chunks/{_internal.d.mts → limits.d.mts} +1 -1
  23. package/dist/_chunks/payload.mjs +23 -0
  24. package/dist/_chunks/wrap.mjs +209 -0
  25. package/dist/adapters/elysia/index.d.mts +4 -4
  26. package/dist/adapters/elysia/index.mjs +4 -3
  27. package/dist/adapters/h3/index.d.mts +13 -13
  28. package/dist/adapters/h3/index.mjs +8 -11
  29. package/dist/adapters/h3v1/index.d.mts +5 -5
  30. package/dist/adapters/h3v1/index.mjs +6 -9
  31. package/dist/core/internal.mjs +1 -1
  32. package/dist/core/{jwe.d.mts → jwe/index.d.mts} +5 -4
  33. package/dist/core/jwe/index.mjs +5 -0
  34. package/dist/core/{jwk.d.mts → jwk/index.d.mts} +5 -4
  35. package/dist/core/jwk/index.mjs +7 -0
  36. package/dist/core/{jws.d.mts → jws/index.d.mts} +5 -4
  37. package/dist/core/jws/index.mjs +5 -0
  38. package/dist/dpop/index.d.mts +73 -0
  39. package/dist/dpop/index.mjs +296 -0
  40. package/dist/error.d.mts +3 -0
  41. package/dist/error.mjs +20 -0
  42. package/dist/index.d.mts +10 -9
  43. package/dist/index.mjs +7 -4
  44. package/dist/session/index.d.mts +4 -4
  45. package/dist/session/index.mjs +6 -47
  46. package/dist/utils/index.d.mts +7 -0
  47. package/dist/{core/utils → utils}/index.mjs +4 -3
  48. package/package.json +21 -10
  49. package/dist/_chunks/jwk.d.mts +0 -205
  50. package/dist/core/jwe.mjs +0 -5
  51. package/dist/core/jws.mjs +0 -5
  52. package/dist/core/utils/index.d.mts +0 -6
  53. /package/dist/_chunks/{_cookie-attributes.d.mts → cookie-attributes.d.mts} +0 -0
@@ -1,43 +1,86 @@
1
- import { UnsecureError, base64Parse as base64Parse$1, base64Stringify as base64Stringify$1, safeJsonParse, textEncoder } from "./libs/unsecure.mjs";
2
- var JWTError = class extends Error {
3
- code;
4
- cause;
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 isUnsecureError(error, code) {
17
- if (!(error instanceof UnsecureError)) return false;
18
- return code === void 0 || error.code === code;
6
+ function base64Stringify(data) {
7
+ return encode(bytesOf(data, "Base64"), "base64");
19
8
  }
20
- function base64UrlStringify(data) {
21
- return base64Stringify$1(data, { alphabet: "base64url" });
9
+ function base64UrlParse(text, options) {
10
+ return parse(text, URL, false, "Base64Url", options);
22
11
  }
23
- function base64UrlParse(input, options) {
24
- const parseOptions = {
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
- function base64Stringify(data) {
32
- return base64Stringify$1(data);
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 base64Parse(input, options) {
35
- const parseOptions = {
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" || "d" in key && typeof key.d === "string") return false;
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 (${(/* @__PURE__ */ new Date(exp * 1e3)).toISOString()}).`;
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 = Math.round((options.currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
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 (${(/* @__PURE__ */ new Date(jwtClaims.nbf * 1e3)).toISOString()}).`, "ERR_JWT_NBF");
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 (${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()}).`, "ERR_JWT_CLAIM_INVALID");
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.round((currentDate ?? /* @__PURE__ */ new Date()).getTime() / 1e3);
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 "${param}" is missing from the protected header.`, `ERR_${family}_INVALID`);
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 { JWTError, applyTypDefault, assertClaimValidationOptions, assertCritShape, assertCryptoKey, assertPlainObject, assertStringList, base64Decode, base64Encode, base64Parse, base64Stringify, base64UrlDecode, base64UrlEncode, base64UrlParse, base64UrlStringify, computeDurationInSeconds, computeJwtTimeClaims, concatUint8Arrays, expiredClaimMessage, getPlaintextBytes, isAsymmetricJWK, isCryptoKey, isCryptoKeyPair, isJWK, isJWKSet, isJWTContent, isJWTError, isPlainObject, isPrivateJWK, isPublicJWK, isSymmetricJWK, isUnsecureError, maybeArray, parsePayloadBytes, parsePayloadSegment, parsePayloadText, uint32be, validateCriticalHeadersJWE, validateCriticalHeadersJWS, validateJwtClaims };
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, enumerable or not. Cycle-safe.
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
- * `Object.prototype` or on `null`; copies of both are rooted on
123
- * `Object.prototype`. Every other value — `Date`, `Map`, `Set`, typed
124
- * arrays, `RegExp`, class instances, functions — is carried into the copy by
125
- * reference, exactly as {@link sanitizeObject} leaves it in place. By
126
- * reference means unchanged *and* unsanitized: what a `Map` or `Set` holds is
127
- * never read by either function.
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
- * Non-object / undefined inputs — and objects that are not arrays or plain
139
- * objects — are returned unchanged.
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 extends Record<string, unknown> | undefined>(obj: T): 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 {};