@dbx-tools/shared-core 0.3.44 → 0.4.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/lib/index.d.ts +36 -0
- package/lib/index.js +30 -0
- package/lib/src/async.d.ts +152 -0
- package/lib/src/async.js +163 -0
- package/lib/src/brand.d.ts +94 -0
- package/lib/src/brand.js +123 -0
- package/lib/src/error.d.ts +76 -0
- package/lib/src/error.js +168 -0
- package/lib/src/function.d.ts +38 -0
- package/lib/src/function.js +44 -0
- package/lib/src/hash.d.ts +102 -0
- package/lib/src/hash.js +274 -0
- package/lib/src/http.d.ts +79 -0
- package/lib/src/http.js +189 -0
- package/lib/src/json.d.ts +48 -0
- package/lib/src/json.js +49 -0
- package/lib/src/log.d.ts +86 -0
- package/lib/src/log.js +352 -0
- package/lib/src/net.d.ts +246 -0
- package/lib/src/net.js +465 -0
- package/lib/src/object.d.ts +382 -0
- package/lib/src/object.js +749 -0
- package/lib/src/predicate.d.ts +81 -0
- package/lib/src/predicate.js +43 -0
- package/lib/src/string.d.ts +221 -0
- package/lib/src/string.js +509 -0
- package/lib/src/token.d.ts +30 -0
- package/lib/src/token.js +126 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +9 -5
package/lib/src/hash.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Short, deterministic non-cryptographic hashing and id minting.
|
|
3
|
+
*
|
|
4
|
+
* {@link fnvHash} / {@link fnvHashWithOptions} produce a stable FNV-1a
|
|
5
|
+
* digest over arbitrary structured input; {@link toBase32} encodes a
|
|
6
|
+
* 32-bit integer compactly; {@link id} mints v4 UUIDs (or short hex
|
|
7
|
+
* slices). All browser-safe - built on `globalThis.crypto`, no
|
|
8
|
+
* `node:crypto` import. **Never** use these for tokens, signatures, or
|
|
9
|
+
* anything an attacker shouldn't be able to forge.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Mint a v4 UUID, or a short hex slice of one when `length` is set.
|
|
15
|
+
*
|
|
16
|
+
* - `id()` returns a full RFC 4122 v4 UUID. Pick this when global
|
|
17
|
+
* uniqueness matters: long-running batches, ids that cross a storage /
|
|
18
|
+
* process boundary, anything that may collide across machines.
|
|
19
|
+
* - `id(length)` returns the first `length` hex chars of a fresh UUID
|
|
20
|
+
* with dashes stripped (e.g. `id(8) -> "a3f1c92b"`). Pick this when the
|
|
21
|
+
* id has to be short / typeable and the scope is bounded - cache keys
|
|
22
|
+
* local to a request, slug suffixes. `length <= 0` throws.
|
|
23
|
+
*
|
|
24
|
+
* Prefers `crypto.randomUUID()`, which covers Node (>= 19) and a browser on
|
|
25
|
+
* a secure origin. Browsers gate `randomUUID` behind a secure context, so a
|
|
26
|
+
* page served over plain http (a LAN dev host) has `crypto` but not that
|
|
27
|
+
* method; this package is browser-safe and its callers mint ids on the
|
|
28
|
+
* render path, so it degrades instead of throwing: `getRandomValues` when
|
|
29
|
+
* present, else `Math.random`. Every branch returns a well-formed v4 UUID -
|
|
30
|
+
* only the entropy source differs.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* id(); // "123e4567-e89b-12d3-a456-426614174000"
|
|
34
|
+
* id(8); // "a3f1c92b"
|
|
35
|
+
*/
|
|
36
|
+
export function id(length) {
|
|
37
|
+
if (length !== undefined && length <= 0) {
|
|
38
|
+
throw new Error("Length must be greater than 0");
|
|
39
|
+
}
|
|
40
|
+
const id = uuidV4();
|
|
41
|
+
if (length !== undefined) {
|
|
42
|
+
return id.replace(/-/g, "").slice(0, length);
|
|
43
|
+
}
|
|
44
|
+
return id;
|
|
45
|
+
}
|
|
46
|
+
/** A v4 UUID from the strongest randomness source this runtime offers. */
|
|
47
|
+
function uuidV4() {
|
|
48
|
+
const webCrypto = globalThis.crypto;
|
|
49
|
+
if (typeof webCrypto?.randomUUID === "function")
|
|
50
|
+
return webCrypto.randomUUID();
|
|
51
|
+
const bytes = new Uint8Array(16);
|
|
52
|
+
if (typeof webCrypto?.getRandomValues === "function") {
|
|
53
|
+
webCrypto.getRandomValues(bytes);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
for (let i = 0; i < bytes.length; i++)
|
|
57
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
58
|
+
}
|
|
59
|
+
// Stamp the version (4) and variant (10xx) fields RFC 4122 requires.
|
|
60
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
61
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
62
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
63
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Short, deterministic FNV-1a hash over one or more values. Wraps
|
|
67
|
+
* {@link fnvHashWithOptions} with all defaults: 6-char Crockford-style
|
|
68
|
+
* base-32 output (digits + lowercase, minus `i`/`l`/`o`/`u`).
|
|
69
|
+
* Browser-safe (no `node:crypto`).
|
|
70
|
+
*
|
|
71
|
+
* Accepts any mix of primitives, arrays, plain objects, `Map`s, and
|
|
72
|
+
* `Set`s; nested structures are walked deterministically so the hash is
|
|
73
|
+
* order-stable for objects / maps / sets and order-sensitive for arrays.
|
|
74
|
+
* Cycles are detected and folded into a `circular:` marker.
|
|
75
|
+
*
|
|
76
|
+
* Use for cache keys, slug suffixes, log correlation ids, and other
|
|
77
|
+
* "give me something short and stable" needs - **never** for tokens or
|
|
78
|
+
* signatures. FNV-1a is a non-cryptographic hash.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* fnvHash("databricks-claude-sonnet-4-6"); // "k3p9q7"
|
|
82
|
+
* fnvHash([1, 2, 3]) !== fnvHash([3, 2, 1]);
|
|
83
|
+
*/
|
|
84
|
+
export function fnvHash(...values) {
|
|
85
|
+
return fnvHashWithOptions({}, ...values);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Configurable counterpart to {@link fnvHash}.
|
|
89
|
+
*
|
|
90
|
+
* Options:
|
|
91
|
+
* - `length` (default `6`): number of base-32 chars to return. Capped
|
|
92
|
+
* at 7 - the underlying digest is 32 bits, which base-32-encodes to
|
|
93
|
+
* at most 7 chars. Output is left-padded with the alphabet's zero
|
|
94
|
+
* character so short digests still hit the requested width.
|
|
95
|
+
* - `alphabet` (default Crockford-style
|
|
96
|
+
* `"0123456789abcdefghjkmnpqrstvwxyz"`): 32 distinct characters used
|
|
97
|
+
* to encode the digest. Throws when not exactly 32 unique chars.
|
|
98
|
+
* - `digest` (default `0x811c9dc5`, the FNV-1a offset basis): the seed
|
|
99
|
+
* the running digest starts from. Useful for namespacing so
|
|
100
|
+
* otherwise-identical inputs hashed under different namespaces never
|
|
101
|
+
* collide, and for chaining hashes across pipeline stages.
|
|
102
|
+
*
|
|
103
|
+
* The hash is **not** stable across changes to the alphabet or `length` -
|
|
104
|
+
* those tune the output, not the digest input.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* fnvHashWithOptions({ length: 4 }, "user@example.com"); // 4 chars
|
|
108
|
+
* fnvHashWithOptions({ digest: nsHash }, key) !== fnvHash(key); // namespaced
|
|
109
|
+
*/
|
|
110
|
+
export function fnvHashWithOptions(options = {}, ...values) {
|
|
111
|
+
const { length = 6 } = options;
|
|
112
|
+
let digest = options.digest ?? 0x811c9dc5;
|
|
113
|
+
for (const value of hashAttributes(values)) {
|
|
114
|
+
for (let i = 0; i < value.length; i++) {
|
|
115
|
+
digest ^= value.charCodeAt(i);
|
|
116
|
+
digest = Math.imul(digest, 0x01000193);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const alphabet = base32Alphabet(options.alphabet);
|
|
120
|
+
return toBase32(digest, alphabet, true).padStart(7, alphabet[0]).slice(0, Math.min(length, 7));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Walk an arbitrary value as a stream of canonicalized string tokens
|
|
124
|
+
* suitable for feeding into a streaming hash like FNV-1a. Used by
|
|
125
|
+
* {@link fnvHashWithOptions} so structured inputs hash deterministically
|
|
126
|
+
* without a stringification round-trip through `JSON.stringify` (which
|
|
127
|
+
* silently drops `undefined`, has no canonical key order, and can't
|
|
128
|
+
* represent cycles).
|
|
129
|
+
*
|
|
130
|
+
* Canonicalization rules:
|
|
131
|
+
*
|
|
132
|
+
* - `null` / `undefined` collapse to `null:`.
|
|
133
|
+
* - Primitives (`string` / `number` / `boolean`) are tagged with their
|
|
134
|
+
* `typeof` so `"1"` and `1` produce different digests.
|
|
135
|
+
* - Arrays preserve order: `[1,2]` and `[2,1]` hash differently.
|
|
136
|
+
* - Plain objects emit keys in lexical order of each key's own
|
|
137
|
+
* hash-token stream, so `{a:1,b:2}` and `{b:2,a:1}` collapse.
|
|
138
|
+
* - `Map` keys go through the same key-sort path as objects.
|
|
139
|
+
* - `Set`s are sorted by each element's hash-token stream and emit only
|
|
140
|
+
* the elements, so insertion order doesn't leak into the digest.
|
|
141
|
+
* - Cycles emit `circular:` and stop descending.
|
|
142
|
+
* - Anything else falls through to a `${typeof}:${JSON.stringify}`
|
|
143
|
+
* token.
|
|
144
|
+
*/
|
|
145
|
+
function* hashAttributes(input, seen) {
|
|
146
|
+
if (input === null || input === undefined) {
|
|
147
|
+
yield "null:";
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const inputType = typeof input;
|
|
151
|
+
if (inputType === "string" || inputType === "number" || inputType === "boolean") {
|
|
152
|
+
yield `${inputType}:`;
|
|
153
|
+
yield input.toString();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
seen ??= new WeakSet();
|
|
157
|
+
if (inputType === "object") {
|
|
158
|
+
if (seen.has(input)) {
|
|
159
|
+
yield "circular:";
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
seen.add(input);
|
|
163
|
+
try {
|
|
164
|
+
if (Array.isArray(input)) {
|
|
165
|
+
yield "[";
|
|
166
|
+
for (const item of input) {
|
|
167
|
+
yield* hashAttributes(item, seen);
|
|
168
|
+
yield ",";
|
|
169
|
+
}
|
|
170
|
+
yield "]";
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const hashAttributeKeys = (keys) => {
|
|
175
|
+
return keys
|
|
176
|
+
.map((key) => {
|
|
177
|
+
const keyHashAttributes = [...hashAttributes(key, seen)];
|
|
178
|
+
return {
|
|
179
|
+
key,
|
|
180
|
+
keyHashAttributes,
|
|
181
|
+
sortKey: keyHashAttributes.join("\0"),
|
|
182
|
+
};
|
|
183
|
+
})
|
|
184
|
+
.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
185
|
+
};
|
|
186
|
+
if (input instanceof Set) {
|
|
187
|
+
yield "[";
|
|
188
|
+
for (const hashAttributeKey of hashAttributeKeys(Array.from(input))) {
|
|
189
|
+
yield* hashAttributeKey.keyHashAttributes;
|
|
190
|
+
yield ",";
|
|
191
|
+
}
|
|
192
|
+
yield "]";
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
yield "{";
|
|
197
|
+
const keys = input instanceof Map ? Array.from(input.keys()) : Object.keys(input);
|
|
198
|
+
for (const hashAttributeKey of hashAttributeKeys(keys)) {
|
|
199
|
+
const value = input instanceof Map
|
|
200
|
+
? input.get(hashAttributeKey.key)
|
|
201
|
+
: input[hashAttributeKey.key];
|
|
202
|
+
yield* hashAttributeKey.keyHashAttributes;
|
|
203
|
+
yield ":";
|
|
204
|
+
yield* hashAttributes(value, seen);
|
|
205
|
+
yield ",";
|
|
206
|
+
}
|
|
207
|
+
yield "}";
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
seen.delete(input);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
yield `${inputType}:${JSON.stringify(input)}`;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Default Crockford-style base-32 alphabet: digits `0-9` then lowercase
|
|
220
|
+
* letters with `i`, `l`, `o`, `u` removed. Output is safe to drop into
|
|
221
|
+
* URLs, filenames, and `[A-Za-z0-9_-]`-bound marker captures.
|
|
222
|
+
*/
|
|
223
|
+
const BASE32_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
|
|
224
|
+
/**
|
|
225
|
+
* Resolve a caller-supplied alphabet against the default. Returns the
|
|
226
|
+
* default when the caller passed nothing; otherwise validates the
|
|
227
|
+
* override is exactly 32 unique chars. Throws on bad alphabets so callers
|
|
228
|
+
* fail fast instead of producing silently-degraded encodings.
|
|
229
|
+
*/
|
|
230
|
+
function base32Alphabet(alphabet) {
|
|
231
|
+
if (alphabet === undefined) {
|
|
232
|
+
return BASE32_ALPHABET;
|
|
233
|
+
}
|
|
234
|
+
else if (new Set(alphabet).size !== 32) {
|
|
235
|
+
throw new Error("Base32 alphabet must contain 32 unique characters");
|
|
236
|
+
}
|
|
237
|
+
return alphabet;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Encode a 32-bit unsigned integer as base-32 using the default
|
|
241
|
+
* Crockford-style alphabet (or `alphabet` when provided). The encoding
|
|
242
|
+
* has **no** zero-padding by default - `toBase32(0)` returns the
|
|
243
|
+
* alphabet's zero character, otherwise the result is the minimal number
|
|
244
|
+
* of digits that fits the value. Pad / truncate at the call site when you
|
|
245
|
+
* need a fixed width.
|
|
246
|
+
*
|
|
247
|
+
* `disableAlphabetValidation` skips the unique-32-char check for hot
|
|
248
|
+
* paths that have already validated the alphabet. The function still
|
|
249
|
+
* requires `alphabet.length === 32` either way.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* toBase32(0); // "0"
|
|
253
|
+
* toBase32(31); // "z"
|
|
254
|
+
* toBase32(0xdeadbe); // "6vmtw"
|
|
255
|
+
*/
|
|
256
|
+
export function toBase32(value, alphabet, disableAlphabetValidation) {
|
|
257
|
+
if (!disableAlphabetValidation) {
|
|
258
|
+
alphabet = base32Alphabet(alphabet);
|
|
259
|
+
}
|
|
260
|
+
if (alphabet.length !== 32) {
|
|
261
|
+
throw new Error(`Base32 alphabet must contain exactly 32 characters, got ${alphabet.length}`);
|
|
262
|
+
}
|
|
263
|
+
value >>>= 0;
|
|
264
|
+
if (value === 0) {
|
|
265
|
+
return alphabet[0];
|
|
266
|
+
}
|
|
267
|
+
let result = "";
|
|
268
|
+
while (value > 0) {
|
|
269
|
+
result = alphabet[value & 31] + result;
|
|
270
|
+
value >>>= 5;
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9oYXNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7OztHQVdHO0FBRUg7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FzQkc7QUFDSCxNQUFNLFVBQVUsRUFBRSxDQUFDLE1BQWU7SUFDaEMsSUFBSSxNQUFNLEtBQUssU0FBUyxJQUFJLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUN4QyxNQUFNLElBQUksS0FBSyxDQUFDLCtCQUErQixDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUNELE1BQU0sRUFBRSxHQUFHLE1BQU0sRUFBRSxDQUFDO0lBQ3BCLElBQUksTUFBTSxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3pCLE9BQU8sRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBQ0QsT0FBTyxFQUFFLENBQUM7QUFDWixDQUFDO0FBYUQsMEVBQTBFO0FBQzFFLFNBQVMsTUFBTTtJQUNiLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxNQUFnQyxDQUFDO0lBQzlELElBQUksT0FBTyxTQUFTLEVBQUUsVUFBVSxLQUFLLFVBQVU7UUFBRSxPQUFPLFNBQVMsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUUvRSxNQUFNLEtBQUssR0FBRyxJQUFJLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNqQyxJQUFJLE9BQU8sU0FBUyxFQUFFLGVBQWUsS0FBSyxVQUFVLEVBQUUsQ0FBQztRQUNyRCxTQUFTLENBQUMsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25DLENBQUM7U0FBTSxDQUFDO1FBQ04sS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFO1lBQUUsS0FBSyxDQUFDLENBQUMs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QUM7SUFDRCxJQUFJLE1BQU0sR0FBRyxFQUFFLENBQUM7SUFDaEIsT0FBTyxLQUFLLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDakIsTUFBTSxHQUFHLFFBQVMsQ0FBQyxLQUFLLEdBQUcsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDO1FBQ3hDLEtBQUssTUFBTSxDQUFDLENBQUM7SUFDZixDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hvcnQsIGRldGVybWluaXN0aWMgbm9uLWNyeXB0b2dyYXBoaWMgaGFzaGluZyBhbmQgaWQgbWludGluZy5cbiAqXG4gKiB7QGxpbmsgZm52SGFzaH0gLyB7QGxpbmsgZm52SGFzaFdpdGhPcHRpb25zfSBwcm9kdWNlIGEgc3RhYmxlIEZOVi0xYVxuICogZGlnZXN0IG92ZXIgYXJiaXRyYXJ5IHN0cnVjdHVyZWQgaW5wdXQ7IHtAbGluayB0b0Jhc2UzMn0gZW5jb2RlcyBhXG4gKiAzMi1iaXQgaW50ZWdlciBjb21wYWN0bHk7IHtAbGluayBpZH0gbWludHMgdjQgVVVJRHMgKG9yIHNob3J0IGhleFxuICogc2xpY2VzKS4gQWxsIGJyb3dzZXItc2FmZSAtIGJ1aWx0IG9uIGBnbG9iYWxUaGlzLmNyeXB0b2AsIG5vXG4gKiBgbm9kZTpjcnlwdG9gIGltcG9ydC4gKipOZXZlcioqIHVzZSB0aGVzZSBmb3IgdG9rZW5zLCBzaWduYXR1cmVzLCBvclxuICogYW55dGhpbmcgYW4gYXR0YWNrZXIgc2hvdWxkbid0IGJlIGFibGUgdG8gZm9yZ2UuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbi8qKlxuICogTWludCBhIHY0IFVVSUQsIG9yIGEgc2hvcnQgaGV4IHNsaWNlIG9mIG9uZSB3aGVuIGBsZW5ndGhgIGlzIHNldC5cbiAqXG4gKiAtIGBpZCgpYCByZXR1cm5zIGEgZnVsbCBSRkMgNDEyMiB2NCBVVUlELiBQaWNrIHRoaXMgd2hlbiBnbG9iYWxcbiAqICAgdW5pcXVlbmVzcyBtYXR0ZXJzOiBsb25nLXJ1bm5pbmcgYmF0Y2hlcywgaWRzIHRoYXQgY3Jvc3MgYSBzdG9yYWdlIC9cbiAqICAgcHJvY2VzcyBib3VuZGFyeSwgYW55dGhpbmcgdGhhdCBtYXkgY29sbGlkZSBhY3Jvc3MgbWFjaGluZXMuXG4gKiAtIGBpZChsZW5ndGgpYCByZXR1cm5zIHRoZSBmaXJzdCBgbGVuZ3RoYCBoZXggY2hhcnMgb2YgYSBmcmVzaCBVVUlEXG4gKiAgIHdpdGggZGFzaGVzIHN0cmlwcGVkIChlLmcuIGBpZCg4KSAtPiBcImEzZjFjOTJiXCJgKS4gUGljayB0aGlzIHdoZW4gdGhlXG4gKiAgIGlkIGhhcyB0byBiZSBzaG9ydCAvIHR5cGVhYmxlIGFuZCB0aGUgc2NvcGUgaXMgYm91bmRlZCAtIGNhY2hlIGtleXNcbiAqICAgbG9jYWwgdG8gYSByZXF1ZXN0LCBzbHVnIHN1ZmZpeGVzLiBgbGVuZ3RoIDw9IDBgIHRocm93cy5cbiAqXG4gKiBQcmVmZXJzIGBjcnlwdG8ucmFuZG9tVVVJRCgpYCwgd2hpY2ggY292ZXJzIE5vZGUgKD49IDE5KSBhbmQgYSBicm93c2VyIG9uXG4gKiBhIHNlY3VyZSBvcmlnaW4uIEJyb3dzZXJzIGdhdGUgYHJhbmRvbVVVSURgIGJlaGluZCBhIHNlY3VyZSBjb250ZXh0LCBzbyBhXG4gKiBwYWdlIHNlcnZlZCBvdmVyIHBsYWluIGh0dHAgKGEgTEFOIGRldiBob3N0KSBoYXMgYGNyeXB0b2AgYnV0IG5vdCB0aGF0XG4gKiBtZXRob2Q7IHRoaXMgcGFja2FnZSBpcyBicm93c2VyLXNhZmUgYW5kIGl0cyBjYWxsZXJzIG1pbnQgaWRzIG9uIHRoZVxuICogcmVuZGVyIHBhdGgsIHNvIGl0IGRlZ3JhZGVzIGluc3RlYWQgb2YgdGhyb3dpbmc6IGBnZXRSYW5kb21WYWx1ZXNgIHdoZW5cbiAqIHByZXNlbnQsIGVsc2UgYE1hdGgucmFuZG9tYC4gRXZlcnkgYnJhbmNoIHJldHVybnMgYSB3ZWxsLWZvcm1lZCB2NCBVVUlEIC1cbiAqIG9ubHkgdGhlIGVudHJvcHkgc291cmNlIGRpZmZlcnMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGlkKCk7ICAgLy8gXCIxMjNlNDU2Ny1lODliLTEyZDMtYTQ1Ni00MjY2MTQxNzQwMDBcIlxuICogaWQoOCk7ICAvLyBcImEzZjFjOTJiXCJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlkKGxlbmd0aD86IG51bWJlcik6IHN0cmluZyB7XG4gIGlmIChsZW5ndGggIT09IHVuZGVmaW5lZCAmJiBsZW5ndGggPD0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcIkxlbmd0aCBtdXN0IGJlIGdyZWF0ZXIgdGhhbiAwXCIpO1xuICB9XG4gIGNvbnN0IGlkID0gdXVpZFY0KCk7XG4gIGlmIChsZW5ndGggIT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiBpZC5yZXBsYWNlKC8tL2csIFwiXCIpLnNsaWNlKDAsIGxlbmd0aCk7XG4gIH1cbiAgcmV0dXJuIGlkO1xufVxuXG4vKipcbiAqIFRoZSB0d28gYGNyeXB0b2AgbWVtYmVycyB0aGlzIG1vZHVsZSB1c2VzLCBkZWNsYXJlZCBzdHJ1Y3R1cmFsbHkuIGBDcnlwdG9gXG4gKiBpdHNlbGYgaXMgbm90IG5hbWVhYmxlIGhlcmU6IHRoaXMgcGFja2FnZSBjb21waWxlcyB3aXRoIG5laXRoZXIgdGhlIERPTSBub3JcbiAqIHRoZSBOb2RlIGxpYiwgYW5kIGEgY29uc3VtZXIgdGhhdCBkb2VzIHB1bGwgaW4gTm9kZSB0eXBlcyByZXNvbHZlcyBgQ3J5cHRvYFxuICogdG8gYSBnbG9iYWwgdmFsdWUgcmF0aGVyIHRoYW4gYSB0eXBlLlxuICovXG50eXBlIENyeXB0b0xpa2UgPSB7XG4gIHJhbmRvbVVVSUQ/OiAoKSA9PiBzdHJpbmc7XG4gIGdldFJhbmRvbVZhbHVlcz86IChieXRlczogVWludDhBcnJheSkgPT4gdW5rbm93bjtcbn07XG5cbi8qKiBBIHY0IFVVSUQgZnJvbSB0aGUgc3Ryb25nZXN0IHJhbmRvbW5lc3Mgc291cmNlIHRoaXMgcnVudGltZSBvZmZlcnMuICovXG5mdW5jdGlvbiB1dWlkVjQoKTogc3RyaW5nIHtcbiAgY29uc3Qgd2ViQ3J5cHRvID0gZ2xvYmFsVGhpcy5jcnlwdG8gYXMgQ3J5cHRvTGlrZSB8IHVuZGVmaW5lZDtcbiAgaWYgKHR5cGVvZiB3ZWJDcnlwdG8/LnJhbmRvbVVVSUQgPT09IFwiZnVuY3Rpb25cIikgcmV0dXJuIHdlYkNyeXB0by5yYW5kb21VVUlEKCk7XG5cbiAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheSgxNik7XG4gIGlmICh0eXBlb2Ygd2ViQ3J5cHRvPy5nZXRSYW5kb21WYWx1ZXMgPT09IFwiZnVuY3Rpb25cIikge1xuICAgIHdlYkNyeXB0by5nZXRSYW5kb21WYWx1ZXMoYnl0ZXMpO1xuICB9IGVsc2Uge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYnl0ZXMubGVuZ3RoOyBpKyspIGJ5dGVzW2ldID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogMjU2KTtcbiAgfVxuICAvLyBTdGFtcCB0aGUgdmVyc2lvbiAoNCkgYW5kIHZhcmlhbnQgKDEweHgpIGZpZWxkcyBSRkMgNDEyMiByZXF1aXJlcy5cbiAgYnl0ZXNbNl0gPSAoYnl0ZXNbNl0hICYgMHgwZikgfCAweDQwO1xuICBieXRlc1s4XSA9IChieXRlc1s4XSEgJiAweDNmKSB8IDB4ODA7XG5cbiAgY29uc3QgaGV4ID0gQXJyYXkuZnJvbShieXRlcywgKGIpID0+IGIudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsIFwiMFwiKSkuam9pbihcIlwiKTtcbiAgcmV0dXJuIGAke2hleC5zbGljZSgwLCA4KX0tJHtoZXguc2xpY2UoOCwgMTIpfS0ke2hleC5zbGljZSgxMiwgMTYpfS0ke2hleC5zbGljZSgxNiwgMjApfS0ke2hleC5zbGljZSgyMCl9YDtcbn1cblxuLyoqXG4gKiBTaG9ydCwgZGV0ZXJtaW5pc3RpYyBGTlYtMWEgaGFzaCBvdmVyIG9uZSBvciBtb3JlIHZhbHVlcy4gV3JhcHNcbiAqIHtAbGluayBmbnZIYXNoV2l0aE9wdGlvbnN9IHdpdGggYWxsIGRlZmF1bHRzOiA2LWNoYXIgQ3JvY2tmb3JkLXN0eWxlXG4gKiBiYXNlLTMyIG91dHB1dCAoZGlnaXRzICsgbG93ZXJjYXNlLCBtaW51cyBgaWAvYGxgL2BvYC9gdWApLlxuICogQnJvd3Nlci1zYWZlIChubyBgbm9kZTpjcnlwdG9gKS5cbiAqXG4gKiBBY2NlcHRzIGFueSBtaXggb2YgcHJpbWl0aXZlcywgYXJyYXlzLCBwbGFpbiBvYmplY3RzLCBgTWFwYHMsIGFuZFxuICogYFNldGBzOyBuZXN0ZWQgc3RydWN0dXJlcyBhcmUgd2Fsa2VkIGRldGVybWluaXN0aWNhbGx5IHNvIHRoZSBoYXNoIGlzXG4gKiBvcmRlci1zdGFibGUgZm9yIG9iamVjdHMgLyBtYXBzIC8gc2V0cyBhbmQgb3JkZXItc2Vuc2l0aXZlIGZvciBhcnJheXMuXG4gKiBDeWNsZXMgYXJlIGRldGVjdGVkIGFuZCBmb2xkZWQgaW50byBhIGBjaXJjdWxhcjpgIG1hcmtlci5cbiAqXG4gKiBVc2UgZm9yIGNhY2hlIGtleXMsIHNsdWcgc3VmZml4ZXMsIGxvZyBjb3JyZWxhdGlvbiBpZHMsIGFuZCBvdGhlclxuICogXCJnaXZlIG1lIHNvbWV0aGluZyBzaG9ydCBhbmQgc3RhYmxlXCIgbmVlZHMgLSAqKm5ldmVyKiogZm9yIHRva2VucyBvclxuICogc2lnbmF0dXJlcy4gRk5WLTFhIGlzIGEgbm9uLWNyeXB0b2dyYXBoaWMgaGFzaC5cbiAqXG4gKiBAZXhhbXBsZVxuICogZm52SGFzaChcImRhdGFicmlja3MtY2xhdWRlLXNvbm5ldC00LTZcIik7IC8vIFwiazNwOXE3XCJcbiAqIGZudkhhc2goWzEsIDIsIDNdKSAhPT0gZm52SGFzaChbMywgMiwgMV0pO1xuICovXG5leHBvcnQgZnVuY3Rpb24gZm52SGFzaCguLi52YWx1ZXM6IHVua25vd25bXSk6IHN0cmluZyB7XG4gIHJldHVybiBmbnZIYXNoV2l0aE9wdGlvbnMoe30sIC4uLnZhbHVlcyk7XG59XG5cbi8qKlxuICogQ29uZmlndXJhYmxlIGNvdW50ZXJwYXJ0IHRvIHtAbGluayBmbnZIYXNofS5cbiAqXG4gKiBPcHRpb25zOlxuICogICAtIGBsZW5ndGhgIChkZWZhdWx0IGA2YCk6IG51bWJlciBvZiBiYXNlLTMyIGNoYXJzIHRvIHJldHVybi4gQ2FwcGVkXG4gKiAgICAgYXQgNyAtIHRoZSB1bmRlcmx5aW5nIGRpZ2VzdCBpcyAzMiBiaXRzLCB3aGljaCBiYXNlLTMyLWVuY29kZXMgdG9cbiAqICAgICBhdCBtb3N0IDcgY2hhcnMuIE91dHB1dCBpcyBsZWZ0LXBhZGRlZCB3aXRoIHRoZSBhbHBoYWJldCdzIHplcm9cbiAqICAgICBjaGFyYWN0ZXIgc28gc2hvcnQgZGlnZXN0cyBzdGlsbCBoaXQgdGhlIHJlcXVlc3RlZCB3aWR0aC5cbiAqICAgLSBgYWxwaGFiZXRgIChkZWZhdWx0IENyb2NrZm9yZC1zdHlsZVxuICogICAgIGBcIjAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzdHZ3eHl6XCJgKTogMzIgZGlzdGluY3QgY2hhcmFjdGVycyB1c2VkXG4gKiAgICAgdG8gZW5jb2RlIHRoZSBkaWdlc3QuIFRocm93cyB3aGVuIG5vdCBleGFjdGx5IDMyIHVuaXF1ZSBjaGFycy5cbiAqICAgLSBgZGlnZXN0YCAoZGVmYXVsdCBgMHg4MTFjOWRjNWAsIHRoZSBGTlYtMWEgb2Zmc2V0IGJhc2lzKTogdGhlIHNlZWRcbiAqICAgICB0aGUgcnVubmluZyBkaWdlc3Qgc3RhcnRzIGZyb20uIFVzZWZ1bCBmb3IgbmFtZXNwYWNpbmcgc29cbiAqICAgICBvdGhlcndpc2UtaWRlbnRpY2FsIGlucHV0cyBoYXNoZWQgdW5kZXIgZGlmZmVyZW50IG5hbWVzcGFjZXMgbmV2ZXJcbiAqICAgICBjb2xsaWRlLCBhbmQgZm9yIGNoYWluaW5nIGhhc2hlcyBhY3Jvc3MgcGlwZWxpbmUgc3RhZ2VzLlxuICpcbiAqIFRoZSBoYXNoIGlzICoqbm90Kiogc3RhYmxlIGFjcm9zcyBjaGFuZ2VzIHRvIHRoZSBhbHBoYWJldCBvciBgbGVuZ3RoYCAtXG4gKiB0aG9zZSB0dW5lIHRoZSBvdXRwdXQsIG5vdCB0aGUgZGlnZXN0IGlucHV0LlxuICpcbiAqIEBleGFtcGxlXG4gKiBmbnZIYXNoV2l0aE9wdGlvbnMoeyBsZW5ndGg6IDQgfSwgXCJ1c2VyQGV4YW1wbGUuY29tXCIpOyAgICAgICAgLy8gNCBjaGFyc1xuICogZm52SGFzaFdpdGhPcHRpb25zKHsgZGlnZXN0OiBuc0hhc2ggfSwga2V5KSAhPT0gZm52SGFzaChrZXkpOyAvLyBuYW1lc3BhY2VkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmbnZIYXNoV2l0aE9wdGlvbnMoXG4gIG9wdGlvbnM6IHsgbGVuZ3RoPzogbnVtYmVyOyBhbHBoYWJldD86IHN0cmluZzsgZGlnZXN0PzogbnVtYmVyIH0gPSB7fSxcbiAgLi4udmFsdWVzOiB1bmtub3duW11cbik6IHN0cmluZyB7XG4gIGNvbnN0IHsgbGVuZ3RoID0gNiB9ID0gb3B0aW9ucztcblxuICBsZXQgZGlnZXN0ID0gb3B0aW9ucy5kaWdlc3QgPz8gMHg4MTFjOWRjNTtcblxuICBmb3IgKGNvbnN0IHZhbHVlIG9mIGhhc2hBdHRyaWJ1dGVzKHZhbHVlcykpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBkaWdlc3QgXj0gdmFsdWUuY2hhckNvZGVBdChpKTtcbiAgICAgIGRpZ2VzdCA9IE1hdGguaW11bChkaWdlc3QsIDB4MDEwMDAxOTMpO1xuICAgIH1cbiAgfVxuICBjb25zdCBhbHBoYWJldCA9IGJhc2UzMkFscGhhYmV0KG9wdGlvbnMuYWxwaGFiZXQpO1xuICByZXR1cm4gdG9CYXNlMzIoZGlnZXN0LCBhbHBoYWJldCwgdHJ1ZSkucGFkU3RhcnQoNywgYWxwaGFiZXRbMF0pLnNsaWNlKDAsIE1hdGgubWluKGxlbmd0aCwgNykpO1xufVxuXG4vKipcbiAqIFdhbGsgYW4gYXJiaXRyYXJ5IHZhbHVlIGFzIGEgc3RyZWFtIG9mIGNhbm9uaWNhbGl6ZWQgc3RyaW5nIHRva2Vuc1xuICogc3VpdGFibGUgZm9yIGZlZWRpbmcgaW50byBhIHN0cmVhbWluZyBoYXNoIGxpa2UgRk5WLTFhLiBVc2VkIGJ5XG4gKiB7QGxpbmsgZm52SGFzaFdpdGhPcHRpb25zfSBzbyBzdHJ1Y3R1cmVkIGlucHV0cyBoYXNoIGRldGVybWluaXN0aWNhbGx5XG4gKiB3aXRob3V0IGEgc3RyaW5naWZpY2F0aW9uIHJvdW5kLXRyaXAgdGhyb3VnaCBgSlNPTi5zdHJpbmdpZnlgICh3aGljaFxuICogc2lsZW50bHkgZHJvcHMgYHVuZGVmaW5lZGAsIGhhcyBubyBjYW5vbmljYWwga2V5IG9yZGVyLCBhbmQgY2FuJ3RcbiAqIHJlcHJlc2VudCBjeWNsZXMpLlxuICpcbiAqIENhbm9uaWNhbGl6YXRpb24gcnVsZXM6XG4gKlxuICogICAtIGBudWxsYCAvIGB1bmRlZmluZWRgIGNvbGxhcHNlIHRvIGBudWxsOmAuXG4gKiAgIC0gUHJpbWl0aXZlcyAoYHN0cmluZ2AgLyBgbnVtYmVyYCAvIGBib29sZWFuYCkgYXJlIHRhZ2dlZCB3aXRoIHRoZWlyXG4gKiAgICAgYHR5cGVvZmAgc28gYFwiMVwiYCBhbmQgYDFgIHByb2R1Y2UgZGlmZmVyZW50IGRpZ2VzdHMuXG4gKiAgIC0gQXJyYXlzIHByZXNlcnZlIG9yZGVyOiBgWzEsMl1gIGFuZCBgWzIsMV1gIGhhc2ggZGlmZmVyZW50bHkuXG4gKiAgIC0gUGxhaW4gb2JqZWN0cyBlbWl0IGtleXMgaW4gbGV4aWNhbCBvcmRlciBvZiBlYWNoIGtleSdzIG93blxuICogICAgIGhhc2gtdG9rZW4gc3RyZWFtLCBzbyBge2E6MSxiOjJ9YCBhbmQgYHtiOjIsYToxfWAgY29sbGFwc2UuXG4gKiAgIC0gYE1hcGAga2V5cyBnbyB0aHJvdWdoIHRoZSBzYW1lIGtleS1zb3J0IHBhdGggYXMgb2JqZWN0cy5cbiAqICAgLSBgU2V0YHMgYXJlIHNvcnRlZCBieSBlYWNoIGVsZW1lbnQncyBoYXNoLXRva2VuIHN0cmVhbSBhbmQgZW1pdCBvbmx5XG4gKiAgICAgdGhlIGVsZW1lbnRzLCBzbyBpbnNlcnRpb24gb3JkZXIgZG9lc24ndCBsZWFrIGludG8gdGhlIGRpZ2VzdC5cbiAqICAgLSBDeWNsZXMgZW1pdCBgY2lyY3VsYXI6YCBhbmQgc3RvcCBkZXNjZW5kaW5nLlxuICogICAtIEFueXRoaW5nIGVsc2UgZmFsbHMgdGhyb3VnaCB0byBhIGAke3R5cGVvZn06JHtKU09OLnN0cmluZ2lmeX1gXG4gKiAgICAgdG9rZW4uXG4gKi9cbmZ1bmN0aW9uKiBoYXNoQXR0cmlidXRlcyhpbnB1dDogYW55LCBzZWVuPzogV2Vha1NldDxvYmplY3Q+KTogR2VuZXJhdG9yPHN0cmluZz4ge1xuICBpZiAoaW5wdXQgPT09IG51bGwgfHwgaW5wdXQgPT09IHVuZGVmaW5lZCkge1xuICAgIHlpZWxkIFwibnVsbDpcIjtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBpbnB1dFR5cGUgPSB0eXBlb2YgaW5wdXQ7XG4gIGlmIChpbnB1dFR5cGUgPT09IFwic3RyaW5nXCIgfHwgaW5wdXRUeXBlID09PSBcIm51bWJlclwiIHx8IGlucHV0VHlwZSA9PT0gXCJib29sZWFuXCIpIHtcbiAgICB5aWVsZCBgJHtpbnB1dFR5cGV9OmA7XG4gICAgeWllbGQgaW5wdXQudG9TdHJpbmcoKTtcbiAgICByZXR1cm47XG4gIH1cbiAgc2VlbiA/Pz0gbmV3IFdlYWtTZXQ8b2JqZWN0PigpO1xuXG4gIGlmIChpbnB1dFR5cGUgPT09IFwib2JqZWN0XCIpIHtcbiAgICBpZiAoc2Vlbi5oYXMoaW5wdXQpKSB7XG4gICAgICB5aWVsZCBcImNpcmN1bGFyOlwiO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzZWVuLmFkZChpbnB1dCk7XG4gICAgdHJ5IHtcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KGlucHV0KSkge1xuICAgICAgICB5aWVsZCBcIltcIjtcbiAgICAgICAgZm9yIChjb25zdCBpdGVtIG9mIGlucHV0KSB7XG4gICAgICAgICAgeWllbGQqIGhhc2hBdHRyaWJ1dGVzKGl0ZW0sIHNlZW4pO1xuICAgICAgICAgIHlpZWxkIFwiLFwiO1xuICAgICAgICB9XG4gICAgICAgIHlpZWxkIFwiXVwiO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBoYXNoQXR0cmlidXRlS2V5cyA9IChrZXlzOiBBcnJheTx1bmtub3duPikgPT4ge1xuICAgICAgICAgIHJldHVybiBrZXlzXG4gICAgICAgICAgICAubWFwKChrZXkpID0+IHtcbiAgICAgICAgICAgICAgY29uc3Qga2V5SGFzaEF0dHJpYnV0ZXMgPSBbLi4uaGFzaEF0dHJpYnV0ZXMoa2V5LCBzZWVuKV07XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgICAgIGtleUhhc2hBdHRyaWJ1dGVzLFxuICAgICAgICAgICAgICAgIHNvcnRLZXk6IGtleUhhc2hBdHRyaWJ1dGVzLmpvaW4oXCJcXDBcIiksXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnNvcnQoKGEsIGIpID0+IGEuc29ydEtleS5sb2NhbGVDb21wYXJlKGIuc29ydEtleSkpO1xuICAgICAgICB9O1xuICAgICAgICBpZiAoaW5wdXQgaW5zdGFuY2VvZiBTZXQpIHtcbiAgICAgICAgICB5aWVsZCBcIltcIjtcbiAgICAgICAgICBmb3IgKGNvbnN0IGhhc2hBdHRyaWJ1dGVLZXkgb2YgaGFzaEF0dHJpYnV0ZUtleXMoQXJyYXkuZnJvbShpbnB1dCkpKSB7XG4gICAgICAgICAgICB5aWVsZCogaGFzaEF0dHJpYnV0ZUtleS5rZXlIYXNoQXR0cmlidXRlcztcbiAgICAgICAgICAgIHlpZWxkIFwiLFwiO1xuICAgICAgICAgIH1cbiAgICAgICAgICB5aWVsZCBcIl1cIjtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgeWllbGQgXCJ7XCI7XG4gICAgICAgICAgY29uc3Qga2V5cyA9IGlucHV0IGluc3RhbmNlb2YgTWFwID8gQXJyYXkuZnJvbShpbnB1dC5rZXlzKCkpIDogT2JqZWN0LmtleXMoaW5wdXQpO1xuICAgICAgICAgIGZvciAoY29uc3QgaGFzaEF0dHJpYnV0ZUtleSBvZiBoYXNoQXR0cmlidXRlS2V5cyhrZXlzKSkge1xuICAgICAgICAgICAgY29uc3QgdmFsdWUgPVxuICAgICAgICAgICAgICBpbnB1dCBpbnN0YW5jZW9mIE1hcFxuICAgICAgICAgICAgICAgID8gaW5wdXQuZ2V0KGhhc2hBdHRyaWJ1dGVLZXkua2V5KVxuICAgICAgICAgICAgICAgIDogaW5wdXRbaGFzaEF0dHJpYnV0ZUtleS5rZXkgYXMga2V5b2YgdHlwZW9mIGlucHV0XTtcbiAgICAgICAgICAgIHlpZWxkKiBoYXNoQXR0cmlidXRlS2V5LmtleUhhc2hBdHRyaWJ1dGVzO1xuICAgICAgICAgICAgeWllbGQgXCI6XCI7XG4gICAgICAgICAgICB5aWVsZCogaGFzaEF0dHJpYnV0ZXModmFsdWUsIHNlZW4pO1xuICAgICAgICAgICAgeWllbGQgXCIsXCI7XG4gICAgICAgICAgfVxuICAgICAgICAgIHlpZWxkIFwifVwiO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZmluYWxseSB7XG4gICAgICBzZWVuLmRlbGV0ZShpbnB1dCk7XG4gICAgfVxuICB9XG4gIHlpZWxkIGAke2lucHV0VHlwZX06JHtKU09OLnN0cmluZ2lmeShpbnB1dCl9YDtcbn1cblxuLyoqXG4gKiBEZWZhdWx0IENyb2NrZm9yZC1zdHlsZSBiYXNlLTMyIGFscGhhYmV0OiBkaWdpdHMgYDAtOWAgdGhlbiBsb3dlcmNhc2VcbiAqIGxldHRlcnMgd2l0aCBgaWAsIGBsYCwgYG9gLCBgdWAgcmVtb3ZlZC4gT3V0cHV0IGlzIHNhZmUgdG8gZHJvcCBpbnRvXG4gKiBVUkxzLCBmaWxlbmFtZXMsIGFuZCBgW0EtWmEtejAtOV8tXWAtYm91bmQgbWFya2VyIGNhcHR1cmVzLlxuICovXG5jb25zdCBCQVNFMzJfQUxQSEFCRVQgPSBcIjAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzdHZ3eHl6XCI7XG5cbi8qKlxuICogUmVzb2x2ZSBhIGNhbGxlci1zdXBwbGllZCBhbHBoYWJldCBhZ2FpbnN0IHRoZSBkZWZhdWx0LiBSZXR1cm5zIHRoZVxuICogZGVmYXVsdCB3aGVuIHRoZSBjYWxsZXIgcGFzc2VkIG5vdGhpbmc7IG90aGVyd2lzZSB2YWxpZGF0ZXMgdGhlXG4gKiBvdmVycmlkZSBpcyBleGFjdGx5IDMyIHVuaXF1ZSBjaGFycy4gVGhyb3dzIG9uIGJhZCBhbHBoYWJldHMgc28gY2FsbGVyc1xuICogZmFpbCBmYXN0IGluc3RlYWQgb2YgcHJvZHVjaW5nIHNpbGVudGx5LWRlZ3JhZGVkIGVuY29kaW5ncy5cbiAqL1xuZnVuY3Rpb24gYmFzZTMyQWxwaGFiZXQoYWxwaGFiZXQ/OiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoYWxwaGFiZXQgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiBCQVNFMzJfQUxQSEFCRVQ7XG4gIH0gZWxzZSBpZiAobmV3IFNldChhbHBoYWJldCkuc2l6ZSAhPT0gMzIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJCYXNlMzIgYWxwaGFiZXQgbXVzdCBjb250YWluIDMyIHVuaXF1ZSBjaGFyYWN0ZXJzXCIpO1xuICB9XG4gIHJldHVybiBhbHBoYWJldDtcbn1cblxuLyoqXG4gKiBFbmNvZGUgYSAzMi1iaXQgdW5zaWduZWQgaW50ZWdlciBhcyBiYXNlLTMyIHVzaW5nIHRoZSBkZWZhdWx0XG4gKiBDcm9ja2ZvcmQtc3R5bGUgYWxwaGFiZXQgKG9yIGBhbHBoYWJldGAgd2hlbiBwcm92aWRlZCkuIFRoZSBlbmNvZGluZ1xuICogaGFzICoqbm8qKiB6ZXJvLXBhZGRpbmcgYnkgZGVmYXVsdCAtIGB0b0Jhc2UzMigwKWAgcmV0dXJucyB0aGVcbiAqIGFscGhhYmV0J3MgemVybyBjaGFyYWN0ZXIsIG90aGVyd2lzZSB0aGUgcmVzdWx0IGlzIHRoZSBtaW5pbWFsIG51bWJlclxuICogb2YgZGlnaXRzIHRoYXQgZml0cyB0aGUgdmFsdWUuIFBhZCAvIHRydW5jYXRlIGF0IHRoZSBjYWxsIHNpdGUgd2hlbiB5b3VcbiAqIG5lZWQgYSBmaXhlZCB3aWR0aC5cbiAqXG4gKiBgZGlzYWJsZUFscGhhYmV0VmFsaWRhdGlvbmAgc2tpcHMgdGhlIHVuaXF1ZS0zMi1jaGFyIGNoZWNrIGZvciBob3RcbiAqIHBhdGhzIHRoYXQgaGF2ZSBhbHJlYWR5IHZhbGlkYXRlZCB0aGUgYWxwaGFiZXQuIFRoZSBmdW5jdGlvbiBzdGlsbFxuICogcmVxdWlyZXMgYGFscGhhYmV0Lmxlbmd0aCA9PT0gMzJgIGVpdGhlciB3YXkuXG4gKlxuICogQGV4YW1wbGVcbiAqIHRvQmFzZTMyKDApOyAgICAgICAgLy8gXCIwXCJcbiAqIHRvQmFzZTMyKDMxKTsgICAgICAgLy8gXCJ6XCJcbiAqIHRvQmFzZTMyKDB4ZGVhZGJlKTsgLy8gXCI2dm10d1wiXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0b0Jhc2UzMihcbiAgdmFsdWU6IG51bWJlcixcbiAgYWxwaGFiZXQ/OiBzdHJpbmcsXG4gIGRpc2FibGVBbHBoYWJldFZhbGlkYXRpb24/OiBib29sZWFuLFxuKTogc3RyaW5nIHtcbiAgaWYgKCFkaXNhYmxlQWxwaGFiZXRWYWxpZGF0aW9uKSB7XG4gICAgYWxwaGFiZXQgPSBiYXNlMzJBbHBoYWJldChhbHBoYWJldCk7XG4gIH1cbiAgaWYgKGFscGhhYmV0IS5sZW5ndGggIT09IDMyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBCYXNlMzIgYWxwaGFiZXQgbXVzdCBjb250YWluIGV4YWN0bHkgMzIgY2hhcmFjdGVycywgZ290ICR7YWxwaGFiZXQhLmxlbmd0aH1gKTtcbiAgfVxuICB2YWx1ZSA+Pj49IDA7XG4gIGlmICh2YWx1ZSA9PT0gMCkge1xuICAgIHJldHVybiBhbHBoYWJldCFbMF0hO1xuICB9XG4gIGxldCByZXN1bHQgPSBcIlwiO1xuICB3aGlsZSAodmFsdWUgPiAwKSB7XG4gICAgcmVzdWx0ID0gYWxwaGFiZXQhW3ZhbHVlICYgMzFdICsgcmVzdWx0O1xuICAgIHZhbHVlID4+Pj0gNTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuIl19
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic readers for HTTP requests, shared across AppKit
|
|
3
|
+
* plugins: header and cookie extraction. Works uniformly across
|
|
4
|
+
* Express, Node `IncomingMessage`, WHATWG `Request` / `Response` /
|
|
5
|
+
* `Headers`, Hono, and any object that exposes a `headers` field of one
|
|
6
|
+
* of those shapes. Dependency-free and browser-safe so it can run in
|
|
7
|
+
* either a server or a client bundle.
|
|
8
|
+
*
|
|
9
|
+
* URL path matching lives in `./net.browser.ts` (`pathMatch`).
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Anything that contains HTTP headers. Accepts:
|
|
15
|
+
*
|
|
16
|
+
* - A WHATWG `Headers` instance (fetch / undici / Hono `c.req.raw.headers`).
|
|
17
|
+
* - A header record (`Record<string, string | string[] | undefined>`),
|
|
18
|
+
* Node / Express style.
|
|
19
|
+
* - Any object with a `headers` field of one of the above. This covers
|
|
20
|
+
* Express `req`, Node `IncomingMessage`, WHATWG `Request` / `Response`,
|
|
21
|
+
* Hono `c.req.raw`, and similar shapes.
|
|
22
|
+
*/
|
|
23
|
+
export type HeaderLike = Headers | HeaderRecord | {
|
|
24
|
+
headers: Headers | HeaderRecord;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Single header value as exposed by Node `IncomingMessage.headers` and
|
|
28
|
+
* Express `req.headers` (string for most headers, array for repeated
|
|
29
|
+
* headers such as `Set-Cookie`).
|
|
30
|
+
*/
|
|
31
|
+
type HeaderValueLike = string[] | string | undefined;
|
|
32
|
+
/** Header bag with case-insensitive keys (Node / Express style). */
|
|
33
|
+
type HeaderRecord = Record<string, HeaderValueLike>;
|
|
34
|
+
/**
|
|
35
|
+
* Invokes `consumer` once per value for `headerName`, case-insensitive.
|
|
36
|
+
*
|
|
37
|
+
* - **Record input:** if the field is an array (e.g. repeated `Set-Cookie`),
|
|
38
|
+
* `consumer` runs once per array item.
|
|
39
|
+
* - **`Headers` input:** uses `get(name)` (which spec-joins repeats with
|
|
40
|
+
* `, `) except for `Set-Cookie`, which uses `getSetCookie()` so each
|
|
41
|
+
* cookie is delivered separately.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* forEachHeaderValue(req, "x-trace-id", (v) => spans.push(v)); // Express
|
|
45
|
+
* forEachHeaderValue(c.req.raw, "set-cookie", (v) => log(v)); // Hono
|
|
46
|
+
* forEachHeaderValue(headersInstance, "cookie", parse); // fetch
|
|
47
|
+
*/
|
|
48
|
+
export declare function forEachHeaderValue(input: HeaderLike | null | undefined, headerName: string, consumer: (value: string) => void): void;
|
|
49
|
+
/**
|
|
50
|
+
* Parses `Cookie` header values into a name-to-value map (URI-decoded).
|
|
51
|
+
*
|
|
52
|
+
* Accepts:
|
|
53
|
+
*
|
|
54
|
+
* - A raw `Cookie` string (`"a=1; b=2"`).
|
|
55
|
+
* - An array of such strings (e.g. multiple `Cookie` headers).
|
|
56
|
+
* - Any {@link HeaderLike}: a WHATWG `Headers` instance, a header
|
|
57
|
+
* record, or a request-like object with a `headers` field.
|
|
58
|
+
*
|
|
59
|
+
* First occurrence of each cookie name wins; later duplicates are ignored.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* parseCookies("session=abc; theme=dark");
|
|
63
|
+
* // { session: "abc", theme: "dark" }
|
|
64
|
+
*
|
|
65
|
+
* parseCookies(req); // Express / Node
|
|
66
|
+
* parseCookies(c.req.raw); // Hono
|
|
67
|
+
* parseCookies(request); // fetch Request
|
|
68
|
+
* parseCookies(request.headers); // WHATWG Headers directly
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseCookies(input: HeaderLike | HeaderValueLike | null): Record<string, string>;
|
|
71
|
+
/**
|
|
72
|
+
* Build an `Error` describing a failed `fetch` response: status line,
|
|
73
|
+
* the URL(s) involved (the requested `url` and the final `response.url`
|
|
74
|
+
* after redirects, de-duplicated), and a truncated snapshot of the
|
|
75
|
+
* response body. Reads the body defensively - a body that can't be read
|
|
76
|
+
* is simply omitted. Call only on a non-`ok` response.
|
|
77
|
+
*/
|
|
78
|
+
export declare function createFetchError(response: Response, url?: string): Promise<Error>;
|
|
79
|
+
export {};
|
package/lib/src/http.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic readers for HTTP requests, shared across AppKit
|
|
3
|
+
* plugins: header and cookie extraction. Works uniformly across
|
|
4
|
+
* Express, Node `IncomingMessage`, WHATWG `Request` / `Response` /
|
|
5
|
+
* `Headers`, Hono, and any object that exposes a `headers` field of one
|
|
6
|
+
* of those shapes. Dependency-free and browser-safe so it can run in
|
|
7
|
+
* either a server or a client bundle.
|
|
8
|
+
*
|
|
9
|
+
* URL path matching lives in `./net.browser.ts` (`pathMatch`).
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
// ────────────────────────────────────────────────────────────────
|
|
14
|
+
// Header helpers
|
|
15
|
+
// ────────────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Invokes `consumer` once per value for `headerName`, case-insensitive.
|
|
18
|
+
*
|
|
19
|
+
* - **Record input:** if the field is an array (e.g. repeated `Set-Cookie`),
|
|
20
|
+
* `consumer` runs once per array item.
|
|
21
|
+
* - **`Headers` input:** uses `get(name)` (which spec-joins repeats with
|
|
22
|
+
* `, `) except for `Set-Cookie`, which uses `getSetCookie()` so each
|
|
23
|
+
* cookie is delivered separately.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* forEachHeaderValue(req, "x-trace-id", (v) => spans.push(v)); // Express
|
|
27
|
+
* forEachHeaderValue(c.req.raw, "set-cookie", (v) => log(v)); // Hono
|
|
28
|
+
* forEachHeaderValue(headersInstance, "cookie", parse); // fetch
|
|
29
|
+
*/
|
|
30
|
+
export function forEachHeaderValue(input, headerName, consumer) {
|
|
31
|
+
const headers = unwrap(input);
|
|
32
|
+
if (!headers)
|
|
33
|
+
return;
|
|
34
|
+
const target = headerName.toLowerCase();
|
|
35
|
+
if (isHeaders(headers)) {
|
|
36
|
+
// `Headers.get` joins repeated values with `, ` per spec, which
|
|
37
|
+
// mangles `Set-Cookie` (cookies legitimately contain commas in
|
|
38
|
+
// their `expires=` attribute). `getSetCookie` is the dedicated
|
|
39
|
+
// splitter and is the only safe path for that header.
|
|
40
|
+
if (target === "set-cookie") {
|
|
41
|
+
for (const value of headers.getSetCookie())
|
|
42
|
+
consumer(value);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const value = headers.get(headerName);
|
|
46
|
+
if (value !== null)
|
|
47
|
+
consumer(value);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
51
|
+
if (value == null || key.toLowerCase() !== target)
|
|
52
|
+
continue;
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
for (const item of value)
|
|
55
|
+
consumer(item);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
consumer(value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parses `Cookie` header values into a name-to-value map (URI-decoded).
|
|
64
|
+
*
|
|
65
|
+
* Accepts:
|
|
66
|
+
*
|
|
67
|
+
* - A raw `Cookie` string (`"a=1; b=2"`).
|
|
68
|
+
* - An array of such strings (e.g. multiple `Cookie` headers).
|
|
69
|
+
* - Any {@link HeaderLike}: a WHATWG `Headers` instance, a header
|
|
70
|
+
* record, or a request-like object with a `headers` field.
|
|
71
|
+
*
|
|
72
|
+
* First occurrence of each cookie name wins; later duplicates are ignored.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* parseCookies("session=abc; theme=dark");
|
|
76
|
+
* // { session: "abc", theme: "dark" }
|
|
77
|
+
*
|
|
78
|
+
* parseCookies(req); // Express / Node
|
|
79
|
+
* parseCookies(c.req.raw); // Hono
|
|
80
|
+
* parseCookies(request); // fetch Request
|
|
81
|
+
* parseCookies(request.headers); // WHATWG Headers directly
|
|
82
|
+
*/
|
|
83
|
+
export function parseCookies(input) {
|
|
84
|
+
if (input == null)
|
|
85
|
+
return {};
|
|
86
|
+
const out = {};
|
|
87
|
+
if (typeof input === "string") {
|
|
88
|
+
parseCookieString(input, out);
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(input)) {
|
|
92
|
+
for (const item of input) {
|
|
93
|
+
if (typeof item === "string")
|
|
94
|
+
parseCookieString(item, out);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
forEachHeaderValue(input, "cookie", (value) => {
|
|
99
|
+
parseCookieString(value, out);
|
|
100
|
+
});
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build an `Error` describing a failed `fetch` response: status line,
|
|
105
|
+
* the URL(s) involved (the requested `url` and the final `response.url`
|
|
106
|
+
* after redirects, de-duplicated), and a truncated snapshot of the
|
|
107
|
+
* response body. Reads the body defensively - a body that can't be read
|
|
108
|
+
* is simply omitted. Call only on a non-`ok` response.
|
|
109
|
+
*/
|
|
110
|
+
export async function createFetchError(response, url) {
|
|
111
|
+
let body = "";
|
|
112
|
+
try {
|
|
113
|
+
body = (await response.text()).trim();
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// ignore
|
|
117
|
+
}
|
|
118
|
+
if (body.length > 1_000) {
|
|
119
|
+
body = `${body.slice(0, 1_000)}…`;
|
|
120
|
+
}
|
|
121
|
+
const urlSummary = [...new Set([url, response.url].filter(Boolean))].join(" -> ");
|
|
122
|
+
return new Error([
|
|
123
|
+
`Request failed.`,
|
|
124
|
+
`${response.status} ${response.statusText}`,
|
|
125
|
+
`${urlSummary}`,
|
|
126
|
+
body && `Response:\n${body}`,
|
|
127
|
+
]
|
|
128
|
+
.filter(Boolean)
|
|
129
|
+
.join("\n"));
|
|
130
|
+
}
|
|
131
|
+
// ────────────────────────────────────────────────────────────────
|
|
132
|
+
// Private helpers
|
|
133
|
+
// ────────────────────────────────────────────────────────────────
|
|
134
|
+
/**
|
|
135
|
+
* Type guard for WHATWG `Headers`. Duck-types on the two methods that
|
|
136
|
+
* matter to this module (`get` and `getSetCookie`) so polyfilled
|
|
137
|
+
* implementations and Hono's `HonoHeaders` are accepted without
|
|
138
|
+
* pulling `Headers` in as a hard dependency.
|
|
139
|
+
*/
|
|
140
|
+
function isHeaders(value) {
|
|
141
|
+
return (typeof value === "object" &&
|
|
142
|
+
value !== null &&
|
|
143
|
+
typeof value.get === "function" &&
|
|
144
|
+
typeof value.getSetCookie === "function");
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* `HeaderRecord` is an index signature, so `"headers" in input` cannot
|
|
148
|
+
* discriminate it from the wrapped `{ headers }` shape at the type
|
|
149
|
+
* level. This guard inspects the runtime value of `headers`: only
|
|
150
|
+
* objects (`Headers` or a nested record) qualify as the wrapper shape,
|
|
151
|
+
* never stray string/array values that happen to live under a `headers`
|
|
152
|
+
* key on a header record.
|
|
153
|
+
*/
|
|
154
|
+
function isWrapped(input) {
|
|
155
|
+
const headers = input.headers;
|
|
156
|
+
return headers != null && typeof headers === "object" && !Array.isArray(headers);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Parse a single `Cookie`-style header string (`"a=1; b=2"`) into
|
|
160
|
+
* `out`. Names without a value are skipped; first occurrence wins so
|
|
161
|
+
* later duplicates are ignored. Cookie values are URI-decoded.
|
|
162
|
+
*/
|
|
163
|
+
function parseCookieString(input, out) {
|
|
164
|
+
for (const part of input.split(";")) {
|
|
165
|
+
const eq = part.indexOf("=");
|
|
166
|
+
if (eq < 0)
|
|
167
|
+
continue;
|
|
168
|
+
const name = part.slice(0, eq).trim();
|
|
169
|
+
if (!name || name in out)
|
|
170
|
+
continue;
|
|
171
|
+
const raw = part.slice(eq + 1).trim();
|
|
172
|
+
out[name] = decodeURIComponent(raw);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Normalize a {@link HeaderLike} input down to either a `Headers`
|
|
177
|
+
* instance or a header `Record`. Returns `null` for missing input so
|
|
178
|
+
* callers can short-circuit without a separate nullish check.
|
|
179
|
+
*/
|
|
180
|
+
function unwrap(input) {
|
|
181
|
+
if (input == null)
|
|
182
|
+
return null;
|
|
183
|
+
if (isHeaders(input))
|
|
184
|
+
return input;
|
|
185
|
+
if (isWrapped(input))
|
|
186
|
+
return input.headers;
|
|
187
|
+
return input;
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7OztHQVdHO0FBNEJILG1FQUFtRTtBQUNuRSxpQkFBaUI7QUFDakIsbUVBQW1FO0FBRW5FOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQ2hDLEtBQW9DLEVBQ3BDLFVBQWtCLEVBQ2xCLFFBQWlDO0lBRWpDLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM5QixJQUFJLENBQUMsT0FBTztRQUFFLE9BQU87SUFFckIsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBRXhDLElBQUksU0FBUyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDdkIsZ0VBQWdFO1FBQ2hFLCtEQUErRDtRQUMvRCwrREFBK0Q7UUFDL0Qsc0RBQXNEO1FBQ3RELElBQUksTUFBTSxLQUFLLFlBQVksRUFBRSxDQUFDO1lBQzVCLEtBQUssTUFBTSxLQUFLLElBQUksT0FBTyxDQUFDLFlBQVksRUFBRTtnQkFBRSxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDNUQsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3RDLElBQUksS0FBSyxLQUFLLElBQUk7WUFBRSxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEMsT0FBTztJQUNULENBQUM7SUFFRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1FBQ25ELElBQUksS0FBSyxJQUFJLElBQUksSUFBSSxHQUFHLENBQUMsV0FBVyxFQUFFLEtBQUssTUFBTTtZQUFFLFNBQVM7UUFDNUQsSUFBSSxLQUFLLENBQUMs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cmVhZGVycyBmb3IgSFRUUCByZXF1ZXN0cywgc2hhcmVkIGFjcm9zcyBBcHBLaXRcbiAqIHBsdWdpbnM6IGhlYWRlciBhbmQgY29va2llIGV4dHJhY3Rpb24uIFdvcmtzIHVuaWZvcm1seSBhY3Jvc3NcbiAqIEV4cHJlc3MsIE5vZGUgYEluY29taW5nTWVzc2FnZWAsIFdIQVRXRyBgUmVxdWVzdGAgLyBgUmVzcG9uc2VgIC9cbiAqIGBIZWFkZXJzYCwgSG9ubywgYW5kIGFueSBvYmplY3QgdGhhdCBleHBvc2VzIGEgYGhlYWRlcnNgIGZpZWxkIG9mIG9uZVxuICogb2YgdGhvc2Ugc2hhcGVzLiBEZXBlbmRlbmN5LWZyZWUgYW5kIGJyb3dzZXItc2FmZSBzbyBpdCBjYW4gcnVuIGluXG4gKiBlaXRoZXIgYSBzZXJ2ZXIgb3IgYSBjbGllbnQgYnVuZGxlLlxuICpcbiAqIFVSTCBwYXRoIG1hdGNoaW5nIGxpdmVzIGluIGAuL25ldC5icm93c2VyLnRzYCAoYHBhdGhNYXRjaGApLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG4vLyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbi8vIFR5cGVzXG4vLyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqXG4gKiBBbnl0aGluZyB0aGF0IGNvbnRhaW5zIEhUVFAgaGVhZGVycy4gQWNjZXB0czpcbiAqXG4gKiAtIEEgV0hBVFdHIGBIZWFkZXJzYCBpbnN0YW5jZSAoZmV0Y2ggLyB1bmRpY2kgLyBIb25vIGBjLnJlcS5yYXcuaGVhZGVyc2ApLlxuICogLSBBIGhlYWRlciByZWNvcmQgKGBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCBzdHJpbmdbXSB8IHVuZGVmaW5lZD5gKSxcbiAqICAgTm9kZSAvIEV4cHJlc3Mgc3R5bGUuXG4gKiAtIEFueSBvYmplY3Qgd2l0aCBhIGBoZWFkZXJzYCBmaWVsZCBvZiBvbmUgb2YgdGhlIGFib3ZlLiBUaGlzIGNvdmVyc1xuICogICBFeHByZXNzIGByZXFgLCBOb2RlIGBJbmNvbWluZ01lc3NhZ2VgLCBXSEFUV0cgYFJlcXVlc3RgIC8gYFJlc3BvbnNlYCxcbiAqICAgSG9ubyBgYy5yZXEucmF3YCwgYW5kIHNpbWlsYXIgc2hhcGVzLlxuICovXG5leHBvcnQgdHlwZSBIZWFkZXJMaWtlID0gSGVhZGVycyB8IEhlYWRlclJlY29yZCB8IHsgaGVhZGVyczogSGVhZGVycyB8IEhlYWRlclJlY29yZCB9O1xuXG4vKipcbiAqIFNpbmdsZSBoZWFkZXIgdmFsdWUgYXMgZXhwb3NlZCBieSBOb2RlIGBJbmNvbWluZ01lc3NhZ2UuaGVhZGVyc2AgYW5kXG4gKiBFeHByZXNzIGByZXEuaGVhZGVyc2AgKHN0cmluZyBmb3IgbW9zdCBoZWFkZXJzLCBhcnJheSBmb3IgcmVwZWF0ZWRcbiAqIGhlYWRlcnMgc3VjaCBhcyBgU2V0LUNvb2tpZWApLlxuICovXG50eXBlIEhlYWRlclZhbHVlTGlrZSA9IHN0cmluZ1tdIHwgc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4vKiogSGVhZGVyIGJhZyB3aXRoIGNhc2UtaW5zZW5zaXRpdmUga2V5cyAoTm9kZSAvIEV4cHJlc3Mgc3R5bGUpLiAqL1xudHlwZSBIZWFkZXJSZWNvcmQgPSBSZWNvcmQ8c3RyaW5nLCBIZWFkZXJWYWx1ZUxpa2U+O1xuXG4vLyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcbi8vIEhlYWRlciBoZWxwZXJzXG4vLyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuLyoqXG4gKiBJbnZva2VzIGBjb25zdW1lcmAgb25jZSBwZXIgdmFsdWUgZm9yIGBoZWFkZXJOYW1lYCwgY2FzZS1pbnNlbnNpdGl2ZS5cbiAqXG4gKiAtICoqUmVjb3JkIGlucHV0OioqIGlmIHRoZSBmaWVsZCBpcyBhbiBhcnJheSAoZS5nLiByZXBlYXRlZCBgU2V0LUNvb2tpZWApLFxuICogICBgY29uc3VtZXJgIHJ1bnMgb25jZSBwZXIgYXJyYXkgaXRlbS5cbiAqIC0gKipgSGVhZGVyc2AgaW5wdXQ6KiogdXNlcyBgZ2V0KG5hbWUpYCAod2hpY2ggc3BlYy1qb2lucyByZXBlYXRzIHdpdGhcbiAqICAgYCwgYCkgZXhjZXB0IGZvciBgU2V0LUNvb2tpZWAsIHdoaWNoIHVzZXMgYGdldFNldENvb2tpZSgpYCBzbyBlYWNoXG4gKiAgIGNvb2tpZSBpcyBkZWxpdmVyZWQgc2VwYXJhdGVseS5cbiAqXG4gKiBAZXhhbXBsZVxuICogZm9yRWFjaEhlYWRlclZhbHVlKHJlcSwgXCJ4LXRyYWNlLWlkXCIsICh2KSA9PiBzcGFucy5wdXNoKHYpKTsgICAgIC8vIEV4cHJlc3NcbiAqIGZvckVhY2hIZWFkZXJWYWx1ZShjLnJlcS5yYXcsIFwic2V0LWNvb2tpZVwiLCAodikgPT4gbG9nKHYpKTsgICAgICAvLyBIb25vXG4gKiBmb3JFYWNoSGVhZGVyVmFsdWUoaGVhZGVyc0luc3RhbmNlLCBcImNvb2tpZVwiLCBwYXJzZSk7ICAgICAgICAgICAgLy8gZmV0Y2hcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGZvckVhY2hIZWFkZXJWYWx1ZShcbiAgaW5wdXQ6IEhlYWRlckxpa2UgfCBudWxsIHwgdW5kZWZpbmVkLFxuICBoZWFkZXJOYW1lOiBzdHJpbmcsXG4gIGNvbnN1bWVyOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZCxcbik6IHZvaWQge1xuICBjb25zdCBoZWFkZXJzID0gdW53cmFwKGlucHV0KTtcbiAgaWYgKCFoZWFkZXJzKSByZXR1cm47XG5cbiAgY29uc3QgdGFyZ2V0ID0gaGVhZGVyTmFtZS50b0xvd2VyQ2FzZSgpO1xuXG4gIGlmIChpc0hlYWRlcnMoaGVhZGVycykpIHtcbiAgICAvLyBgSGVhZGVycy5nZXRgIGpvaW5zIHJlcGVhdGVkIHZhbHVlcyB3aXRoIGAsIGAgcGVyIHNwZWMsIHdoaWNoXG4gICAgLy8gbWFuZ2xlcyBgU2V0LUNvb2tpZWAgKGNvb2tpZXMgbGVnaXRpbWF0ZWx5IGNvbnRhaW4gY29tbWFzIGluXG4gICAgLy8gdGhlaXIgYGV4cGlyZXM9YCBhdHRyaWJ1dGUpLiBgZ2V0U2V0Q29va2llYCBpcyB0aGUgZGVkaWNhdGVkXG4gICAgLy8gc3BsaXR0ZXIgYW5kIGlzIHRoZSBvbmx5IHNhZmUgcGF0aCBmb3IgdGhhdCBoZWFkZXIuXG4gICAgaWYgKHRhcmdldCA9PT0gXCJzZXQtY29va2llXCIpIHtcbiAgICAgIGZvciAoY29uc3QgdmFsdWUgb2YgaGVhZGVycy5nZXRTZXRDb29raWUoKSkgY29uc3VtZXIodmFsdWUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB2YWx1ZSA9IGhlYWRlcnMuZ2V0KGhlYWRlck5hbWUpO1xuICAgIGlmICh2YWx1ZSAhPT0gbnVsbCkgY29uc3VtZXIodmFsdWUpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGhlYWRlcnMpKSB7XG4gICAgaWYgKHZhbHVlID09IG51bGwgfHwga2V5LnRvTG93ZXJDYXNlKCkgIT09IHRhcmdldCkgY29udGludWU7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgdmFsdWUpIGNvbnN1bWVyKGl0ZW0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdW1lcih2YWx1ZSk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogUGFyc2VzIGBDb29raWVgIGhlYWRlciB2YWx1ZXMgaW50byBhIG5hbWUtdG8tdmFsdWUgbWFwIChVUkktZGVjb2RlZCkuXG4gKlxuICogQWNjZXB0czpcbiAqXG4gKiAtIEEgcmF3IGBDb29raWVgIHN0cmluZyAoYFwiYT0xOyBiPTJcImApLlxuICogLSBBbiBhcnJheSBvZiBzdWNoIHN0cmluZ3MgKGUuZy4gbXVsdGlwbGUgYENvb2tpZWAgaGVhZGVycykuXG4gKiAtIEFueSB7QGxpbmsgSGVhZGVyTGlrZX06IGEgV0hBVFdHIGBIZWFkZXJzYCBpbnN0YW5jZSwgYSBoZWFkZXJcbiAqICAgcmVjb3JkLCBvciBhIHJlcXVlc3QtbGlrZSBvYmplY3Qgd2l0aCBhIGBoZWFkZXJzYCBmaWVsZC5cbiAqXG4gKiBGaXJzdCBvY2N1cnJlbmNlIG9mIGVhY2ggY29va2llIG5hbWUgd2luczsgbGF0ZXIgZHVwbGljYXRlcyBhcmUgaWdub3JlZC5cbiAqXG4gKiBAZXhhbXBsZVxuICogcGFyc2VDb29raWVzKFwic2Vzc2lvbj1hYmM7IHRoZW1lPWRhcmtcIik7XG4gKiAvLyB7IHNlc3Npb246IFwiYWJjXCIsIHRoZW1lOiBcImRhcmtcIiB9XG4gKlxuICogcGFyc2VDb29raWVzKHJlcSk7ICAgICAgICAgICAgICAvLyBFeHByZXNzIC8gTm9kZVxuICogcGFyc2VDb29raWVzKGMucmVxLnJhdyk7ICAgICAgICAvLyBIb25vXG4gKiBwYXJzZUNvb2tpZXMocmVxdWVzdCk7ICAgICAgICAgIC8vIGZldGNoIFJlcXVlc3RcbiAqIHBhcnNlQ29va2llcyhyZXF1ZXN0LmhlYWRlcnMpOyAgLy8gV0hBVFdHIEhlYWRlcnMgZGlyZWN0bHlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlQ29va2llcyhpbnB1dDogSGVhZGVyTGlrZSB8IEhlYWRlclZhbHVlTGlrZSB8IG51bGwpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcbiAgaWYgKGlucHV0ID09IG51bGwpIHJldHVybiB7fTtcbiAgY29uc3Qgb3V0OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cbiAgaWYgKHR5cGVvZiBpbnB1dCA9PT0gXCJzdHJpbmdcIikge1xuICAgIHBhcnNlQ29va2llU3RyaW5nKGlucHV0LCBvdXQpO1xuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICBpZiAoQXJyYXkuaXNBcnJheShpbnB1dCkpIHtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgaW5wdXQpIHtcbiAgICAgIGlmICh0eXBlb2YgaXRlbSA9PT0gXCJzdHJpbmdcIikgcGFyc2VDb29raWVTdHJpbmcoaXRlbSwgb3V0KTtcbiAgICB9XG4gICAgcmV0dXJuIG91dDtcbiAgfVxuXG4gIGZvckVhY2hIZWFkZXJWYWx1ZShpbnB1dCwgXCJjb29raWVcIiwgKHZhbHVlKSA9PiB7XG4gICAgcGFyc2VDb29raWVTdHJpbmcodmFsdWUsIG91dCk7XG4gIH0pO1xuICByZXR1cm4gb3V0O1xufVxuXG4vKipcbiAqIEJ1aWxkIGFuIGBFcnJvcmAgZGVzY3JpYmluZyBhIGZhaWxlZCBgZmV0Y2hgIHJlc3BvbnNlOiBzdGF0dXMgbGluZSxcbiAqIHRoZSBVUkwocykgaW52b2x2ZWQgKHRoZSByZXF1ZXN0ZWQgYHVybGAgYW5kIHRoZSBmaW5hbCBgcmVzcG9uc2UudXJsYFxuICogYWZ0ZXIgcmVkaXJlY3RzLCBkZS1kdXBsaWNhdGVkKSwgYW5kIGEgdHJ1bmNhdGVkIHNuYXBzaG90IG9mIHRoZVxuICogcmVzcG9uc2UgYm9keS4gUmVhZHMgdGhlIGJvZHkgZGVmZW5zaXZlbHkgLSBhIGJvZHkgdGhhdCBjYW4ndCBiZSByZWFkXG4gKiBpcyBzaW1wbHkgb21pdHRlZC4gQ2FsbCBvbmx5IG9uIGEgbm9uLWBva2AgcmVzcG9uc2UuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVGZXRjaEVycm9yKHJlc3BvbnNlOiBSZXNwb25zZSwgdXJsPzogc3RyaW5nKTogUHJvbWlzZTxFcnJvcj4ge1xuICBsZXQgYm9keSA9IFwiXCI7XG4gIHRyeSB7XG4gICAgYm9keSA9IChhd2FpdCByZXNwb25zZS50ZXh0KCkpLnRyaW0oKTtcbiAgfSBjYXRjaCB7XG4gICAgLy8gaWdub3JlXG4gIH1cbiAgaWYgKGJvZHkubGVuZ3RoID4gMV8wMDApIHtcbiAgICBib2R5ID0gYCR7Ym9keS5zbGljZSgwLCAxXzAwMCl94oCmYDtcbiAgfVxuICBjb25zdCB1cmxTdW1tYXJ5ID0gWy4uLm5ldyBTZXQoW3VybCwgcmVzcG9uc2UudXJsXS5maWx0ZXIoQm9vbGVhbikpXS5qb2luKFwiIC0+IFwiKTtcbiAgcmV0dXJuIG5ldyBFcnJvcihcbiAgICBbXG4gICAgICBgUmVxdWVzdCBmYWlsZWQuYCxcbiAgICAgIGAke3Jlc3BvbnNlLnN0YXR1c30gJHtyZXNwb25zZS5zdGF0dXNUZXh0fWAsXG4gICAgICBgJHt1cmxTdW1tYXJ5fWAsXG4gICAgICBib2R5ICYmIGBSZXNwb25zZTpcXG4ke2JvZHl9YCxcbiAgICBdXG4gICAgICAuZmlsdGVyKEJvb2xlYW4pXG4gICAgICAuam9pbihcIlxcblwiKSxcbiAgKTtcbn1cblxuLy8g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4vLyBQcml2YXRlIGhlbHBlcnNcbi8vIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4vKipcbiAqIFR5cGUgZ3VhcmQgZm9yIFdIQVRXRyBgSGVhZGVyc2AuIER1Y2stdHlwZXMgb24gdGhlIHR3byBtZXRob2RzIHRoYXRcbiAqIG1hdHRlciB0byB0aGlzIG1vZHVsZSAoYGdldGAgYW5kIGBnZXRTZXRDb29raWVgKSBzbyBwb2x5ZmlsbGVkXG4gKiBpbXBsZW1lbnRhdGlvbnMgYW5kIEhvbm8ncyBgSG9ub0hlYWRlcnNgIGFyZSBhY2NlcHRlZCB3aXRob3V0XG4gKiBwdWxsaW5nIGBIZWFkZXJzYCBpbiBhcyBhIGhhcmQgZGVwZW5kZW5jeS5cbiAqL1xuZnVuY3Rpb24gaXNIZWFkZXJzKHZhbHVlOiB1bmtub3duKTogdmFsdWUgaXMgSGVhZGVycyB7XG4gIHJldHVybiAoXG4gICAgdHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiICYmXG4gICAgdmFsdWUgIT09IG51bGwgJiZcbiAgICB0eXBlb2YgKHZhbHVlIGFzIEhlYWRlcnMpLmdldCA9PT0gXCJmdW5jdGlvblwiICYmXG4gICAgdHlwZW9mICh2YWx1ZSBhcyBIZWFkZXJzKS5nZXRTZXRDb29raWUgPT09IFwiZnVuY3Rpb25cIlxuICApO1xufVxuXG4vKipcbiAqIGBIZWFkZXJSZWNvcmRgIGlzIGFuIGluZGV4IHNpZ25hdHVyZSwgc28gYFwiaGVhZGVyc1wiIGluIGlucHV0YCBjYW5ub3RcbiAqIGRpc2NyaW1pbmF0ZSBpdCBmcm9tIHRoZSB3cmFwcGVkIGB7IGhlYWRlcnMgfWAgc2hhcGUgYXQgdGhlIHR5cGVcbiAqIGxldmVsLiBUaGlzIGd1YXJkIGluc3BlY3RzIHRoZSBydW50aW1lIHZhbHVlIG9mIGBoZWFkZXJzYDogb25seVxuICogb2JqZWN0cyAoYEhlYWRlcnNgIG9yIGEgbmVzdGVkIHJlY29yZCkgcXVhbGlmeSBhcyB0aGUgd3JhcHBlciBzaGFwZSxcbiAqIG5ldmVyIHN0cmF5IHN0cmluZy9hcnJheSB2YWx1ZXMgdGhhdCBoYXBwZW4gdG8gbGl2ZSB1bmRlciBhIGBoZWFkZXJzYFxuICoga2V5IG9uIGEgaGVhZGVyIHJlY29yZC5cbiAqL1xuZnVuY3Rpb24gaXNXcmFwcGVkKFxuICBpbnB1dDogSGVhZGVyUmVjb3JkIHwgeyBoZWFkZXJzOiBIZWFkZXJzIHwgSGVhZGVyUmVjb3JkIH0sXG4pOiBpbnB1dCBpcyB7IGhlYWRlcnM6IEhlYWRlcnMgfCBIZWFkZXJSZWNvcmQgfSB7XG4gIGNvbnN0IGhlYWRlcnMgPSAoaW5wdXQgYXMgeyBoZWFkZXJzPzogdW5rbm93biB9KS5oZWFkZXJzO1xuICByZXR1cm4gaGVhZGVycyAhPSBudWxsICYmIHR5cGVvZiBoZWFkZXJzID09PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KGhlYWRlcnMpO1xufVxuXG4vKipcbiAqIFBhcnNlIGEgc2luZ2xlIGBDb29raWVgLXN0eWxlIGhlYWRlciBzdHJpbmcgKGBcImE9MTsgYj0yXCJgKSBpbnRvXG4gKiBgb3V0YC4gTmFtZXMgd2l0aG91dCBhIHZhbHVlIGFyZSBza2lwcGVkOyBmaXJzdCBvY2N1cnJlbmNlIHdpbnMgc29cbiAqIGxhdGVyIGR1cGxpY2F0ZXMgYXJlIGlnbm9yZWQuIENvb2tpZSB2YWx1ZXMgYXJlIFVSSS1kZWNvZGVkLlxuICovXG5mdW5jdGlvbiBwYXJzZUNvb2tpZVN0cmluZyhpbnB1dDogc3RyaW5nLCBvdXQ6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4pOiB2b2lkIHtcbiAgZm9yIChjb25zdCBwYXJ0IG9mIGlucHV0LnNwbGl0KFwiO1wiKSkge1xuICAgIGNvbnN0IGVxID0gcGFydC5pbmRleE9mKFwiPVwiKTtcbiAgICBpZiAoZXEgPCAwKSBjb250aW51ZTtcbiAgICBjb25zdCBuYW1lID0gcGFydC5zbGljZSgwLCBlcSkudHJpbSgpO1xuICAgIGlmICghbmFtZSB8fCBuYW1lIGluIG91dCkgY29udGludWU7XG4gICAgY29uc3QgcmF3ID0gcGFydC5zbGljZShlcSArIDEpLnRyaW0oKTtcbiAgICBvdXRbbmFtZV0gPSBkZWNvZGVVUklDb21wb25lbnQocmF3KTtcbiAgfVxufVxuXG4vKipcbiAqIE5vcm1hbGl6ZSBhIHtAbGluayBIZWFkZXJMaWtlfSBpbnB1dCBkb3duIHRvIGVpdGhlciBhIGBIZWFkZXJzYFxuICogaW5zdGFuY2Ugb3IgYSBoZWFkZXIgYFJlY29yZGAuIFJldHVybnMgYG51bGxgIGZvciBtaXNzaW5nIGlucHV0IHNvXG4gKiBjYWxsZXJzIGNhbiBzaG9ydC1jaXJjdWl0IHdpdGhvdXQgYSBzZXBhcmF0ZSBudWxsaXNoIGNoZWNrLlxuICovXG5mdW5jdGlvbiB1bndyYXAoaW5wdXQ6IEhlYWRlckxpa2UgfCBudWxsIHwgdW5kZWZpbmVkKTogSGVhZGVycyB8IEhlYWRlclJlY29yZCB8IG51bGwge1xuICBpZiAoaW5wdXQgPT0gbnVsbCkgcmV0dXJuIG51bGw7XG4gIGlmIChpc0hlYWRlcnMoaW5wdXQpKSByZXR1cm4gaW5wdXQ7XG4gIGlmIChpc1dyYXBwZWQoaW5wdXQpKSByZXR1cm4gaW5wdXQuaGVhZGVycztcbiAgcmV0dXJuIGlucHV0O1xufVxuIl19
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe JSON helpers for the parse-untrusted-text case.
|
|
3
|
+
*
|
|
4
|
+
* `JSON.parse` throws on malformed input and returns `any`, so almost every
|
|
5
|
+
* caller in this repo wrapped it in the same try/catch and then re-narrowed the
|
|
6
|
+
* result by hand. {@link parse} collapses the try/catch (a bad document yields
|
|
7
|
+
* the fallback instead of throwing) and {@link parseRecord} adds the narrowing
|
|
8
|
+
* that untrusted JSON almost always needs before it can be indexed.
|
|
9
|
+
*
|
|
10
|
+
* Use these when the document comes from OUTSIDE the process - a request body,
|
|
11
|
+
* an env var, a config file, a subprocess's stdout, a third-party API. Keep
|
|
12
|
+
* bare `JSON.parse` where a throw is the correct outcome (an internally
|
|
13
|
+
* produced document that must be well-formed).
|
|
14
|
+
*
|
|
15
|
+
* These deliberately do NOT validate shape beyond "is it a record". Reach for
|
|
16
|
+
* a zod schema when the payload has a contract; these only get you from `string`
|
|
17
|
+
* to a safely typed starting point.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Parse JSON text, returning `fallback` (default `undefined`) instead of
|
|
23
|
+
* throwing when `text` is absent or malformed.
|
|
24
|
+
*
|
|
25
|
+
* The return type is caller-asserted, exactly like `JSON.parse` - this only
|
|
26
|
+
* removes the try/catch, it does not validate. Prefer {@link parseRecord} or a
|
|
27
|
+
* schema when the result is indexed.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const config = json.parse<Config>(await readFile(path, "utf8"));
|
|
31
|
+
* const tags = json.parse(process.env.TAGS, []);
|
|
32
|
+
*/
|
|
33
|
+
export declare function parse<T = unknown>(text: unknown, fallback: T): T;
|
|
34
|
+
export declare function parse<T = unknown>(text: unknown): T | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Parse JSON text that is expected to be an object, returning `undefined` when
|
|
37
|
+
* it is absent, malformed, or parses to a non-record (an array, a bare string,
|
|
38
|
+
* `null`, ...).
|
|
39
|
+
*
|
|
40
|
+
* This is the common case for manifests, env-var config blobs, and untyped API
|
|
41
|
+
* payloads: {@link parse} followed by {@link isRecord}, so the result can be
|
|
42
|
+
* indexed without an `as Record<string, unknown>` cast.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const manifest = json.parseRecord(await readFile("package.json", "utf8")) ?? {};
|
|
46
|
+
* const name = manifest.name;
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseRecord(text: unknown): Record<string, unknown> | undefined;
|