@dbx-tools/shared-core 0.9.5 → 0.9.7
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 +45 -35
- package/index.ts +12 -12
- package/lib/index.d.ts +12 -12
- package/lib/index.js +8 -8
- package/lib/src/{async.d.ts → async-utils.d.ts} +20 -0
- package/lib/src/async-utils.js +208 -0
- package/lib/src/brand-utils.js +132 -0
- package/lib/src/error-utils.js +168 -0
- package/lib/src/execution.js +3 -3
- package/lib/src/{function.d.ts → function-utils.d.ts} +1 -1
- package/lib/src/function-utils.js +44 -0
- package/lib/src/log.js +2 -2
- package/lib/src/pattern.js +2 -2
- package/lib/src/{string.d.ts → string-utils.d.ts} +19 -0
- package/lib/src/string-utils.js +520 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/{async.ts → async-utils.ts} +62 -0
- package/src/{error.ts → error-utils.ts} +1 -1
- package/src/execution.ts +2 -2
- package/src/{function.ts → function-utils.ts} +1 -1
- package/src/log.ts +1 -1
- package/src/pattern.ts +1 -1
- package/src/{string.ts → string-utils.ts} +33 -13
- package/lib/src/async.js +0 -169
- package/lib/src/brand.js +0 -132
- package/lib/src/error.js +0 -168
- package/lib/src/function.js +0 -44
- package/lib/src/string.js +0 -513
- /package/lib/src/{brand.d.ts → brand-utils.d.ts} +0 -0
- /package/lib/src/{error.d.ts → error-utils.d.ts} +0 -0
- /package/src/{brand.ts → brand-utils.ts} +0 -0
package/README.md
CHANGED
|
@@ -8,16 +8,16 @@ namespaces so call sites stay explicit:
|
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
asyncUtils,
|
|
12
|
+
brandUtils,
|
|
13
|
+
errorUtils,
|
|
14
14
|
hash,
|
|
15
15
|
http,
|
|
16
16
|
json,
|
|
17
17
|
log,
|
|
18
18
|
net,
|
|
19
19
|
object,
|
|
20
|
-
|
|
20
|
+
stringUtils,
|
|
21
21
|
} from "@dbx-tools/shared-core";
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -46,11 +46,11 @@ Key features:
|
|
|
46
46
|
## Brand Context
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
|
-
import {
|
|
49
|
+
import { brandUtils } from "@dbx-tools/shared-core";
|
|
50
50
|
|
|
51
|
-
const context =
|
|
52
|
-
const jsonSchema =
|
|
53
|
-
const instructions =
|
|
51
|
+
const context = brandUtils.parseBrandContext({ name: "Acme Data" });
|
|
52
|
+
const jsonSchema = brandUtils.brandContextJsonSchema();
|
|
53
|
+
const instructions = brandUtils.brandContextPrompt(context);
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
`BrandContextSchema` validates identity, theme-aware assets, colors,
|
|
@@ -62,7 +62,7 @@ to discover and read YAML/JSON files, and
|
|
|
62
62
|
## Async Control
|
|
63
63
|
|
|
64
64
|
```ts
|
|
65
|
-
for await (const status of
|
|
65
|
+
for await (const status of asyncUtils.poll(fetchStatus, {
|
|
66
66
|
intervalMs: 250,
|
|
67
67
|
timeoutMs: 30_000,
|
|
68
68
|
predicate: (s) => s !== "READY",
|
|
@@ -70,11 +70,11 @@ for await (const status of async.poll(fetchStatus, {
|
|
|
70
70
|
render(status);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
await
|
|
73
|
+
await asyncUtils.sleep(500, abortSignal);
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
`
|
|
77
|
-
`
|
|
76
|
+
`asyncUtils.poll()` is useful for Databricks APIs that expose long-running state.
|
|
77
|
+
`asyncUtils.tieAbortSignal()` and `asyncUtils.sleep()` let route handlers connect caller
|
|
78
78
|
cancellation to background work.
|
|
79
79
|
|
|
80
80
|
## Error Handling
|
|
@@ -83,14 +83,14 @@ cancellation to background work.
|
|
|
83
83
|
try {
|
|
84
84
|
await run();
|
|
85
85
|
} catch (err) {
|
|
86
|
-
logger.warn("run failed", { error:
|
|
87
|
-
const ctx =
|
|
86
|
+
logger.warn("run failed", { error: errorUtils.errorMessage(err) });
|
|
87
|
+
const ctx = errorUtils.errorContext(err);
|
|
88
88
|
return Response.json({ message: ctx.message }, { status: ctx.status ?? 500 });
|
|
89
89
|
}
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
`
|
|
93
|
-
`
|
|
92
|
+
`errorUtils.toError()`, `errorUtils.errorMessage()`, `errorUtils.errorMessages()`, and
|
|
93
|
+
`errorUtils.errorNodes()` normalize unknown thrown values. `errorUtils.errorContext()`
|
|
94
94
|
extracts HTTP-ish status/message detail from nested errors.
|
|
95
95
|
|
|
96
96
|
## Hashes And Ids
|
|
@@ -127,26 +127,35 @@ reading a file this repo generated itself.
|
|
|
127
127
|
## Strings And Descriptions
|
|
128
128
|
|
|
129
129
|
```ts
|
|
130
|
-
const slug =
|
|
131
|
-
const id =
|
|
132
|
-
const unique =
|
|
133
|
-
const
|
|
130
|
+
const slug = stringUtils.toSlug("My Cool Project!");
|
|
131
|
+
const id = stringUtils.toIdentifierWithOptions({ delimiter: "_" }, "Model Name");
|
|
132
|
+
const unique = stringUtils.toUniqueSlug("Send Email", { fallbackPrefix: "tool" });
|
|
133
|
+
const script = stringUtils.dedent(`
|
|
134
|
+
set -euo pipefail
|
|
135
|
+
bun run build
|
|
136
|
+
`);
|
|
137
|
+
const description = stringUtils.toDescription([
|
|
134
138
|
"Answer with SQL first.",
|
|
135
139
|
{ "When data is missing": "Say what is missing." },
|
|
136
140
|
]);
|
|
137
141
|
```
|
|
138
142
|
|
|
139
|
-
`
|
|
143
|
+
`stringUtils.tokenize()`, `toSlug()`, and `toIdentifier()` keep package names, tool
|
|
140
144
|
ids, schema ids, and generated labels consistent. `toDescription()` turns nested
|
|
141
145
|
description data into prompt/tool text without hand-concatenating paragraphs.
|
|
146
|
+
`dedent()` removes common indentation and surrounding blank lines from multiline
|
|
147
|
+
code or configuration templates. Its `trimStart` and `trimEnd` options preserve
|
|
148
|
+
boundary blank lines when generated output requires them, while `trimLineEnd:
|
|
149
|
+
false` preserves trailing spaces and tabs. `dedentLines()` returns the same
|
|
150
|
+
normalization as a line array for file generators.
|
|
142
151
|
|
|
143
152
|
Three helpers exist so call sites stop re-implementing them:
|
|
144
153
|
|
|
145
154
|
```ts
|
|
146
|
-
const label =
|
|
147
|
-
const name =
|
|
148
|
-
const host =
|
|
149
|
-
const allowed =
|
|
155
|
+
const label = stringUtils.toLabel("web_search"); // "Web Search"
|
|
156
|
+
const name = stringUtils.capitalize(segment); // no charAt(0).toUpperCase() idiom
|
|
157
|
+
const host = stringUtils.trimToEmpty(parsed.host); // unknown JSON field -> string
|
|
158
|
+
const allowed = stringUtils.parseList(process.env.ALLOWED_URLS);
|
|
150
159
|
```
|
|
151
160
|
|
|
152
161
|
`toLabel()` and `capitalize()` are the humanizers for identifiers and path
|
|
@@ -325,7 +334,7 @@ parse IPv4 and IPv6 into a shared bigint comparison model.
|
|
|
325
334
|
## Allow-List Patterns
|
|
326
335
|
|
|
327
336
|
```ts
|
|
328
|
-
// One matcher from a config array OR a delimited env
|
|
337
|
+
// One matcher from a config array OR a delimited env stringUtils.
|
|
329
338
|
const forwardable = pattern.toPatternMatcher(["x-mastra-*", "/^x-trace-/", "x-tenant"]);
|
|
330
339
|
|
|
331
340
|
forwardable("x-mastra-model"); // true (glob)
|
|
@@ -337,7 +346,7 @@ forwardable("x-forwarded-user"); // false
|
|
|
337
346
|
Every configurable allow-list in this repo takes the same three shapes, so the
|
|
338
347
|
compilation lives here once: a `/regex/` literal (with optional flags), a
|
|
339
348
|
shell-style glob (`*`, `?`, anchored at both ends, all other characters escaped),
|
|
340
|
-
or a literal compared whole-
|
|
349
|
+
or a literal compared whole-stringUtils. Matching is case-insensitive by default -
|
|
341
350
|
what HTTP header names and email addresses both want - and `caseSensitive` opts
|
|
342
351
|
out.
|
|
343
352
|
|
|
@@ -366,12 +375,12 @@ the request.
|
|
|
366
375
|
## Memoization
|
|
367
376
|
|
|
368
377
|
```ts
|
|
369
|
-
const getRanges =
|
|
378
|
+
const getRanges = functionUtils.memoize(fetchRanges, {
|
|
370
379
|
ttlMs: 24 * 60 * 60 * 1000,
|
|
371
380
|
});
|
|
372
381
|
```
|
|
373
382
|
|
|
374
|
-
`
|
|
383
|
+
`functionUtils.memoize()` caches sync or async factories, evicts rejected
|
|
375
384
|
promises, and supports TTL-based refresh. It is useful for public metadata feeds,
|
|
376
385
|
SDK catalogues, and expensive computed constants.
|
|
377
386
|
|
|
@@ -397,12 +406,13 @@ when disabled.
|
|
|
397
406
|
|
|
398
407
|
## Modules
|
|
399
408
|
|
|
400
|
-
- `
|
|
401
|
-
- `
|
|
409
|
+
- `asyncUtils` - polling, sleep, and abort-signal wiring.
|
|
410
|
+
- `errorUtils` - unknown-error normalization and HTTP-ish error context.
|
|
402
411
|
- `hash` - ids, FNV hashes, and base32 encoding.
|
|
403
412
|
- `json` - non-throwing `parse()` and record-narrowing `parseRecord()`.
|
|
404
|
-
- `
|
|
405
|
-
config lists, descriptions, pluralization,
|
|
413
|
+
- `stringUtils` - tokenization, slugs, identifiers, human labels, string coercion,
|
|
414
|
+
config lists, multiline string/line dedenting, descriptions, pluralization,
|
|
415
|
+
and HTML escaping.
|
|
406
416
|
- `object` - record checks, number/boolean/date/duration coercion, present-only
|
|
407
417
|
field spreading, deep equality, JSON-round-trip guards
|
|
408
418
|
(`isSerializableValue`), canonical identity keys (`toStableKey`), shape types,
|
|
@@ -414,6 +424,6 @@ when disabled.
|
|
|
414
424
|
- `execution` - direct executor fallback, cancellation merging, and result unwrapping.
|
|
415
425
|
- `net` - URL building, email parsing, path matching, IP/CIDR helpers.
|
|
416
426
|
- `token` - JWT payload and scope readers.
|
|
417
|
-
- `
|
|
427
|
+
- `functionUtils` - memoization.
|
|
418
428
|
- `log` - tagged leveled logging.
|
|
419
|
-
- `
|
|
429
|
+
- `brandUtils` - Zod schema, defaults, JSON Schema, and LLM prompt serialization.
|
package/index.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/shared-core";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
7
|
-
export * as
|
|
8
|
-
export * as
|
|
9
|
-
export * as
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.7";
|
|
7
|
+
export * as asyncUtils from "./src/async-utils.ts";
|
|
8
|
+
export * as brandUtils from "./src/brand-utils.ts";
|
|
9
|
+
export * as errorUtils from "./src/error-utils.ts";
|
|
10
10
|
export * as execution from "./src/execution.ts";
|
|
11
|
-
export * as
|
|
11
|
+
export * as functionUtils from "./src/function-utils.ts";
|
|
12
12
|
export * as hash from "./src/hash.ts";
|
|
13
13
|
export * as http from "./src/http.ts";
|
|
14
14
|
export * as json from "./src/json.ts";
|
|
@@ -17,19 +17,19 @@ export * as net from "./src/net.ts";
|
|
|
17
17
|
export * as object from "./src/object.ts";
|
|
18
18
|
export * as pattern from "./src/pattern.ts";
|
|
19
19
|
export * as predicate from "./src/predicate.ts";
|
|
20
|
-
export * as
|
|
20
|
+
export * as stringUtils from "./src/string-utils.ts";
|
|
21
21
|
export * as token from "./src/token.ts";
|
|
22
|
-
export type { PollContext, PollProducer, PollOptions } from "./src/async.ts";
|
|
23
|
-
export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand.ts";
|
|
24
|
-
export type { BrandContext, BrandContextInput, BrandAssetSet } from "./src/brand.ts";
|
|
25
|
-
export type { ErrorContext } from "./src/error.ts";
|
|
22
|
+
export type { ConcurrentErrorMode, MapConcurrentOptions, PollContext, PollProducer, PollOptions } from "./src/async-utils.ts";
|
|
23
|
+
export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand-utils.ts";
|
|
24
|
+
export type { BrandContext, BrandContextInput, BrandAssetSet } from "./src/brand-utils.ts";
|
|
25
|
+
export type { ErrorContext } from "./src/error-utils.ts";
|
|
26
26
|
export type { ExecutionResult, Executor, ExecutionFailure, RunOptions } from "./src/execution.ts";
|
|
27
|
-
export type { MemoizeOptions } from "./src/function.ts";
|
|
27
|
+
export type { MemoizeOptions } from "./src/function-utils.ts";
|
|
28
28
|
export type { HeaderLike } from "./src/http.ts";
|
|
29
29
|
export type { LogLevel, Logger } from "./src/log.ts";
|
|
30
30
|
export type { UrlLike, IpVersion, ParsedIp, Cidr, UrlBuilder, ParseEmailsOptions } from "./src/net.ts";
|
|
31
31
|
export type { Sequence, Container, Collection, OneOrMany, NameLike, NonFunctionKeys, SerializablePrimitive, SerializableValue, ToNumberOptions, ToDateOptions, ToDurationOptions, DeepEqualComparator } from "./src/object.ts";
|
|
32
32
|
export type { PatternOptions } from "./src/pattern.ts";
|
|
33
33
|
export type { PredicateFunction, TypePredicateFunction, PredicateInput, Predicate } from "./src/predicate.ts";
|
|
34
|
-
export type { TokenizeOptions, KeyOptions, IdentifierOptions, Description } from "./src/string.ts";
|
|
34
|
+
export type { TokenizeOptions, KeyOptions, IdentifierOptions, DedentOptions, Description } from "./src/string-utils.ts";
|
|
35
35
|
export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER } from "./src/token.ts";
|
package/lib/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export declare const PACKAGE_IDENTIFIER = "@dbx-tools/shared-core";
|
|
2
|
-
export declare const PACKAGE_VERSION = "0.9.
|
|
3
|
-
export * as
|
|
4
|
-
export * as
|
|
5
|
-
export * as
|
|
2
|
+
export declare const PACKAGE_VERSION = "0.9.7";
|
|
3
|
+
export * as asyncUtils from "./src/async-utils.ts";
|
|
4
|
+
export * as brandUtils from "./src/brand-utils.ts";
|
|
5
|
+
export * as errorUtils from "./src/error-utils.ts";
|
|
6
6
|
export * as execution from "./src/execution.ts";
|
|
7
|
-
export * as
|
|
7
|
+
export * as functionUtils from "./src/function-utils.ts";
|
|
8
8
|
export * as hash from "./src/hash.ts";
|
|
9
9
|
export * as http from "./src/http.ts";
|
|
10
10
|
export * as json from "./src/json.ts";
|
|
@@ -13,19 +13,19 @@ export * as net from "./src/net.ts";
|
|
|
13
13
|
export * as object from "./src/object.ts";
|
|
14
14
|
export * as pattern from "./src/pattern.ts";
|
|
15
15
|
export * as predicate from "./src/predicate.ts";
|
|
16
|
-
export * as
|
|
16
|
+
export * as stringUtils from "./src/string-utils.ts";
|
|
17
17
|
export * as token from "./src/token.ts";
|
|
18
|
-
export type { PollContext, PollProducer, PollOptions } from "./src/async.ts";
|
|
19
|
-
export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand.ts";
|
|
20
|
-
export type { BrandContext, BrandContextInput, BrandAssetSet } from "./src/brand.ts";
|
|
21
|
-
export type { ErrorContext } from "./src/error.ts";
|
|
18
|
+
export type { ConcurrentErrorMode, MapConcurrentOptions, PollContext, PollProducer, PollOptions } from "./src/async-utils.ts";
|
|
19
|
+
export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand-utils.ts";
|
|
20
|
+
export type { BrandContext, BrandContextInput, BrandAssetSet } from "./src/brand-utils.ts";
|
|
21
|
+
export type { ErrorContext } from "./src/error-utils.ts";
|
|
22
22
|
export type { ExecutionResult, Executor, ExecutionFailure, RunOptions } from "./src/execution.ts";
|
|
23
|
-
export type { MemoizeOptions } from "./src/function.ts";
|
|
23
|
+
export type { MemoizeOptions } from "./src/function-utils.ts";
|
|
24
24
|
export type { HeaderLike } from "./src/http.ts";
|
|
25
25
|
export type { LogLevel, Logger } from "./src/log.ts";
|
|
26
26
|
export type { UrlLike, IpVersion, ParsedIp, Cidr, UrlBuilder, ParseEmailsOptions } from "./src/net.ts";
|
|
27
27
|
export type { Sequence, Container, Collection, OneOrMany, NameLike, NonFunctionKeys, SerializablePrimitive, SerializableValue, ToNumberOptions, ToDateOptions, ToDurationOptions, DeepEqualComparator } from "./src/object.ts";
|
|
28
28
|
export type { PatternOptions } from "./src/pattern.ts";
|
|
29
29
|
export type { PredicateFunction, TypePredicateFunction, PredicateInput, Predicate } from "./src/predicate.ts";
|
|
30
|
-
export type { TokenizeOptions, KeyOptions, IdentifierOptions, Description } from "./src/string.ts";
|
|
30
|
+
export type { TokenizeOptions, KeyOptions, IdentifierOptions, DedentOptions, Description } from "./src/string-utils.ts";
|
|
31
31
|
export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER } from "./src/token.ts";
|
package/lib/index.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Regenerated from the exporting modules in ./src.
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/shared-core";
|
|
5
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
-
export * as
|
|
7
|
-
export * as
|
|
8
|
-
export * as
|
|
5
|
+
export const PACKAGE_VERSION = "0.9.7";
|
|
6
|
+
export * as asyncUtils from "./src/async-utils.js";
|
|
7
|
+
export * as brandUtils from "./src/brand-utils.js";
|
|
8
|
+
export * as errorUtils from "./src/error-utils.js";
|
|
9
9
|
export * as execution from "./src/execution.js";
|
|
10
|
-
export * as
|
|
10
|
+
export * as functionUtils from "./src/function-utils.js";
|
|
11
11
|
export * as hash from "./src/hash.js";
|
|
12
12
|
export * as http from "./src/http.js";
|
|
13
13
|
export * as json from "./src/json.js";
|
|
@@ -16,8 +16,8 @@ export * as net from "./src/net.js";
|
|
|
16
16
|
export * as object from "./src/object.js";
|
|
17
17
|
export * as pattern from "./src/pattern.js";
|
|
18
18
|
export * as predicate from "./src/predicate.js";
|
|
19
|
-
export * as
|
|
19
|
+
export * as stringUtils from "./src/string-utils.js";
|
|
20
20
|
export * as token from "./src/token.js";
|
|
21
|
-
export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand.js";
|
|
21
|
+
export { DEFAULT_BRAND_ASSETS, BrandAssetSetSchema, BrandColorsSchema, BrandVoiceSchema, BrandContextSchema, defaultBrandContext } from "./src/brand-utils.js";
|
|
22
22
|
export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER } from "./src/token.js";
|
|
23
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyx3QkFBd0IsQ0FBQztBQUMzRCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxVQUFVLE1BQU0sc0JBQXNCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQkFBc0IsQ0FBQztBQUNuRCxPQUFPLEtBQUssVUFBVSxNQUFNLHNCQUFzQixDQUFDO0FBQ25ELE9BQU8sS0FBSyxTQUFTLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxLQUFLLGFBQWEsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssR0FBRyxNQUFNLGNBQWMsQ0FBQztBQUNwQyxPQUFPLEtBQUssR0FBRyxNQUFNLGNBQWMsQ0FBQztBQUNwQyxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxLQUFLLFNBQVMsTUFBTSxvQkFBb0IsQ0FBQztBQUNoRCxPQUFPLEtBQUssV0FBVyxNQUFNLHVCQUF1QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxLQUFLLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLG1CQUFtQixFQUFFLGlCQUFpQixFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFZL0osT0FBTyxFQUFFLG1CQUFtQixFQUFFLGNBQWMsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfVkVSU0lPTiA9IFwiMC45LjdcIjtcbmV4cG9ydCAqIGFzIGFzeW5jVXRpbHMgZnJvbSBcIi4vc3JjL2FzeW5jLXV0aWxzLnRzXCI7XG5leHBvcnQgKiBhcyBicmFuZFV0aWxzIGZyb20gXCIuL3NyYy9icmFuZC11dGlscy50c1wiO1xuZXhwb3J0ICogYXMgZXJyb3JVdGlscyBmcm9tIFwiLi9zcmMvZXJyb3ItdXRpbHMudHNcIjtcbmV4cG9ydCAqIGFzIGV4ZWN1dGlvbiBmcm9tIFwiLi9zcmMvZXhlY3V0aW9uLnRzXCI7XG5leHBvcnQgKiBhcyBmdW5jdGlvblV0aWxzIGZyb20gXCIuL3NyYy9mdW5jdGlvbi11dGlscy50c1wiO1xuZXhwb3J0ICogYXMgaGFzaCBmcm9tIFwiLi9zcmMvaGFzaC50c1wiO1xuZXhwb3J0ICogYXMgaHR0cCBmcm9tIFwiLi9zcmMvaHR0cC50c1wiO1xuZXhwb3J0ICogYXMganNvbiBmcm9tIFwiLi9zcmMvanNvbi50c1wiO1xuZXhwb3J0ICogYXMgbG9nIGZyb20gXCIuL3NyYy9sb2cudHNcIjtcbmV4cG9ydCAqIGFzIG5ldCBmcm9tIFwiLi9zcmMvbmV0LnRzXCI7XG5leHBvcnQgKiBhcyBvYmplY3QgZnJvbSBcIi4vc3JjL29iamVjdC50c1wiO1xuZXhwb3J0ICogYXMgcGF0dGVybiBmcm9tIFwiLi9zcmMvcGF0dGVybi50c1wiO1xuZXhwb3J0ICogYXMgcHJlZGljYXRlIGZyb20gXCIuL3NyYy9wcmVkaWNhdGUudHNcIjtcbmV4cG9ydCAqIGFzIHN0cmluZ1V0aWxzIGZyb20gXCIuL3NyYy9zdHJpbmctdXRpbHMudHNcIjtcbmV4cG9ydCAqIGFzIHRva2VuIGZyb20gXCIuL3NyYy90b2tlbi50c1wiO1xuZXhwb3J0IHR5cGUgeyBDb25jdXJyZW50RXJyb3JNb2RlLCBNYXBDb25jdXJyZW50T3B0aW9ucywgUG9sbENvbnRleHQsIFBvbGxQcm9kdWNlciwgUG9sbE9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvYXN5bmMtdXRpbHMudHNcIjtcbmV4cG9ydCB7IERFRkFVTFRfQlJBTkRfQVNTRVRTLCBCcmFuZEFzc2V0U2V0U2NoZW1hLCBCcmFuZENvbG9yc1NjaGVtYSwgQnJhbmRWb2ljZVNjaGVtYSwgQnJhbmRDb250ZXh0U2NoZW1hLCBkZWZhdWx0QnJhbmRDb250ZXh0IH0gZnJvbSBcIi4vc3JjL2JyYW5kLXV0aWxzLnRzXCI7XG5leHBvcnQgdHlwZSB7IEJyYW5kQ29udGV4dCwgQnJhbmRDb250ZXh0SW5wdXQsIEJyYW5kQXNzZXRTZXQgfSBmcm9tIFwiLi9zcmMvYnJhbmQtdXRpbHMudHNcIjtcbmV4cG9ydCB0eXBlIHsgRXJyb3JDb250ZXh0IH0gZnJvbSBcIi4vc3JjL2Vycm9yLXV0aWxzLnRzXCI7XG5leHBvcnQgdHlwZSB7IEV4ZWN1dGlvblJlc3VsdCwgRXhlY3V0b3IsIEV4ZWN1dGlvbkZhaWx1cmUsIFJ1bk9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvZXhlY3V0aW9uLnRzXCI7XG5leHBvcnQgdHlwZSB7IE1lbW9pemVPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2Z1bmN0aW9uLXV0aWxzLnRzXCI7XG5leHBvcnQgdHlwZSB7IEhlYWRlckxpa2UgfSBmcm9tIFwiLi9zcmMvaHR0cC50c1wiO1xuZXhwb3J0IHR5cGUgeyBMb2dMZXZlbCwgTG9nZ2VyIH0gZnJvbSBcIi4vc3JjL2xvZy50c1wiO1xuZXhwb3J0IHR5cGUgeyBVcmxMaWtlLCBJcFZlcnNpb24sIFBhcnNlZElwLCBDaWRyLCBVcmxCdWlsZGVyLCBQYXJzZUVtYWlsc09wdGlvbnMgfSBmcm9tIFwiLi9zcmMvbmV0LnRzXCI7XG5leHBvcnQgdHlwZSB7IFNlcXVlbmNlLCBDb250YWluZXIsIENvbGxlY3Rpb24sIE9uZU9yTWFueSwgTmFtZUxpa2UsIE5vbkZ1bmN0aW9uS2V5cywgU2VyaWFsaXphYmxlUHJpbWl0aXZlLCBTZXJpYWxpemFibGVWYWx1ZSwgVG9OdW1iZXJPcHRpb25zLCBUb0RhdGVPcHRpb25zLCBUb0R1cmF0aW9uT3B0aW9ucywgRGVlcEVxdWFsQ29tcGFyYXRvciB9IGZyb20gXCIuL3NyYy9vYmplY3QudHNcIjtcbmV4cG9ydCB0eXBlIHsgUGF0dGVybk9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvcGF0dGVybi50c1wiO1xuZXhwb3J0IHR5cGUgeyBQcmVkaWNhdGVGdW5jdGlvbiwgVHlwZVByZWRpY2F0ZUZ1bmN0aW9uLCBQcmVkaWNhdGVJbnB1dCwgUHJlZGljYXRlIH0gZnJvbSBcIi4vc3JjL3ByZWRpY2F0ZS50c1wiO1xuZXhwb3J0IHR5cGUgeyBUb2tlbml6ZU9wdGlvbnMsIEtleU9wdGlvbnMsIElkZW50aWZpZXJPcHRpb25zLCBEZWRlbnRPcHRpb25zLCBEZXNjcmlwdGlvbiB9IGZyb20gXCIuL3NyYy9zdHJpbmctdXRpbHMudHNcIjtcbmV4cG9ydCB7IEFDQ0VTU19UT0tFTl9IRUFERVIsIFVTRVJfSURfSEVBREVSLCBVU0VSX0VNQUlMX0hFQURFUiB9IGZyb20gXCIuL3NyYy90b2tlbi50c1wiO1xuIl19
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/** Controls how {@link mapConcurrent} reacts when a worker rejects. */
|
|
2
|
+
export type ConcurrentErrorMode = "fail-fast" | "settle";
|
|
3
|
+
/** Bounded-worker settings for {@link mapConcurrent}. */
|
|
4
|
+
export interface MapConcurrentOptions {
|
|
5
|
+
/** Maximum number of callbacks running at once. */
|
|
6
|
+
concurrency: number;
|
|
7
|
+
/**
|
|
8
|
+
* `"fail-fast"` stops claiming new work after the first rejection.
|
|
9
|
+
* `"settle"` attempts every value and throws an `AggregateError` afterward.
|
|
10
|
+
*/
|
|
11
|
+
errorMode?: ConcurrentErrorMode;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Map values through a bounded worker pool while preserving input order.
|
|
15
|
+
*
|
|
16
|
+
* Active callbacks are always allowed to settle. In fail-fast mode no new
|
|
17
|
+
* callback starts after the first observed rejection. Settle mode attempts
|
|
18
|
+
* every value and reports all failures in input order through `AggregateError`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function mapConcurrent<T, R>(values: readonly T[], callback: (value: T, index: number) => R | PromiseLike<R>, options: MapConcurrentOptions): Promise<R[]>;
|
|
1
21
|
export declare function boundedRetryDelay(attempt: number, delaysMs?: readonly number[]): number;
|
|
2
22
|
/**
|
|
3
23
|
* Per-iteration context handed to {@link PollProducer} and the
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cancellation-aware async primitives: an abortable {@link sleep}, an
|
|
3
|
+
* {@link AbortController} linker ({@link tieAbortSignal}), and a periodic
|
|
4
|
+
* {@link poll} generator. Dependency-free; `poll`'s `"distinct"` filter
|
|
5
|
+
* uses the local {@link deepEqual}.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { deepEqual } from "./object.js";
|
|
10
|
+
const DEFAULT_RETRY_DELAYS_MS = [1_000, 2_000, 5_000, 10_000, 30_000];
|
|
11
|
+
/**
|
|
12
|
+
* Map values through a bounded worker pool while preserving input order.
|
|
13
|
+
*
|
|
14
|
+
* Active callbacks are always allowed to settle. In fail-fast mode no new
|
|
15
|
+
* callback starts after the first observed rejection. Settle mode attempts
|
|
16
|
+
* every value and reports all failures in input order through `AggregateError`.
|
|
17
|
+
*/
|
|
18
|
+
export async function mapConcurrent(values, callback, options) {
|
|
19
|
+
const { concurrency, errorMode = "fail-fast" } = options;
|
|
20
|
+
if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
|
|
21
|
+
throw new RangeError(`concurrency must be a positive integer, got ${String(concurrency)}`);
|
|
22
|
+
}
|
|
23
|
+
if (values.length === 0)
|
|
24
|
+
return [];
|
|
25
|
+
const results = new Array(values.length);
|
|
26
|
+
const failures = [];
|
|
27
|
+
let next = 0;
|
|
28
|
+
let failed = false;
|
|
29
|
+
const worker = async () => {
|
|
30
|
+
while (next < values.length && !(failed && errorMode === "fail-fast")) {
|
|
31
|
+
const index = next++;
|
|
32
|
+
try {
|
|
33
|
+
results[index] = await callback(values[index], index);
|
|
34
|
+
}
|
|
35
|
+
catch (reason) {
|
|
36
|
+
failures.push({ index, reason });
|
|
37
|
+
failed = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, () => worker()));
|
|
42
|
+
if (failures.length === 0)
|
|
43
|
+
return results;
|
|
44
|
+
failures.sort((left, right) => left.index - right.index);
|
|
45
|
+
if (errorMode === "settle") {
|
|
46
|
+
throw new AggregateError(failures.map(({ reason }) => reason), `${failures.length} concurrent operation${failures.length === 1 ? "" : "s"} failed`);
|
|
47
|
+
}
|
|
48
|
+
throw failures[0].reason;
|
|
49
|
+
}
|
|
50
|
+
export function boundedRetryDelay(attempt, delaysMs = DEFAULT_RETRY_DELAYS_MS) {
|
|
51
|
+
if (!delaysMs.length)
|
|
52
|
+
return 0;
|
|
53
|
+
return delaysMs[Math.min(Math.max(0, attempt), delaysMs.length - 1)];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Async iterable that drives a periodic poll. Each iteration:
|
|
57
|
+
*
|
|
58
|
+
* 1. Builds a {@link PollContext} (`attempt`, `previous`, `signal`,
|
|
59
|
+
* shared `attributes`) and calls `producer(ctx)`; yields the
|
|
60
|
+
* resolved value (subject to `filter`).
|
|
61
|
+
* 2. Evaluates `options.predicate(value, ctx)`; stops when it
|
|
62
|
+
* returns (or resolves to) `false`.
|
|
63
|
+
* 3. Sleeps `options.intervalMs` before the next attempt.
|
|
64
|
+
*
|
|
65
|
+
* The first call runs immediately (no leading sleep) so the consumer
|
|
66
|
+
* sees a value without waiting an interval. Errors thrown by
|
|
67
|
+
* `producer` propagate through the generator.
|
|
68
|
+
*
|
|
69
|
+
* `poll` always creates an internal `AbortController` and exposes
|
|
70
|
+
* `internal.signal` as `ctx.signal`, so producers can rely on a
|
|
71
|
+
* defined signal without a nullish check. The external
|
|
72
|
+
* `options.signal` is tied in, and a `try/finally` aborts the
|
|
73
|
+
* internal signal when the consumer breaks out of the `for await`
|
|
74
|
+
* (or the loop throws), so any producer work still holding the
|
|
75
|
+
* signal sees the cancellation too.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* for await (const msg of poll(
|
|
79
|
+
* async ({ signal }) =>
|
|
80
|
+
* client.genie.getMessage({ ... }, { abortSignal: signal }),
|
|
81
|
+
* {
|
|
82
|
+
* intervalMs: 250,
|
|
83
|
+
* predicate: (m) => !TERMINAL_STATUSES.has(m.status),
|
|
84
|
+
* signal: controller.signal,
|
|
85
|
+
* },
|
|
86
|
+
* )) {
|
|
87
|
+
* render(msg);
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
90
|
+
export async function* poll(producer, options) {
|
|
91
|
+
const { intervalMs, predicate, signal, attributes, timeoutMs } = options;
|
|
92
|
+
const controller = new AbortController();
|
|
93
|
+
if (signal)
|
|
94
|
+
tieAbortSignal(controller, signal);
|
|
95
|
+
if (timeoutMs !== undefined) {
|
|
96
|
+
tieAbortSignal(controller, AbortSignal.timeout(timeoutMs));
|
|
97
|
+
}
|
|
98
|
+
// Single shared attributes object so writes from one iteration are
|
|
99
|
+
// visible on the next. `{} as A` is safe because either the caller
|
|
100
|
+
// supplied `attributes` (typed) or `A` defaulted to the unknown
|
|
101
|
+
// record shape (in which case `{}` satisfies it).
|
|
102
|
+
const sharedAttributes = attributes ?? {};
|
|
103
|
+
try {
|
|
104
|
+
let previous;
|
|
105
|
+
for (let attempt = 0;; attempt++) {
|
|
106
|
+
controller.signal.throwIfAborted();
|
|
107
|
+
const ctx = {
|
|
108
|
+
attempt,
|
|
109
|
+
previous,
|
|
110
|
+
signal: controller.signal,
|
|
111
|
+
attributes: sharedAttributes,
|
|
112
|
+
};
|
|
113
|
+
const value = await producer(ctx);
|
|
114
|
+
if (options.filter) {
|
|
115
|
+
if (options.filter === "distinct") {
|
|
116
|
+
if (deepEqual(previous, value)) {
|
|
117
|
+
await sleep(intervalMs, controller.signal);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (!(await options.filter(value, ctx))) {
|
|
122
|
+
await sleep(intervalMs, controller.signal);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
yield value;
|
|
127
|
+
if (predicate && !(await predicate(value, ctx)))
|
|
128
|
+
return;
|
|
129
|
+
await sleep(intervalMs, controller.signal);
|
|
130
|
+
previous = value;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
controller.abort();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Tie a child `AbortController` to a parent signal. The child aborts
|
|
139
|
+
* whenever the parent aborts; aborting the child does not affect the
|
|
140
|
+
* parent (so a fetch-level cancel doesn't tear down the main poll loop).
|
|
141
|
+
*/
|
|
142
|
+
export function tieAbortSignal(child, parent) {
|
|
143
|
+
if (!parent) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
else if (parent.aborted) {
|
|
147
|
+
child.abort(parent.reason);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
parent.addEventListener("abort", () => child.abort(parent.reason), {
|
|
151
|
+
once: true,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Combine several optional cancellation sources into one signal that aborts
|
|
156
|
+
* as soon as any of them does.
|
|
157
|
+
*
|
|
158
|
+
* The usual caller is an operation that has to honor more than one source at
|
|
159
|
+
* once - a caller's own signal (a closed connection, an agent run being
|
|
160
|
+
* cancelled) plus one derived from a timeout - where the awaited I/O accepts
|
|
161
|
+
* only a single signal. Absent sources are ignored, and a lone signal is
|
|
162
|
+
* returned as-is so the common path allocates nothing. Returns `undefined`
|
|
163
|
+
* only when every input is absent, which callers can pass straight through
|
|
164
|
+
* to an optional `signal` parameter.
|
|
165
|
+
*
|
|
166
|
+
* Aborting an input aborts the result (carrying that input's `reason`);
|
|
167
|
+
* nothing propagates back the other way.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* await fetch(url, { signal: combineAbortSignals(req.signal, timeout.signal) });
|
|
171
|
+
*/
|
|
172
|
+
export function combineAbortSignals(...signals) {
|
|
173
|
+
const present = signals.filter((signal) => signal !== undefined);
|
|
174
|
+
if (present.length <= 1)
|
|
175
|
+
return present[0];
|
|
176
|
+
return AbortSignal.any(present);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Promisified `setTimeout` that wakes up early (and rejects with
|
|
180
|
+
* `signal.reason`) when `signal` aborts mid-wait. Short-circuits to a
|
|
181
|
+
* rejected promise when the signal is already aborted on entry, so the
|
|
182
|
+
* abort path is consistent regardless of whether the wait actually
|
|
183
|
+
* started.
|
|
184
|
+
*
|
|
185
|
+
* Use as the building block for any "wait, but cancel cleanly" pattern -
|
|
186
|
+
* inter-poll backoff, pacing loops, retry timers, long-poll budgets - so
|
|
187
|
+
* cancellation always rejects with the caller's `signal.reason` rather
|
|
188
|
+
* than silently resolving after the timer expires.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* await sleep(250, req.signal);
|
|
192
|
+
*/
|
|
193
|
+
export function sleep(ms, signal) {
|
|
194
|
+
if (signal?.aborted)
|
|
195
|
+
return Promise.reject(signal.reason);
|
|
196
|
+
return new Promise((resolve, reject) => {
|
|
197
|
+
const onAbort = () => {
|
|
198
|
+
clearTimeout(timer);
|
|
199
|
+
reject(signal.reason);
|
|
200
|
+
};
|
|
201
|
+
const timer = setTimeout(() => {
|
|
202
|
+
signal?.removeEventListener("abort", onAbort);
|
|
203
|
+
resolve();
|
|
204
|
+
}, ms);
|
|
205
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXN5bmMtdXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXN5bmMtdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7R0FPRztBQUNILE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFeEMsTUFBTSx1QkFBdUIsR0FBRyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQVUsQ0FBQztBQWdCL0U7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxhQUFhLENBQ2pDLE1BQW9CLEVBQ3BCLFFBQXlELEVBQ3pELE9BQTZCO0lBRTdCLE1BQU0sRUFBRSxXQUFXLEVBQUUsU0FBUyxHQUFHLFdBQVcsRUFBRSxHQUFHLE9BQU8sQ0FBQztJQUN6RCxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxXQUFXLENBQUMsSUFBSSxXQUFXLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDMUQsTUFBTSxJQUFJLFVBQVUsQ0FBQywrQ0FBK0MsTUFBTSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM3RixDQUFDO0lBQ0QsSUFBSSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUVuQyxNQUFNLE9BQU8sR0FBRyxJQUFJLEtBQUssQ0FBSSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDNUMsTUFBTSxRQUFRLEdBQThDLEVBQUUsQ0FBQztJQUMvRCxJQUFJLElBQUksR0FBRyxDQUFDLENBQUM7SUFDYixJQUFJLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFFbkIsTUFBTSxNQUFNLEdBQUcsS0FBSyxJQUFtQixFQUFFO1FBQ3ZDLE9BQU8sSUFBSSxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDLE1BQU0sSUFBSSxTQUFTLEtBQUssV0FBVyxDQUFDLEVBQUUsQ0FBQztZQUN0RSxNQUFN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LENBQUMsR0FBRyxFQUFFO1lBQzVCLE1BQU0sRUFBRSxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDOUMsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDUCxNQUFNLEVBQUUsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzdELENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ2FuY2VsbGF0aW9uLWF3YXJlIGFzeW5jIHByaW1pdGl2ZXM6IGFuIGFib3J0YWJsZSB7QGxpbmsgc2xlZXB9LCBhblxuICoge0BsaW5rIEFib3J0Q29udHJvbGxlcn0gbGlua2VyICh7QGxpbmsgdGllQWJvcnRTaWduYWx9KSwgYW5kIGEgcGVyaW9kaWNcbiAqIHtAbGluayBwb2xsfSBnZW5lcmF0b3IuIERlcGVuZGVuY3ktZnJlZTsgYHBvbGxgJ3MgYFwiZGlzdGluY3RcImAgZmlsdGVyXG4gKiB1c2VzIHRoZSBsb2NhbCB7QGxpbmsgZGVlcEVxdWFsfS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cbmltcG9ydCB7IGRlZXBFcXVhbCB9IGZyb20gXCIuL29iamVjdC50c1wiO1xuXG5jb25zdCBERUZBVUxUX1JFVFJZX0RFTEFZU19NUyA9IFsxXzAwMCwgMl8wMDAsIDVfMDAwLCAxMF8wMDAsIDMwXzAwMF0gYXMgY29uc3Q7XG5cbi8qKiBDb250cm9scyBob3cge0BsaW5rIG1hcENvbmN1cnJlbnR9IHJlYWN0cyB3aGVuIGEgd29ya2VyIHJlamVjdHMuICovXG5leHBvcnQgdHlwZSBDb25jdXJyZW50RXJyb3JNb2RlID0gXCJmYWlsLWZhc3RcIiB8IFwic2V0dGxlXCI7XG5cbi8qKiBCb3VuZGVkLXdvcmtlciBzZXR0aW5ncyBmb3Ige0BsaW5rIG1hcENvbmN1cnJlbnR9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBNYXBDb25jdXJyZW50T3B0aW9ucyB7XG4gIC8qKiBNYXhpbXVtIG51bWJlciBvZiBjYWxsYmFja3MgcnVubmluZyBhdCBvbmNlLiAqL1xuICBjb25jdXJyZW5jeTogbnVtYmVyO1xuICAvKipcbiAgICogYFwiZmFpbC1mYXN0XCJgIHN0b3BzIGNsYWltaW5nIG5ldyB3b3JrIGFmdGVyIHRoZSBmaXJzdCByZWplY3Rpb24uXG4gICAqIGBcInNldHRsZVwiYCBhdHRlbXB0cyBldmVyeSB2YWx1ZSBhbmQgdGhyb3dzIGFuIGBBZ2dyZWdhdGVFcnJvcmAgYWZ0ZXJ3YXJkLlxuICAgKi9cbiAgZXJyb3JNb2RlPzogQ29uY3VycmVudEVycm9yTW9kZTtcbn1cblxuLyoqXG4gKiBNYXAgdmFsdWVzIHRocm91Z2ggYSBib3VuZGVkIHdvcmtlciBwb29sIHdoaWxlIHByZXNlcnZpbmcgaW5wdXQgb3JkZXIuXG4gKlxuICogQWN0aXZlIGNhbGxiYWNrcyBhcmUgYWx3YXlzIGFsbG93ZWQgdG8gc2V0dGxlLiBJbiBmYWlsLWZhc3QgbW9kZSBubyBuZXdcbiAqIGNhbGxiYWNrIHN0YXJ0cyBhZnRlciB0aGUgZmlyc3Qgb2JzZXJ2ZWQgcmVqZWN0aW9uLiBTZXR0bGUgbW9kZSBhdHRlbXB0c1xuICogZXZlcnkgdmFsdWUgYW5kIHJlcG9ydHMgYWxsIGZhaWx1cmVzIGluIGlucHV0IG9yZGVyIHRocm91Z2ggYEFnZ3JlZ2F0ZUVycm9yYC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1hcENvbmN1cnJlbnQ8VCwgUj4oXG4gIHZhbHVlczogcmVhZG9ubHkgVFtdLFxuICBjYWxsYmFjazogKHZhbHVlOiBULCBpbmRleDogbnVtYmVyKSA9PiBSIHwgUHJvbWlzZUxpa2U8Uj4sXG4gIG9wdGlvbnM6IE1hcENvbmN1cnJlbnRPcHRpb25zLFxuKTogUHJvbWlzZTxSW10+IHtcbiAgY29uc3QgeyBjb25jdXJyZW5jeSwgZXJyb3JNb2RlID0gXCJmYWlsLWZhc3RcIiB9ID0gb3B0aW9ucztcbiAgaWYgKCFOdW1iZXIuaXNTYWZlSW50ZWdlcihjb25jdXJyZW5jeSkgfHwgY29uY3VycmVuY3kgPCAxKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoYGNvbmN1cnJlbmN5IG11c3QgYmUgYSBwb3NpdGl2ZSBpbnRlZ2VyLCBnb3QgJHtTdHJpbmcoY29uY3VycmVuY3kpfWApO1xuICB9XG4gIGlmICh2YWx1ZXMubGVuZ3RoID09PSAwKSByZXR1cm4gW107XG5cbiAgY29uc3QgcmVzdWx0cyA9IG5ldyBBcnJheTxSPih2YWx1ZXMubGVuZ3RoKTtcbiAgY29uc3QgZmFpbHVyZXM6IEFycmF5PHsgaW5kZXg6IG51bWJlcjsgcmVhc29uOiB1bmtub3duIH0+ID0gW107XG4gIGxldCBuZXh0ID0gMDtcbiAgbGV0IGZhaWxlZCA9IGZhbHNlO1xuXG4gIGNvbnN0IHdvcmtlciA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICB3aGlsZSAobmV4dCA8IHZhbHVlcy5sZW5ndGggJiYgIShmYWlsZWQgJiYgZXJyb3JNb2RlID09PSBcImZhaWwtZmFzdFwiKSkge1xuICAgICAgY29uc3QgaW5kZXggPSBuZXh0Kys7XG4gICAgICB0cnkge1xuICAgICAgICByZXN1bHRzW2luZGV4XSA9IGF3YWl0IGNhbGxiYWNrKHZhbHVlc1tpbmRleF0hLCBpbmRleCk7XG4gICAgICB9IGNhdGNoIChyZWFzb24pIHtcbiAgICAgICAgZmFpbHVyZXMucHVzaCh7IGluZGV4LCByZWFzb24gfSk7XG4gICAgICAgIGZhaWxlZCA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oeyBsZW5ndGg6IE1hdGgubWluKGNvbmN1cnJlbmN5LCB2YWx1ZXMubGVuZ3RoKSB9LCAoKSA9PiB3b3JrZXIoKSkpO1xuICBpZiAoZmFpbHVyZXMubGVuZ3RoID09PSAwKSByZXR1cm4gcmVzdWx0cztcblxuICBmYWlsdXJlcy5zb3J0KChsZWZ0LCByaWdodCkgPT4gbGVmdC5pbmRleCAtIHJpZ2h0LmluZGV4KTtcbiAgaWYgKGVycm9yTW9kZSA9PT0gXCJzZXR0bGVcIikge1xuICAgIHRocm93IG5ldyBBZ2dyZWdhdGVFcnJvcihcbiAgICAgIGZhaWx1cmVzLm1hcCgoeyByZWFzb24gfSkgPT4gcmVhc29uKSxcbiAgICAgIGAke2ZhaWx1cmVzLmxlbmd0aH0gY29uY3VycmVudCBvcGVyYXRpb24ke2ZhaWx1cmVzLmxlbmd0aCA9PT0gMSA/IFwiXCIgOiBcInNcIn0gZmFpbGVkYCxcbiAgICApO1xuICB9XG4gIHRocm93IGZhaWx1cmVzWzBdIS5yZWFzb247XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBib3VuZGVkUmV0cnlEZWxheShcbiAgYXR0ZW1wdDogbnVtYmVyLFxuICBkZWxheXNNczogcmVhZG9ubHkgbnVtYmVyW10gPSBERUZBVUxUX1JFVFJZX0RFTEFZU19NUyxcbik6IG51bWJlciB7XG4gIGlmICghZGVsYXlzTXMubGVuZ3RoKSByZXR1cm4gMDtcbiAgcmV0dXJuIGRlbGF5c01zW01hdGgubWluKE1hdGgubWF4KDAsIGF0dGVtcHQpLCBkZWxheXNNcy5sZW5ndGggLSAxKV0hO1xufVxuXG4vKipcbiAqIFBlci1pdGVyYXRpb24gY29udGV4dCBoYW5kZWQgdG8ge0BsaW5rIFBvbGxQcm9kdWNlcn0gYW5kIHRoZVxuICogcHJlZGljYXRlIG9uIGVhY2ggc3RlcCBvZiBhIHtAbGluayBwb2xsfSBsb29wLiBCdW5kbGVzIHRoZVxuICogaXRlcmF0aW9uIG1ldGFkYXRhIHNvIHRoZSBjYWxsIHNpZ25hdHVyZXMgc3RheSBzdGFibGUgYXMgYHBvbGxgXG4gKiBncm93cyBhZGRpdGlvbmFsIGZpZWxkcy5cbiAqXG4gKiBgc2lnbmFsYCBpcyBvd25lZCBieSBgcG9sbGA6IGl0IHRyYWNrcyB0aGUgZXh0ZXJuYWxcbiAqIGBQb2xsT3B0aW9ucy5zaWduYWxgICh3aGVuIHN1cHBsaWVkKSBhbmQgYWxzbyBmaXJlcyB3aGVuIHRoZVxuICogY29uc3VtZXIgYnJlYWtzIG91dCBvZiB0aGUgbG9vcCwgc28gcHJvZHVjZXJzIGNhbiBmb3J3YXJkIGl0IHRvXG4gKiBhbnkgaW4tZmxpZ2h0IHdvcmsgKGBmZXRjaGAsIFNESyBjYWxscywgZXRjLikgYW5kIGhhdmUgYSBzaW5nbGVcbiAqIGNhbmNlbGxhdGlvbiBzb3VyY2UgdGVhciBkb3duIGJvdGggdGhlIHJlcXVlc3QgYW5kIHRoZSBsb29wLlxuICpcbiAqIGBhdHRyaWJ1dGVzYCBpcyBhIG11dGFibGUgc2NyYXRjaHBhZCBzaGFyZWQgYWNyb3NzIGV2ZXJ5XG4gKiBpdGVyYXRpb24gb2YgYSBzaW5nbGUgYHBvbGxgIHJ1bi4gVGhlIHNhbWUgb2JqZWN0IHJlZmVyZW5jZSBpc1xuICogcGFzc2VkIGVhY2ggY2FsbCBzbyB3cml0ZXMgZnJvbSBvbmUgaXRlcmF0aW9uIGFyZSB2aXNpYmxlIHRvIHRoZVxuICogbmV4dCAtIHVzZWZ1bCBmb3Igc3Rhc2hpbmcgcGVyLWxvb3Agc3RhdGUgKHJldHJ5IGNvdW50ZXJzLCBzdGFydFxuICogdGltZXN0YW1wcywgYW55dGhpbmcgeW91J2Qgb3RoZXJ3aXNlIGNsb3NlIG92ZXIgdmlhIGEgbGV0KS5cbiAqIEdlbmVyaWMgYEFgIGxldHMgY2FsbGVycyB0eXBlIHRoZSBiYWc7IGRlZmF1bHRzIHRvXG4gKiBgUmVjb3JkPHN0cmluZywgdW5rbm93bj5gLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFBvbGxDb250ZXh0PFQsIEEgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4ge1xuICAvKiogWmVyby1iYXNlZCBpdGVyYXRpb24gaW5kZXggKGAwYCBvbiB0aGUgZmlyc3QgY2FsbCkuICovXG4gIGF0dGVtcHQ6IG51bWJlcjtcbiAgLyoqIFZhbHVlIHlpZWxkZWQgb24gdGhlIHByaW9yIGl0ZXJhdGlvbjsgYHVuZGVmaW5lZGAgb24gdGhlIGZpcnN0LiAqL1xuICBwcmV2aW91czogVCB8IHVuZGVmaW5lZDtcbiAgLyoqIENhbmNlbGxhdGlvbiBoYW5kbGUuIEFsd2F5cyBkZWZpbmVkOyBmb3J3YXJkIHRvIGluLWZsaWdodCB3b3JrLiAqL1xuICBzaWduYWw6IEFib3J0U2lnbmFsO1xuICAvKiogUGVyLXJ1biBtdXRhYmxlIHNjcmF0Y2hwYWQgc2hhcmVkIGFjcm9zcyBpdGVyYXRpb25zLiAqL1xuICBhdHRyaWJ1dGVzOiBBO1xufVxuXG4vKiogT25lIHN0ZXAgb2YgYSB7QGxpbmsgcG9sbH0gbG9vcC4gU2VlIHtAbGluayBQb2xsQ29udGV4dH0uICovXG5leHBvcnQgdHlwZSBQb2xsUHJvZHVjZXI8VCwgQSA9IFJlY29yZDxzdHJpbmcsIHVua25vd24+PiA9IChcbiAgY3R4OiBQb2xsQ29udGV4dDxULCBBPixcbikgPT4gVCB8IFByb21pc2VMaWtlPFQ+O1xuXG4vKipcbiAqIENvbnRyb2xzIHBvbGwgY2FkZW5jZSwgZmlsdGVyaW5nLCB0ZXJtaW5hdGlvbiwgY2FuY2VsbGF0aW9uLCB0aW1lb3V0LCBhbmRcbiAqIHRoZSBtdXRhYmxlIHBlci1ydW4gYXR0cmlidXRlcyBleHBvc2VkIHRocm91Z2gge0BsaW5rIFBvbGxDb250ZXh0fS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQb2xsT3B0aW9uczxULCBBID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4+IHtcbiAgLyoqIE1pbGxpc2Vjb25kcyB0byB3YWl0IGJldHdlZW4gcG9sbHMuICovXG4gIGludGVydmFsTXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIFByZWRpY2F0ZSBldmFsdWF0ZWQgYWdhaW5zdCBlYWNoIHlpZWxkZWQgdmFsdWU6IHJldHVybiBgdHJ1ZWAgdG9cbiAgICoga2VlcCBpdCwgYGZhbHNlYCB0byBza2lwIGl0ICh3aXRob3V0IHN0b3BwaW5nIHRoZSBsb29wKS4gTWF5IGJlXG4gICAqIHN5bmMgb3IgYXN5bmMgLSBhIGBQcm9taXNlTGlrZTxib29sZWFuPmAgaXMgYXdhaXRlZCBiZWZvcmUgdGhlXG4gICAqIGRlY2lzaW9uIGlzIG1hZGUuIFJlY2VpdmVzIHRoZSBzYW1lIHtAbGluayBQb2xsQ29udGV4dH0gYXMgdGhlXG4gICAqIHByb2R1Y2VyIChzYW1lIGBzaWduYWxgLCBzYW1lIGBhdHRyaWJ1dGVzYCBiYWcpLiBUaGUgc3BlY2lhbFxuICAgKiB2YWx1ZSBgXCJkaXN0aW5jdFwiYCBza2lwcyBhIHZhbHVlIHRoYXQgZGVlcC1lcXVhbHMgdGhlIHByZXZpb3VzXG4gICAqIG9uZS5cbiAgICovXG4gIGZpbHRlcj86ICgodmFsdWU6IFQsIGN0eDogUG9sbENvbnRleHQ8VCwgQT4pID0+IGJvb2xlYW4gfCBQcm9taXNlTGlrZTxib29sZWFuPikgfCBcImRpc3RpbmN0XCI7XG4gIC8qKlxuICAgKiBQcmVkaWNhdGUgZXZhbHVhdGVkIGFnYWluc3QgZWFjaCB5aWVsZGVkIHZhbHVlOiByZXR1cm4gYHRydWVgIHRvXG4gICAqIGtlZXAgcG9sbGluZywgYGZhbHNlYCB0byBzdG9wLiBNYXkgYmUgc3luYyBvciBhc3luYy4gT21pdCB0byBwb2xsXG4gICAqIGZvcmV2ZXIgKHRoZSBjb25zdW1lciBzdG9wcyBieSBicmVha2luZyBvdXQgb2YgdGhlIGxvb3Agb3IgYnlcbiAgICogYWJvcnRpbmcgYHNpZ25hbGApLlxuICAgKi9cbiAgcHJlZGljYXRlPzogKHZhbHVlOiBULCBjdHg6IFBvbGxDb250ZXh0PFQsIEE+KSA9PiBib29sZWFuIHwgUHJvbWlzZUxpa2U8Ym9vbGVhbj47XG4gIC8qKlxuICAgKiBFeHRlcm5hbCBjYW5jZWxsYXRpb24gaGFuZGxlLiBUaWVkIGludG8gdGhlIGludGVybmFsIHNpZ25hbCB0aGF0XG4gICAqIGBwb2xsYCBoYW5kcyB0byBgcHJvZHVjZXJgLCBzbyBhYm9ydGluZyBpdCB0ZWFycyBkb3duIGJvdGggdGhlXG4gICAqIGluLWZsaWdodCByZXF1ZXN0IGFuZCB0aGUgaW50ZXItcG9sbCBzbGVlcC5cbiAgICovXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsO1xuICAvKipcbiAgICogSGFyZCB1cHBlciBib3VuZCBvbiB0aGUgdG90YWwgbGlmZXRpbWUgb2YgdGhlIHBvbGwgbG9vcCwgaW5cbiAgICogbWlsbGlzZWNvbmRzLiBXaGVuIHRoZSBidWRnZXQgZWxhcHNlcywgYHBvbGxgIGFib3J0cyBpdHMgaW50ZXJuYWxcbiAgICogc2lnbmFsIHNvIHRoZSBpbi1mbGlnaHQgcHJvZHVjZXIgYW5kIGludGVyLXBvbGwgc2xlZXAgYm90aCB0ZWFyXG4gICAqIGRvd24gcHJvbXB0bHksIGFuZCB0aGUgbG9vcCB0aHJvd3MgdGhlIGBUaW1lb3V0RXJyb3JgXG4gICAqIGBET01FeGNlcHRpb25gIHByb2R1Y2VkIGJ5IGBBYm9ydFNpZ25hbC50aW1lb3V0KHRpbWVvdXRNcylgLiBUaGVcbiAgICogYnVkZ2V0IHN0YXJ0cyB0aWNraW5nIHRoZSBtb21lbnQgdGhlIGdlbmVyYXRvciBpcyBjcmVhdGVkLlxuICAgKi9cbiAgdGltZW91dE1zPzogbnVtYmVyO1xuICAvKipcbiAgICogSW5pdGlhbCB2YWx1ZSBmb3IgYGN0eC5hdHRyaWJ1dGVzYC4gRGVmYXVsdHMgdG8gYHt9YC4gVGhlIHNhbWVcbiAgICogb2JqZWN0IGlzIHJldXNlZCBhY3Jvc3MgaXRlcmF0aW9ucywgc28gY2FsbGVycyBjYW4gcHJlLXBvcHVsYXRlXG4gICAqIGZpZWxkcyAodGltZXJzLCByZXRyeSBjb3VudGVycywgZXRjLikgYW5kIHRoZSBwcm9kdWNlciAvXG4gICAqIHByZWRpY2F0ZSBjYW4gbXV0YXRlIHRoZW0gaW4gcGxhY2UuXG4gICAqL1xuICBhdHRyaWJ1dGVzPzogQTtcbn1cblxuLyoqXG4gKiBBc3luYyBpdGVyYWJsZSB0aGF0IGRyaXZlcyBhIHBlcmlvZGljIHBvbGwuIEVhY2ggaXRlcmF0aW9uOlxuICpcbiAqICAgMS4gQnVpbGRzIGEge0BsaW5rIFBvbGxDb250ZXh0fSAoYGF0dGVtcHRgLCBgcHJldmlvdXNgLCBgc2lnbmFsYCxcbiAqICAgICAgc2hhcmVkIGBhdHRyaWJ1dGVzYCkgYW5kIGNhbGxzIGBwcm9kdWNlcihjdHgpYDsgeWllbGRzIHRoZVxuICogICAgICByZXNvbHZlZCB2YWx1ZSAoc3ViamVjdCB0byBgZmlsdGVyYCkuXG4gKiAgIDIuIEV2YWx1YXRlcyBgb3B0aW9ucy5wcmVkaWNhdGUodmFsdWUsIGN0eClgOyBzdG9wcyB3aGVuIGl0XG4gKiAgICAgIHJldHVybnMgKG9yIHJlc29sdmVzIHRvKSBgZmFsc2VgLlxuICogICAzLiBTbGVlcHMgYG9wdGlvbnMuaW50ZXJ2YWxNc2AgYmVmb3JlIHRoZSBuZXh0IGF0dGVtcHQuXG4gKlxuICogVGhlIGZpcnN0IGNhbGwgcnVucyBpbW1lZGlhdGVseSAobm8gbGVhZGluZyBzbGVlcCkgc28gdGhlIGNvbnN1bWVyXG4gKiBzZWVzIGEgdmFsdWUgd2l0aG91dCB3YWl0aW5nIGFuIGludGVydmFsLiBFcnJvcnMgdGhyb3duIGJ5XG4gKiBgcHJvZHVjZXJgIHByb3BhZ2F0ZSB0aHJvdWdoIHRoZSBnZW5lcmF0b3IuXG4gKlxuICogYHBvbGxgIGFsd2F5cyBjcmVhdGVzIGFuIGludGVybmFsIGBBYm9ydENvbnRyb2xsZXJgIGFuZCBleHBvc2VzXG4gKiBgaW50ZXJuYWwuc2lnbmFsYCBhcyBgY3R4LnNpZ25hbGAsIHNvIHByb2R1Y2VycyBjYW4gcmVseSBvbiBhXG4gKiBkZWZpbmVkIHNpZ25hbCB3aXRob3V0IGEgbnVsbGlzaCBjaGVjay4gVGhlIGV4dGVybmFsXG4gKiBgb3B0aW9ucy5zaWduYWxgIGlzIHRpZWQgaW4sIGFuZCBhIGB0cnkvZmluYWxseWAgYWJvcnRzIHRoZVxuICogaW50ZXJuYWwgc2lnbmFsIHdoZW4gdGhlIGNvbnN1bWVyIGJyZWFrcyBvdXQgb2YgdGhlIGBmb3IgYXdhaXRgXG4gKiAob3IgdGhlIGxvb3AgdGhyb3dzKSwgc28gYW55IHByb2R1Y2VyIHdvcmsgc3RpbGwgaG9sZGluZyB0aGVcbiAqIHNpZ25hbCBzZWVzIHRoZSBjYW5jZWxsYXRpb24gdG9vLlxuICpcbiAqIEBleGFtcGxlXG4gKiBmb3IgYXdhaXQgKGNvbnN0IG1zZyBvZiBwb2xsKFxuICogICBhc3luYyAoeyBzaWduYWwgfSkgPT5cbiAqICAgICBjbGllbnQuZ2VuaWUuZ2V0TWVzc2FnZSh7IC4uLiB9LCB7IGFib3J0U2lnbmFsOiBzaWduYWwgfSksXG4gKiAgIHtcbiAqICAgICBpbnRlcnZhbE1zOiAyNTAsXG4gKiAgICAgcHJlZGljYXRlOiAobSkgPT4gIVRFUk1JTkFMX1NUQVRVU0VTLmhhcyhtLnN0YXR1cyksXG4gKiAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAqICAgfSxcbiAqICkpIHtcbiAqICAgcmVuZGVyKG1zZyk7XG4gKiB9XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiogcG9sbDxULCBBID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4+KFxuICBwcm9kdWNlcjogUG9sbFByb2R1Y2VyPFQsIEE+LFxuICBvcHRpb25zOiBQb2xsT3B0aW9uczxULCBBPixcbik6IEFzeW5jR2VuZXJhdG9yPFQsIHZvaWQsIHZvaWQ+IHtcbiAgY29uc3QgeyBpbnRlcnZhbE1zLCBwcmVkaWNhdGUsIHNpZ25hbCwgYXR0cmlidXRlcywgdGltZW91dE1zIH0gPSBvcHRpb25zO1xuICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICBpZiAoc2lnbmFsKSB0aWVBYm9ydFNpZ25hbChjb250cm9sbGVyLCBzaWduYWwpO1xuICBpZiAodGltZW91dE1zICE9PSB1bmRlZmluZWQpIHtcbiAgICB0aWVBYm9ydFNpZ25hbChjb250cm9sbGVyLCBBYm9ydFNpZ25hbC50aW1lb3V0KHRpbWVvdXRNcykpO1xuICB9XG4gIC8vIFNpbmdsZSBzaGFyZWQgYXR0cmlidXRlcyBvYmplY3Qgc28gd3JpdGVzIGZyb20gb25lIGl0ZXJhdGlvbiBhcmVcbiAgLy8gdmlzaWJsZSBvbiB0aGUgbmV4dC4gYHt9IGFzIEFgIGlzIHNhZmUgYmVjYXVzZSBlaXRoZXIgdGhlIGNhbGxlclxuICAvLyBzdXBwbGllZCBgYXR0cmlidXRlc2AgKHR5cGVkKSBvciBgQWAgZGVmYXVsdGVkIHRvIHRoZSB1bmtub3duXG4gIC8vIHJlY29yZCBzaGFwZSAoaW4gd2hpY2ggY2FzZSBge31gIHNhdGlzZmllcyBpdCkuXG4gIGNvbnN0IHNoYXJlZEF0dHJpYnV0ZXMgPSBhdHRyaWJ1dGVzID8/ICh7fSBhcyBBKTtcbiAgdHJ5IHtcbiAgICBsZXQgcHJldmlvdXM6IFQgfCB1bmRlZmluZWQ7XG4gICAgZm9yIChsZXQgYXR0ZW1wdCA9IDA7IDsgYXR0ZW1wdCsrKSB7XG4gICAgICBjb250cm9sbGVyLnNpZ25hbC50aHJvd0lmQWJvcnRlZCgpO1xuICAgICAgY29uc3QgY3R4OiBQb2xsQ29udGV4dDxULCBBPiA9IHtcbiAgICAgICAgYXR0ZW1wdCxcbiAgICAgICAgcHJldmlvdXMsXG4gICAgICAgIHNpZ25hbDogY29udHJvbGxlci5zaWduYWwsXG4gICAgICAgIGF0dHJpYnV0ZXM6IHNoYXJlZEF0dHJpYnV0ZXMsXG4gICAgICB9O1xuICAgICAgY29uc3QgdmFsdWUgPSBhd2FpdCBwcm9kdWNlcihjdHgpO1xuICAgICAgaWYgKG9wdGlvbnMuZmlsdGVyKSB7XG4gICAgICAgIGlmIChvcHRpb25zLmZpbHRlciA9PT0gXCJkaXN0aW5jdFwiKSB7XG4gICAgICAgICAgaWYgKGRlZXBFcXVhbChwcmV2aW91cywgdmFsdWUpKSB7XG4gICAgICAgICAgICBhd2FpdCBzbGVlcChpbnRlcnZhbE1zLCBjb250cm9sbGVyLnNpZ25hbCk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoIShhd2FpdCBvcHRpb25zLmZpbHRlcih2YWx1ZSwgY3R4KSkpIHtcbiAgICAgICAgICBhd2FpdCBzbGVlcChpbnRlcnZhbE1zLCBjb250cm9sbGVyLnNpZ25hbCk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHlpZWxkIHZhbHVlO1xuICAgICAgaWYgKHByZWRpY2F0ZSAmJiAhKGF3YWl0IHByZWRpY2F0ZSh2YWx1ZSwgY3R4KSkpIHJldHVybjtcbiAgICAgIGF3YWl0IHNsZWVwKGludGVydmFsTXMsIGNvbnRyb2xsZXIuc2lnbmFsKTtcbiAgICAgIHByZXZpb3VzID0gdmFsdWU7XG4gICAgfVxuICB9IGZpbmFsbHkge1xuICAgIGNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgfVxufVxuXG4vKipcbiAqIFRpZSBhIGNoaWxkIGBBYm9ydENvbnRyb2xsZXJgIHRvIGEgcGFyZW50IHNpZ25hbC4gVGhlIGNoaWxkIGFib3J0c1xuICogd2hlbmV2ZXIgdGhlIHBhcmVudCBhYm9ydHM7IGFib3J0aW5nIHRoZSBjaGlsZCBkb2VzIG5vdCBhZmZlY3QgdGhlXG4gKiBwYXJlbnQgKHNvIGEgZmV0Y2gtbGV2ZWwgY2FuY2VsIGRvZXNuJ3QgdGVhciBkb3duIHRoZSBtYWluIHBvbGwgbG9vcCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0aWVBYm9ydFNpZ25hbChjaGlsZDogQWJvcnRDb250cm9sbGVyLCBwYXJlbnQ/OiBBYm9ydFNpZ25hbCk6IHZvaWQge1xuICBpZiAoIXBhcmVudCkge1xuICAgIHJldHVybjtcbiAgfSBlbHNlIGlmIChwYXJlbnQuYWJvcnRlZCkge1xuICAgIGNoaWxkLmFib3J0KHBhcmVudC5yZWFzb24pO1xuICAgIHJldHVybjtcbiAgfVxuICBwYXJlbnQuYWRkRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsICgpID0+IGNoaWxkLmFib3J0KHBhcmVudC5yZWFzb24pLCB7XG4gICAgb25jZTogdHJ1ZSxcbiAgfSk7XG59XG5cbi8qKlxuICogQ29tYmluZSBzZXZlcmFsIG9wdGlvbmFsIGNhbmNlbGxhdGlvbiBzb3VyY2VzIGludG8gb25lIHNpZ25hbCB0aGF0IGFib3J0c1xuICogYXMgc29vbiBhcyBhbnkgb2YgdGhlbSBkb2VzLlxuICpcbiAqIFRoZSB1c3VhbCBjYWxsZXIgaXMgYW4gb3BlcmF0aW9uIHRoYXQgaGFzIHRvIGhvbm9yIG1vcmUgdGhhbiBvbmUgc291cmNlIGF0XG4gKiBvbmNlIC0gYSBjYWxsZXIncyBvd24gc2lnbmFsIChhIGNsb3NlZCBjb25uZWN0aW9uLCBhbiBhZ2VudCBydW4gYmVpbmdcbiAqIGNhbmNlbGxlZCkgcGx1cyBvbmUgZGVyaXZlZCBmcm9tIGEgdGltZW91dCAtIHdoZXJlIHRoZSBhd2FpdGVkIEkvTyBhY2NlcHRzXG4gKiBvbmx5IGEgc2luZ2xlIHNpZ25hbC4gQWJzZW50IHNvdXJjZXMgYXJlIGlnbm9yZWQsIGFuZCBhIGxvbmUgc2lnbmFsIGlzXG4gKiByZXR1cm5lZCBhcy1pcyBzbyB0aGUgY29tbW9uIHBhdGggYWxsb2NhdGVzIG5vdGhpbmcuIFJldHVybnMgYHVuZGVmaW5lZGBcbiAqIG9ubHkgd2hlbiBldmVyeSBpbnB1dCBpcyBhYnNlbnQsIHdoaWNoIGNhbGxlcnMgY2FuIHBhc3Mgc3RyYWlnaHQgdGhyb3VnaFxuICogdG8gYW4gb3B0aW9uYWwgYHNpZ25hbGAgcGFyYW1ldGVyLlxuICpcbiAqIEFib3J0aW5nIGFuIGlucHV0IGFib3J0cyB0aGUgcmVzdWx0IChjYXJyeWluZyB0aGF0IGlucHV0J3MgYHJlYXNvbmApO1xuICogbm90aGluZyBwcm9wYWdhdGVzIGJhY2sgdGhlIG90aGVyIHdheS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYXdhaXQgZmV0Y2godXJsLCB7IHNpZ25hbDogY29tYmluZUFib3J0U2lnbmFscyhyZXEuc2lnbmFsLCB0aW1lb3V0LnNpZ25hbCkgfSk7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb21iaW5lQWJvcnRTaWduYWxzKFxuICAuLi5zaWduYWxzOiAoQWJvcnRTaWduYWwgfCB1bmRlZmluZWQpW11cbik6IEFib3J0U2lnbmFsIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgcHJlc2VudCA9IHNpZ25hbHMuZmlsdGVyKChzaWduYWwpOiBzaWduYWwgaXMgQWJvcnRTaWduYWwgPT4gc2lnbmFsICE9PSB1bmRlZmluZWQpO1xuICBpZiAocHJlc2VudC5sZW5ndGggPD0gMSkgcmV0dXJuIHByZXNlbnRbMF07XG4gIHJldHVybiBBYm9ydFNpZ25hbC5hbnkocHJlc2VudCk7XG59XG5cbi8qKlxuICogUHJvbWlzaWZpZWQgYHNldFRpbWVvdXRgIHRoYXQgd2FrZXMgdXAgZWFybHkgKGFuZCByZWplY3RzIHdpdGhcbiAqIGBzaWduYWwucmVhc29uYCkgd2hlbiBgc2lnbmFsYCBhYm9ydHMgbWlkLXdhaXQuIFNob3J0LWNpcmN1aXRzIHRvIGFcbiAqIHJlamVjdGVkIHByb21pc2Ugd2hlbiB0aGUgc2lnbmFsIGlzIGFscmVhZHkgYWJvcnRlZCBvbiBlbnRyeSwgc28gdGhlXG4gKiBhYm9ydCBwYXRoIGlzIGNvbnNpc3RlbnQgcmVnYXJkbGVzcyBvZiB3aGV0aGVyIHRoZSB3YWl0IGFjdHVhbGx5XG4gKiBzdGFydGVkLlxuICpcbiAqIFVzZSBhcyB0aGUgYnVpbGRpbmcgYmxvY2sgZm9yIGFueSBcIndhaXQsIGJ1dCBjYW5jZWwgY2xlYW5seVwiIHBhdHRlcm4gLVxuICogaW50ZXItcG9sbCBiYWNrb2ZmLCBwYWNpbmcgbG9vcHMsIHJldHJ5IHRpbWVycywgbG9uZy1wb2xsIGJ1ZGdldHMgLSBzb1xuICogY2FuY2VsbGF0aW9uIGFsd2F5cyByZWplY3RzIHdpdGggdGhlIGNhbGxlcidzIGBzaWduYWwucmVhc29uYCByYXRoZXJcbiAqIHRoYW4gc2lsZW50bHkgcmVzb2x2aW5nIGFmdGVyIHRoZSB0aW1lciBleHBpcmVzLlxuICpcbiAqIEBleGFtcGxlXG4gKiBhd2FpdCBzbGVlcCgyNTAsIHJlcS5zaWduYWwpO1xuICovXG5leHBvcnQgZnVuY3Rpb24gc2xlZXAobXM6IG51bWJlciwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKHNpZ25hbD8uYWJvcnRlZCkgcmV0dXJuIFByb21pc2UucmVqZWN0KHNpZ25hbC5yZWFzb24pO1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGNvbnN0IG9uQWJvcnQgPSAoKTogdm9pZCA9PiB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgcmVqZWN0KHNpZ25hbCEucmVhc29uKTtcbiAgICB9O1xuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBzaWduYWw/LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KTtcbiAgICAgIHJlc29sdmUoKTtcbiAgICB9LCBtcyk7XG4gICAgc2lnbmFsPy5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgb25BYm9ydCwgeyBvbmNlOiB0cnVlIH0pO1xuICB9KTtcbn1cbiJdfQ==
|