fetch-retrier 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,12 +2,18 @@
2
2
 
3
3
  A lightweight wrapper around `fetch` that adds **retries**, **timeout**, and **full jitter** backoff. Useful for calling HTTP APIs that may be rate-limited (429) or temporarily unavailable (5xx).
4
4
 
5
+ [![npm version](https://img.shields.io/npm/v/fetch-retrier.svg)](https://www.npmjs.com/package/fetch-retrier)
6
+ [![npm downloads](https://img.shields.io/npm/dm/fetch-retrier.svg)](https://www.npmjs.com/package/fetch-retrier)
7
+ [![build](https://github.com/gammarers-labs/fetch-retrier/actions/workflows/build.yml/badge.svg)](https://github.com/gammarers-labs/fetch-retrier/actions/workflows/build.yml)
8
+ [![release](https://github.com/gammarers-labs/fetch-retrier/actions/workflows/release.yml/badge.svg)](https://github.com/gammarers-labs/fetch-retrier/actions/workflows/release.yml)
9
+
5
10
  ## Features
6
11
 
7
12
  - **Configurable retries** – Set the maximum number of attempts per request.
8
13
  - **Per-request timeout** – Abort requests that exceed a given duration.
9
14
  - **Full jitter backoff** – Exponential backoff with random jitter (AWS-style) between retries.
10
15
  - **Custom retry predicate** – Control which status codes trigger a retry (default: 429, 500, 502, 503, 504).
16
+ - **External cancellation** – Pass an `AbortSignal` to cancel an in-flight request.
11
17
  - **TypeScript** – Exported types for `RequestOptions` and usage in TS/JS.
12
18
 
13
19
  ## Installation
@@ -54,6 +60,7 @@ if (response.ok) {
54
60
  | `timeoutMs` | `number` | Yes | Timeout in milliseconds for each request. Requests are aborted when this is exceeded. |
55
61
  | `baseBackoffMs` | `number` | Yes | Base delay in milliseconds for backoff. Delay is capped at `baseBackoffMs * 2^attempt` and randomized (full jitter). |
56
62
  | `headers` | `Record<string, string>` | No | Headers to send with the request. |
63
+ | `signal` | `AbortSignal` | No | Optional external abort signal. If already aborted, `FetchRetrierAlreadyAbortedError` is thrown. If aborted during an attempt, the request is aborted and retried until `retries` is exhausted. |
57
64
  | `shouldRetry` | `(response: Response, body: string) => boolean` | No | Custom predicate. Return `true` to retry on this response. Default: retry on status 429, 500, 502, 503, 504. |
58
65
 
59
66
  ### Custom retry logic
@@ -72,13 +79,29 @@ const response = await fetchRetrier('https://api.example.com/data', {
72
79
  });
73
80
  ```
74
81
 
82
+ ### Cancellation with `AbortController`
83
+
84
+ ```typescript
85
+ const controller = new AbortController();
86
+
87
+ setTimeout(() => controller.abort(), 250);
88
+
89
+ await fetchRetrier('https://api.example.com/data', {
90
+ retries: 3,
91
+ timeoutMs: 5000,
92
+ baseBackoffMs: 250,
93
+ signal: controller.signal,
94
+ });
95
+ ```
96
+
75
97
  ## Retry and error behavior
76
98
 
77
99
  - **Success** – If `response.ok` is true, the response is returned immediately.
78
- - **Retriable failure** – If the response is not OK and `shouldRetry(response, body)` returns true, the client waits (full jitter backoff) and retries until `retries` is exhausted. On the last attempt, an error is thrown with the HTTP status.
79
- - **Non-retriable failure** – If `shouldRetry` returns false, an error is thrown immediately (e.g. `Non-retriable HTTP error: 404`).
80
- - **Timeout** – If a request exceeds `timeoutMs`, it is aborted and retried (same backoff) until `retries` is exhausted.
81
- - **Network/TypeError** – Network errors and related `TypeError`s are retried with backoff; after the last attempt, the error is rethrown.
100
+ - **Retriable failure** – If the response is not OK and `shouldRetry(response, body)` returns true, the client waits (full jitter backoff) and retries until `retries` is exhausted. On the last attempt, `FetchRetrierHttpError` is thrown (includes `status`).
101
+ - **Non-retriable failure** – If `shouldRetry` returns false, `FetchRetrierHttpError` is thrown immediately (e.g. message `Non-retriable HTTP error: 404`).
102
+ - **Timeout** – If a request exceeds `timeoutMs`, it is aborted and retried (same backoff) until `retries` is exhausted; the final failure is `FetchRetrierAbortError`.
103
+ - **Network/TypeError** – Network errors and related `TypeError`s are retried with backoff; after the last attempt, `FetchRetrierNetworkError` is thrown with the original error as `cause`.
104
+ - **Already aborted signal** – If `signal` is already aborted before an attempt starts, `FetchRetrierAlreadyAbortedError` is thrown immediately (no attempt is made).
82
105
 
83
106
  ## Requirements
84
107
 
@@ -87,4 +110,4 @@ const response = await fetchRetrier('https://api.example.com/data', {
87
110
 
88
111
  ## License
89
112
 
90
- This project is licensed under the Apache-2.0 License.
113
+ This project is licensed under the (Apache-2.0) License.
package/lib/index.d.ts CHANGED
@@ -1,21 +1,88 @@
1
1
  /**
2
- * request Options
2
+ * Configuration for {@link fetchRetrier}.
3
3
  */
4
4
  export interface RequestOptions {
5
+ /** Optional HTTP headers sent with each attempt. */
5
6
  headers?: Record<string, string>;
7
+ /** Maximum number of attempts, including the first. */
6
8
  retries: number;
9
+ /** Per-attempt timeout in milliseconds; uses an internal {@link AbortController} when exceeded. */
7
10
  timeoutMs: number;
11
+ /**
12
+ * Base backoff in milliseconds for full jitter. The cap for attempt `n` is `baseBackoffMs * 2^n`.
13
+ */
8
14
  baseBackoffMs: number;
9
15
  /**
10
- * Custom predicate: return true to retry on this response.
11
- * Default: retry on 429, 500, 502, 503, 504
16
+ * Optional external {@link AbortSignal}. When aborted, the in-flight request is aborted; on the
17
+ * final attempt, cancellation surfaces as {@link FetchRetrierAbortError}.
18
+ */
19
+ signal?: AbortSignal;
20
+ /**
21
+ * Return `true` to schedule another attempt for this non-OK response.
22
+ * Default: retry on status 429, 500, 502, 503, or 504.
12
23
  */
13
24
  shouldRetry?: (response: Response, body: string) => boolean;
14
25
  }
26
+ /** Error thrown when a request is cancelled by timeout or an external {@link AbortSignal}. */
27
+ export declare class FetchRetrierAbortError extends Error {
28
+ readonly name: string;
29
+ /**
30
+ * @param message - Human-readable reason (default: `'Aborted'`)
31
+ */
32
+ constructor(message?: string);
33
+ }
34
+ /**
35
+ * Error thrown when {@link RequestOptions.signal} is already aborted before an attempt starts.
36
+ */
37
+ export declare class FetchRetrierAlreadyAbortedError extends FetchRetrierAbortError {
38
+ readonly name: string;
39
+ /**
40
+ * @param message - Human-readable reason (default: `'Signal was already aborted'`)
41
+ */
42
+ constructor(message?: string);
43
+ }
44
+ /** Error thrown when the server returns a non-OK HTTP status and no further retry is performed. */
45
+ export declare class FetchRetrierHttpError extends Error {
46
+ readonly status: number;
47
+ readonly name: string;
48
+ /**
49
+ * @param message - Error description
50
+ * @param status - HTTP status code from the response
51
+ */
52
+ constructor(message: string, status: number);
53
+ }
54
+ /**
55
+ * Error thrown when a fetch fails with a network-level error (e.g. DNS failure, connection refused).
56
+ */
57
+ export declare class FetchRetrierNetworkError extends Error {
58
+ readonly cause?: unknown | undefined;
59
+ readonly name: string;
60
+ /**
61
+ * @param message - Human-readable reason (default: `'Network error'`)
62
+ * @param cause - Original error, if any
63
+ */
64
+ constructor(message?: string, cause?: unknown | undefined);
65
+ }
66
+ /** Error thrown when an internal invariant fails (should not happen in normal use). */
67
+ export declare class FetchRetrierUnreachableError extends Error {
68
+ readonly name: string;
69
+ /**
70
+ * @param message - Human-readable reason (default: `'Unreachable'`)
71
+ */
72
+ constructor(message?: string);
73
+ }
15
74
  /**
16
- * retry + timeout + Full Jitter
17
- * @param url - The URL to fetch
18
- * @param options - The options for the fetch
19
- * @returns The response
75
+ * Performs `fetch` with retries, a per-attempt timeout, exponential backoff with full jitter,
76
+ * optional {@link RequestOptions.signal} cancellation, and a configurable retry predicate for
77
+ * non-OK responses.
78
+ *
79
+ * @param url - Request URL
80
+ * @param options - Retries, backoff, timeout, optional abort signal, and optional retry predicate
81
+ * @returns The first successful (OK) {@link Response}
82
+ * @throws {FetchRetrierAlreadyAbortedError} If `options.signal` is already aborted before an attempt
83
+ * @throws {FetchRetrierHttpError} On a non-OK response that is not retried or after the last attempt
84
+ * @throws {FetchRetrierNetworkError} On a network error on the final attempt
85
+ * @throws {FetchRetrierAbortError} On timeout or external abort on the final attempt
86
+ * @throws {FetchRetrierUnreachableError} If the retry loop exits without returning (internal bug)
20
87
  */
21
88
  export declare const fetchRetrier: (url: string, options: RequestOptions) => Promise<Response>;
package/lib/index.js CHANGED
@@ -1,26 +1,116 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchRetrier = void 0;
3
+ exports.fetchRetrier = exports.FetchRetrierUnreachableError = exports.FetchRetrierNetworkError = exports.FetchRetrierHttpError = exports.FetchRetrierAlreadyAbortedError = exports.FetchRetrierAbortError = void 0;
4
+ /** Error thrown when a request is cancelled by timeout or an external {@link AbortSignal}. */
5
+ class FetchRetrierAbortError extends Error {
6
+ /**
7
+ * @param message - Human-readable reason (default: `'Aborted'`)
8
+ */
9
+ constructor(message = 'Aborted') {
10
+ super(message);
11
+ this.name = 'FetchRetrierAbortError';
12
+ Object.setPrototypeOf(this, FetchRetrierAbortError.prototype);
13
+ }
14
+ }
15
+ exports.FetchRetrierAbortError = FetchRetrierAbortError;
16
+ /**
17
+ * Error thrown when {@link RequestOptions.signal} is already aborted before an attempt starts.
18
+ */
19
+ class FetchRetrierAlreadyAbortedError extends FetchRetrierAbortError {
20
+ /**
21
+ * @param message - Human-readable reason (default: `'Signal was already aborted'`)
22
+ */
23
+ constructor(message = 'Signal was already aborted') {
24
+ super(message);
25
+ this.name = 'FetchRetrierAlreadyAbortedError';
26
+ Object.setPrototypeOf(this, FetchRetrierAlreadyAbortedError.prototype);
27
+ }
28
+ }
29
+ exports.FetchRetrierAlreadyAbortedError = FetchRetrierAlreadyAbortedError;
30
+ /** Error thrown when the server returns a non-OK HTTP status and no further retry is performed. */
31
+ class FetchRetrierHttpError extends Error {
32
+ /**
33
+ * @param message - Error description
34
+ * @param status - HTTP status code from the response
35
+ */
36
+ constructor(message, status) {
37
+ super(message);
38
+ this.status = status;
39
+ this.name = 'FetchRetrierHttpError';
40
+ Object.setPrototypeOf(this, FetchRetrierHttpError.prototype);
41
+ }
42
+ }
43
+ exports.FetchRetrierHttpError = FetchRetrierHttpError;
44
+ /**
45
+ * Error thrown when a fetch fails with a network-level error (e.g. DNS failure, connection refused).
46
+ */
47
+ class FetchRetrierNetworkError extends Error {
48
+ /**
49
+ * @param message - Human-readable reason (default: `'Network error'`)
50
+ * @param cause - Original error, if any
51
+ */
52
+ constructor(message = 'Network error', cause) {
53
+ super(message);
54
+ this.cause = cause;
55
+ this.name = 'FetchRetrierNetworkError';
56
+ Object.setPrototypeOf(this, FetchRetrierNetworkError.prototype);
57
+ }
58
+ }
59
+ exports.FetchRetrierNetworkError = FetchRetrierNetworkError;
60
+ /** Error thrown when an internal invariant fails (should not happen in normal use). */
61
+ class FetchRetrierUnreachableError extends Error {
62
+ /**
63
+ * @param message - Human-readable reason (default: `'Unreachable'`)
64
+ */
65
+ constructor(message = 'Unreachable') {
66
+ super(message);
67
+ this.name = 'FetchRetrierUnreachableError';
68
+ Object.setPrototypeOf(this, FetchRetrierUnreachableError.prototype);
69
+ }
70
+ }
71
+ exports.FetchRetrierUnreachableError = FetchRetrierUnreachableError;
72
+ /**
73
+ * Default {@link RequestOptions.shouldRetry} implementation: retry on HTTP 429, 500, 502, 503, 504.
74
+ */
4
75
  const defaultShouldRetry = (res) => {
5
76
  return [429, 500, 502, 503, 504].includes(res.status);
6
77
  };
7
78
  /**
8
- * retry + timeout + Full Jitter
9
- * @param url - The URL to fetch
10
- * @param options - The options for the fetch
11
- * @returns The response
79
+ * Performs `fetch` with retries, a per-attempt timeout, exponential backoff with full jitter,
80
+ * optional {@link RequestOptions.signal} cancellation, and a configurable retry predicate for
81
+ * non-OK responses.
82
+ *
83
+ * @param url - Request URL
84
+ * @param options - Retries, backoff, timeout, optional abort signal, and optional retry predicate
85
+ * @returns The first successful (OK) {@link Response}
86
+ * @throws {FetchRetrierAlreadyAbortedError} If `options.signal` is already aborted before an attempt
87
+ * @throws {FetchRetrierHttpError} On a non-OK response that is not retried or after the last attempt
88
+ * @throws {FetchRetrierNetworkError} On a network error on the final attempt
89
+ * @throws {FetchRetrierAbortError} On timeout or external abort on the final attempt
90
+ * @throws {FetchRetrierUnreachableError} If the retry loop exits without returning (internal bug)
12
91
  */
13
92
  const fetchRetrier = async (url, options) => {
14
- const { headers, retries, timeoutMs, baseBackoffMs, shouldRetry = defaultShouldRetry } = options;
93
+ const { headers, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = defaultShouldRetry } = options;
15
94
  for (let attempt = 1; attempt <= retries; attempt++) {
95
+ if (externalSignal?.aborted) {
96
+ throw new FetchRetrierAlreadyAbortedError();
97
+ }
16
98
  const controller = new AbortController();
17
99
  const timer = setTimeout(() => controller.abort(), timeoutMs);
100
+ const onExternalAbort = () => {
101
+ clearTimeout(timer);
102
+ controller.abort();
103
+ };
104
+ if (externalSignal) {
105
+ externalSignal.addEventListener('abort', onExternalAbort);
106
+ }
18
107
  try {
19
108
  const res = await fetch(url, {
20
109
  headers,
21
110
  signal: controller.signal,
22
111
  });
23
112
  clearTimeout(timer);
113
+ externalSignal?.removeEventListener('abort', onExternalAbort);
24
114
  if (res.ok) {
25
115
  return res;
26
116
  }
@@ -28,45 +118,51 @@ const fetchRetrier = async (url, options) => {
28
118
  const isContinue = shouldRetry(res, text);
29
119
  if (isContinue) {
30
120
  if (attempt === retries) {
31
- throw new Error(`HTTP ${res.status}`);
121
+ throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status);
32
122
  }
33
123
  await wait(fullJitter(baseBackoffMs, attempt));
34
124
  }
35
125
  else {
36
- throw new Error(`Non-retriable HTTP error: ${res.status}`);
126
+ throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status);
37
127
  }
38
128
  }
39
129
  catch (err) {
40
130
  clearTimeout(timer);
131
+ externalSignal?.removeEventListener('abort', onExternalAbort);
41
132
  if (err instanceof Error && err.name === 'AbortError') {
42
133
  if (attempt === retries)
43
- throw err;
134
+ throw err instanceof FetchRetrierAbortError ? err : new FetchRetrierAbortError();
44
135
  await wait(fullJitter(baseBackoffMs, attempt));
45
136
  continue;
46
137
  }
47
138
  if (err instanceof TypeError) {
48
139
  if (attempt === retries)
49
- throw err;
140
+ throw new FetchRetrierNetworkError('Network error', err);
50
141
  await wait(fullJitter(baseBackoffMs, attempt));
51
142
  continue;
52
143
  }
53
144
  throw err;
54
145
  }
55
146
  }
56
- throw new Error('Unreachable');
147
+ throw new FetchRetrierUnreachableError();
57
148
  };
58
149
  exports.fetchRetrier = fetchRetrier;
150
+ /**
151
+ * @param ms - Delay in milliseconds
152
+ * @returns A promise that resolves after `ms`
153
+ */
59
154
  const wait = (ms) => {
60
155
  return new Promise((resolve) => setTimeout(resolve, ms));
61
156
  };
62
157
  /**
63
- * AWS recommended Full Jitter
64
- * @param base - The base time in milliseconds
65
- * @param attempt - The attempt number
66
- * @returns The time to wait in milliseconds
158
+ * Full jitter backoff: random delay in `[0, base * 2^attempt)` ms (AWS-recommended pattern).
159
+ *
160
+ * @param base - Base backoff in milliseconds
161
+ * @param attempt - 1-based attempt index (first retry uses `attempt === 1`)
162
+ * @returns Wait duration in milliseconds before the next attempt
67
163
  */
68
164
  const fullJitter = (base, attempt) => {
69
165
  const cap = base * Math.pow(2, attempt);
70
166
  return Math.floor(Math.random() * cap);
71
167
  };
72
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBZUEsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLEdBQWEsRUFBVyxFQUFFO0lBQ3BELE9BQU8sQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUN4RCxDQUFDLENBQUM7QUFFRjs7Ozs7R0FLRztBQUNJLE1BQU0sWUFBWSxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQUUsT0FBdUIsRUFBcUIsRUFBRTtJQUM1RixNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLFdBQVcsR0FBRyxrQkFBa0IsRUFBRSxHQUFHLE9BQU8sQ0FBQztJQUVqRyxLQUFLLElBQUksT0FBTyxHQUFHLENBQUMsRUFBRSxPQUFPLElBQUksT0FBTyxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUM7UUFDcEQsTUFBTSxVQUFVLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQztRQUN6QyxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRTlELElBQUksQ0FBQztZQUNILE1BQU0sR0FBRyxHQUFHLE1BQU0sS0FBSyxDQUFDLEdBQUcsRUFBRTtnQkFDM0IsT0FBTztnQkFDUCxNQUFNLEVBQUUsVUFBVSxDQUFDLE1BQU07YUFDMUIsQ0FBQyxDQUFDO1lBRUgsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBRXBCLElBQUksR0FBRyxDQUFDLEVBQUUsRUFBRSxDQUFDO2dCQUNYLE9BQU8sR0FBRyxDQUFDO1lBQ2IsQ0FBQztZQUVELE1BQU0sSUFBSSxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzlCLE1BQU0sVUFBVSxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFFMUMsSUFBSSxVQUFVLEVBQUUsQ0FBQztnQkFDZixJQUFJLE9BQU8sS0FBSyxPQUFPLEVBQUUsQ0FBQztvQkFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxRQUFRLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO2dCQUN4QyxDQUFDO2dCQUNELE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztZQUNqRCxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsR0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDN0QsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEdBQVksRUFBRSxDQUFDO1lBQ3RCLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUVwQixJQUFJLEdBQUcsWUFBWSxLQUFLLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxZQUFZLEVBQUUsQ0FBQztnQkFDdEQsSUFBSSxPQUFPLEtBQUssT0FBTztvQkFBRSxNQUFNLEdBQUcsQ0FBQztnQkFDbkMsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDO2dCQUMvQyxTQUFTO1lBQ1gsQ0FBQztZQUVELElBQUksR0FBRyxZQUFZLFNBQVMsRUFBRSxDQUFDO2dCQUM3QixJQUFJLE9BQU8sS0FBSyxPQUFPO29CQUFFLE1BQU0sR0FBRyxDQUFDO2dCQUNuQyxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBQy9DLFNBQVM7WUFDWCxDQUFDO1lBRUQsTUFBTSxHQUFHLENBQUM7UUFDWixDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sSUFBSSxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUM7QUFDakMsQ0FBQyxDQUFDO0FBbERXLFFBQUEsWUFBWSxnQkFrRHZCO0FBRUYsTUFBTSxJQUFJLEdBQUcsQ0FBQyxFQUFVLEVBQWlCLEVBQUU7SUFDekMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQzNELENBQUMsQ0FBQztBQUVGOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLEdBQUcsQ0FBQyxJQUFZLEVBQUUsT0FBZSxFQUFVLEVBQUU7SUFDM0QsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3hDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLENBQUM7QUFDekMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiByZXF1ZXN0IE9wdGlvbnNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXF1ZXN0T3B0aW9ucyB7XG4gIGhlYWRlcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICByZXRyaWVzOiBudW1iZXI7XG4gIHRpbWVvdXRNczogbnVtYmVyO1xuICBiYXNlQmFja29mZk1zOiBudW1iZXI7XG4gIC8qKlxuICAgKiBDdXN0b20gcHJlZGljYXRlOiByZXR1cm4gdHJ1ZSB0byByZXRyeSBvbiB0aGlzIHJlc3BvbnNlLlxuICAgKiBEZWZhdWx0OiByZXRyeSBvbiA0MjksIDUwMCwgNTAyLCA1MDMsIDUwNFxuICAgKi9cbiAgc2hvdWxkUmV0cnk/OiAocmVzcG9uc2U6IFJlc3BvbnNlLCBib2R5OiBzdHJpbmcpID0+IGJvb2xlYW47XG59XG5cbmNvbnN0IGRlZmF1bHRTaG91bGRSZXRyeSA9IChyZXM6IFJlc3BvbnNlKTogYm9vbGVhbiA9PiB7XG4gIHJldHVybiBbNDI5LCA1MDAsIDUwMiwgNTAzLCA1MDRdLmluY2x1ZGVzKHJlcy5zdGF0dXMpO1xufTtcblxuLyoqXG4gKiByZXRyeSArIHRpbWVvdXQgKyBGdWxsIEppdHRlclxuICogQHBhcmFtIHVybCAtIFRoZSBVUkwgdG8gZmV0Y2hcbiAqIEBwYXJhbSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgZm9yIHRoZSBmZXRjaFxuICogQHJldHVybnMgVGhlIHJlc3BvbnNlXG4gKi9cbmV4cG9ydCBjb25zdCBmZXRjaFJldHJpZXIgPSBhc3luYyAodXJsOiBzdHJpbmcsIG9wdGlvbnM6IFJlcXVlc3RPcHRpb25zKTogUHJvbWlzZTxSZXNwb25zZT4gPT4ge1xuICBjb25zdCB7IGhlYWRlcnMsIHJldHJpZXMsIHRpbWVvdXRNcywgYmFzZUJhY2tvZmZNcywgc2hvdWxkUmV0cnkgPSBkZWZhdWx0U2hvdWxkUmV0cnkgfSA9IG9wdGlvbnM7XG5cbiAgZm9yIChsZXQgYXR0ZW1wdCA9IDE7IGF0dGVtcHQgPD0gcmV0cmllczsgYXR0ZW1wdCsrKSB7XG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCB0aW1lb3V0TXMpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKHVybCwge1xuICAgICAgICBoZWFkZXJzLFxuICAgICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgICAgfSk7XG5cbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG5cbiAgICAgIGlmIChyZXMub2spIHtcbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICAgIH1cblxuICAgICAgY29uc3QgdGV4dCA9IGF3YWl0IHJlcy50ZXh0KCk7XG4gICAgICBjb25zdCBpc0NvbnRpbnVlID0gc2hvdWxkUmV0cnkocmVzLCB0ZXh0KTtcblxuICAgICAgaWYgKGlzQ29udGludWUpIHtcbiAgICAgICAgaWYgKGF0dGVtcHQgPT09IHJldHJpZXMpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEhUVFAgJHtyZXMuc3RhdHVzfWApO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHdhaXQoZnVsbEppdHRlcihiYXNlQmFja29mZk1zLCBhdHRlbXB0KSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYE5vbi1yZXRyaWFibGUgSFRUUCBlcnJvcjogJHtyZXMuc3RhdHVzfWApO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycjogdW5rbm93bikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcblxuICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIEVycm9yICYmIGVyci5uYW1lID09PSAnQWJvcnRFcnJvcicpIHtcbiAgICAgICAgaWYgKGF0dGVtcHQgPT09IHJldHJpZXMpIHRocm93IGVycjtcbiAgICAgICAgYXdhaXQgd2FpdChmdWxsSml0dGVyKGJhc2VCYWNrb2ZmTXMsIGF0dGVtcHQpKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBUeXBlRXJyb3IpIHtcbiAgICAgICAgaWYgKGF0dGVtcHQgPT09IHJldHJpZXMpIHRocm93IGVycjtcbiAgICAgICAgYXdhaXQgd2FpdChmdWxsSml0dGVyKGJhc2VCYWNrb2ZmTXMsIGF0dGVtcHQpKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoJ1VucmVhY2hhYmxlJyk7XG59O1xuXG5jb25zdCB3YWl0ID0gKG1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIG1zKSk7XG59O1xuXG4vKipcbiAqIEFXUyByZWNvbW1lbmRlZCBGdWxsIEppdHRlclxuICogQHBhcmFtIGJhc2UgLSBUaGUgYmFzZSB0aW1lIGluIG1pbGxpc2Vjb25kc1xuICogQHBhcmFtIGF0dGVtcHQgLSBUaGUgYXR0ZW1wdCBudW1iZXJcbiAqIEByZXR1cm5zIFRoZSB0aW1lIHRvIHdhaXQgaW4gbWlsbGlzZWNvbmRzXG4gKi9cbmNvbnN0IGZ1bGxKaXR0ZXIgPSAoYmFzZTogbnVtYmVyLCBhdHRlbXB0OiBudW1iZXIpOiBudW1iZXIgPT4ge1xuICBjb25zdCBjYXAgPSBiYXNlICogTWF0aC5wb3coMiwgYXR0ZW1wdCk7XG4gIHJldHVybiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBjYXApO1xufTtcbiJdfQ==
168
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBMEJBLDhGQUE4RjtBQUM5RixNQUFhLHNCQUF1QixTQUFRLEtBQUs7SUFFL0M7O09BRUc7SUFDSCxZQUFZLE9BQU8sR0FBRyxTQUFTO1FBQzdCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUxDLFNBQUksR0FBVyx3QkFBd0IsQ0FBQztRQU14RCxNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxzQkFBc0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNoRSxDQUFDO0NBQ0Y7QUFURCx3REFTQztBQUVEOztHQUVHO0FBQ0gsTUFBYSwrQkFBZ0MsU0FBUSxzQkFBc0I7SUFFekU7O09BRUc7SUFDSCxZQUFZLE9BQU8sR0FBRyw0QkFBNEI7UUFDaEQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBTEMsU0FBSSxHQUFXLGlDQUFpQyxDQUFDO1FBTWpFLE1BQU0sQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLCtCQUErQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7Q0FDRjtBQVRELDBFQVNDO0FBRUQsbUdBQW1HO0FBQ25HLE1BQWEscUJBQXNCLFNBQVEsS0FBSztJQUU5Qzs7O09BR0c7SUFDSCxZQUNFLE9BQWUsRUFDQyxNQUFjO1FBRTlCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUZDLFdBQU0sR0FBTixNQUFNLENBQVE7UUFQZCxTQUFJLEdBQVcsdUJBQXVCLENBQUM7UUFVdkQsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJLEVBQUUscUJBQXFCLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDL0QsQ0FBQztDQUNGO0FBYkQsc0RBYUM7QUFFRDs7R0FFRztBQUNILE1BQWEsd0JBQXlCLFNBQVEsS0FBSztJQUVqRDs7O09BR0c7SUFDSCxZQUFZLE9BQU8sR0FBRyxlQUFlLEVBQWtCLEtBQWU7UUFDcEUs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Q3pDLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztBQUMzRCxDQUFDLENBQUM7QUFFRjs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVksRUFBRSxPQUFlLEVBQVUsRUFBRTtJQUMzRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDeEMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUMsQ0FBQztBQUN6QyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbmZpZ3VyYXRpb24gZm9yIHtAbGluayBmZXRjaFJldHJpZXJ9LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlcXVlc3RPcHRpb25zIHtcbiAgLyoqIE9wdGlvbmFsIEhUVFAgaGVhZGVycyBzZW50IHdpdGggZWFjaCBhdHRlbXB0LiAqL1xuICBoZWFkZXJzPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgLyoqIE1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzLCBpbmNsdWRpbmcgdGhlIGZpcnN0LiAqL1xuICByZXRyaWVzOiBudW1iZXI7XG4gIC8qKiBQZXItYXR0ZW1wdCB0aW1lb3V0IGluIG1pbGxpc2Vjb25kczsgdXNlcyBhbiBpbnRlcm5hbCB7QGxpbmsgQWJvcnRDb250cm9sbGVyfSB3aGVuIGV4Y2VlZGVkLiAqL1xuICB0aW1lb3V0TXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIEJhc2UgYmFja29mZiBpbiBtaWxsaXNlY29uZHMgZm9yIGZ1bGwgaml0dGVyLiBUaGUgY2FwIGZvciBhdHRlbXB0IGBuYCBpcyBgYmFzZUJhY2tvZmZNcyAqIDJebmAuXG4gICAqL1xuICBiYXNlQmFja29mZk1zOiBudW1iZXI7XG4gIC8qKlxuICAgKiBPcHRpb25hbCBleHRlcm5hbCB7QGxpbmsgQWJvcnRTaWduYWx9LiBXaGVuIGFib3J0ZWQsIHRoZSBpbi1mbGlnaHQgcmVxdWVzdCBpcyBhYm9ydGVkOyBvbiB0aGVcbiAgICogZmluYWwgYXR0ZW1wdCwgY2FuY2VsbGF0aW9uIHN1cmZhY2VzIGFzIHtAbGluayBGZXRjaFJldHJpZXJBYm9ydEVycm9yfS5cbiAgICovXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsO1xuICAvKipcbiAgICogUmV0dXJuIGB0cnVlYCB0byBzY2hlZHVsZSBhbm90aGVyIGF0dGVtcHQgZm9yIHRoaXMgbm9uLU9LIHJlc3BvbnNlLlxuICAgKiBEZWZhdWx0OiByZXRyeSBvbiBzdGF0dXMgNDI5LCA1MDAsIDUwMiwgNTAzLCBvciA1MDQuXG4gICAqL1xuICBzaG91bGRSZXRyeT86IChyZXNwb25zZTogUmVzcG9uc2UsIGJvZHk6IHN0cmluZykgPT4gYm9vbGVhbjtcbn1cblxuLyoqIEVycm9yIHRocm93biB3aGVuIGEgcmVxdWVzdCBpcyBjYW5jZWxsZWQgYnkgdGltZW91dCBvciBhbiBleHRlcm5hbCB7QGxpbmsgQWJvcnRTaWduYWx9LiAqL1xuZXhwb3J0IGNsYXNzIEZldGNoUmV0cmllckFib3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIG92ZXJyaWRlIHJlYWRvbmx5IG5hbWU6IHN0cmluZyA9ICdGZXRjaFJldHJpZXJBYm9ydEVycm9yJztcbiAgLyoqXG4gICAqIEBwYXJhbSBtZXNzYWdlIC0gSHVtYW4tcmVhZGFibGUgcmVhc29uIChkZWZhdWx0OiBgJ0Fib3J0ZWQnYClcbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UgPSAnQWJvcnRlZCcpIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YodGhpcywgRmV0Y2hSZXRyaWVyQWJvcnRFcnJvci5wcm90b3R5cGUpO1xuICB9XG59XG5cbi8qKlxuICogRXJyb3IgdGhyb3duIHdoZW4ge0BsaW5rIFJlcXVlc3RPcHRpb25zLnNpZ25hbH0gaXMgYWxyZWFkeSBhYm9ydGVkIGJlZm9yZSBhbiBhdHRlbXB0IHN0YXJ0cy5cbiAqL1xuZXhwb3J0IGNsYXNzIEZldGNoUmV0cmllckFscmVhZHlBYm9ydGVkRXJyb3IgZXh0ZW5kcyBGZXRjaFJldHJpZXJBYm9ydEVycm9yIHtcbiAgb3ZlcnJpZGUgcmVhZG9ubHkgbmFtZTogc3RyaW5nID0gJ0ZldGNoUmV0cmllckFscmVhZHlBYm9ydGVkRXJyb3InO1xuICAvKipcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBIdW1hbi1yZWFkYWJsZSByZWFzb24gKGRlZmF1bHQ6IGAnU2lnbmFsIHdhcyBhbHJlYWR5IGFib3J0ZWQnYClcbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UgPSAnU2lnbmFsIHdhcyBhbHJlYWR5IGFib3J0ZWQnKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllckFscmVhZHlBYm9ydGVkRXJyb3IucHJvdG90eXBlKTtcbiAgfVxufVxuXG4vKiogRXJyb3IgdGhyb3duIHdoZW4gdGhlIHNlcnZlciByZXR1cm5zIGEgbm9uLU9LIEhUVFAgc3RhdHVzIGFuZCBubyBmdXJ0aGVyIHJldHJ5IGlzIHBlcmZvcm1lZC4gKi9cbmV4cG9ydCBjbGFzcyBGZXRjaFJldHJpZXJIdHRwRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIG92ZXJyaWRlIHJlYWRvbmx5IG5hbWU6IHN0cmluZyA9ICdGZXRjaFJldHJpZXJIdHRwRXJyb3InO1xuICAvKipcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBFcnJvciBkZXNjcmlwdGlvblxuICAgKiBAcGFyYW0gc3RhdHVzIC0gSFRUUCBzdGF0dXMgY29kZSBmcm9tIHRoZSByZXNwb25zZVxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgbWVzc2FnZTogc3RyaW5nLFxuICAgIHB1YmxpYyByZWFkb25seSBzdGF0dXM6IG51bWJlcixcbiAgKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllckh0dHBFcnJvci5wcm90b3R5cGUpO1xuICB9XG59XG5cbi8qKlxuICogRXJyb3IgdGhyb3duIHdoZW4gYSBmZXRjaCBmYWlscyB3aXRoIGEgbmV0d29yay1sZXZlbCBlcnJvciAoZS5nLiBETlMgZmFpbHVyZSwgY29ubmVjdGlvbiByZWZ1c2VkKS5cbiAqL1xuZXhwb3J0IGNsYXNzIEZldGNoUmV0cmllck5ldHdvcmtFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgb3ZlcnJpZGUgcmVhZG9ubHkgbmFtZTogc3RyaW5nID0gJ0ZldGNoUmV0cmllck5ldHdvcmtFcnJvcic7XG4gIC8qKlxuICAgKiBAcGFyYW0gbWVzc2FnZSAtIEh1bWFuLXJlYWRhYmxlIHJlYXNvbiAoZGVmYXVsdDogYCdOZXR3b3JrIGVycm9yJ2ApXG4gICAqIEBwYXJhbSBjYXVzZSAtIE9yaWdpbmFsIGVycm9yLCBpZiBhbnlcbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UgPSAnTmV0d29yayBlcnJvcicsIHB1YmxpYyByZWFkb25seSBjYXVzZT86IHVua25vd24pIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YodGhpcywgRmV0Y2hSZXRyaWVyTmV0d29ya0Vycm9yLnByb3RvdHlwZSk7XG4gIH1cbn1cblxuLyoqIEVycm9yIHRocm93biB3aGVuIGFuIGludGVybmFsIGludmFyaWFudCBmYWlscyAoc2hvdWxkIG5vdCBoYXBwZW4gaW4gbm9ybWFsIHVzZSkuICovXG5leHBvcnQgY2xhc3MgRmV0Y2hSZXRyaWVyVW5yZWFjaGFibGVFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgb3ZlcnJpZGUgcmVhZG9ubHkgbmFtZTogc3RyaW5nID0gJ0ZldGNoUmV0cmllclVucmVhY2hhYmxlRXJyb3InO1xuICAvKipcbiAgICogQHBhcmFtIG1lc3NhZ2UgLSBIdW1hbi1yZWFkYWJsZSByZWFzb24gKGRlZmF1bHQ6IGAnVW5yZWFjaGFibGUnYClcbiAgICovXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UgPSAnVW5yZWFjaGFibGUnKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIEZldGNoUmV0cmllclVucmVhY2hhYmxlRXJyb3IucHJvdG90eXBlKTtcbiAgfVxufVxuXG4vKipcbiAqIERlZmF1bHQge0BsaW5rIFJlcXVlc3RPcHRpb25zLnNob3VsZFJldHJ5fSBpbXBsZW1lbnRhdGlvbjogcmV0cnkgb24gSFRUUCA0MjksIDUwMCwgNTAyLCA1MDMsIDUwNC5cbiAqL1xuY29uc3QgZGVmYXVsdFNob3VsZFJldHJ5ID0gKHJlczogUmVzcG9uc2UpOiBib29sZWFuID0+IHtcbiAgcmV0dXJuIFs0MjksIDUwMCwgNTAyLCA1MDMsIDUwNF0uaW5jbHVkZXMocmVzLnN0YXR1cyk7XG59O1xuXG4vKipcbiAqIFBlcmZvcm1zIGBmZXRjaGAgd2l0aCByZXRyaWVzLCBhIHBlci1hdHRlbXB0IHRpbWVvdXQsIGV4cG9uZW50aWFsIGJhY2tvZmYgd2l0aCBmdWxsIGppdHRlcixcbiAqIG9wdGlvbmFsIHtAbGluayBSZXF1ZXN0T3B0aW9ucy5zaWduYWx9IGNhbmNlbGxhdGlvbiwgYW5kIGEgY29uZmlndXJhYmxlIHJldHJ5IHByZWRpY2F0ZSBmb3JcbiAqIG5vbi1PSyByZXNwb25zZXMuXG4gKlxuICogQHBhcmFtIHVybCAtIFJlcXVlc3QgVVJMXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFJldHJpZXMsIGJhY2tvZmYsIHRpbWVvdXQsIG9wdGlvbmFsIGFib3J0IHNpZ25hbCwgYW5kIG9wdGlvbmFsIHJldHJ5IHByZWRpY2F0ZVxuICogQHJldHVybnMgVGhlIGZpcnN0IHN1Y2Nlc3NmdWwgKE9LKSB7QGxpbmsgUmVzcG9uc2V9XG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJBbHJlYWR5QWJvcnRlZEVycm9yfSBJZiBgb3B0aW9ucy5zaWduYWxgIGlzIGFscmVhZHkgYWJvcnRlZCBiZWZvcmUgYW4gYXR0ZW1wdFxuICogQHRocm93cyB7RmV0Y2hSZXRyaWVySHR0cEVycm9yfSBPbiBhIG5vbi1PSyByZXNwb25zZSB0aGF0IGlzIG5vdCByZXRyaWVkIG9yIGFmdGVyIHRoZSBsYXN0IGF0dGVtcHRcbiAqIEB0aHJvd3Mge0ZldGNoUmV0cmllck5ldHdvcmtFcnJvcn0gT24gYSBuZXR3b3JrIGVycm9yIG9uIHRoZSBmaW5hbCBhdHRlbXB0XG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJBYm9ydEVycm9yfSBPbiB0aW1lb3V0IG9yIGV4dGVybmFsIGFib3J0IG9uIHRoZSBmaW5hbCBhdHRlbXB0XG4gKiBAdGhyb3dzIHtGZXRjaFJldHJpZXJVbnJlYWNoYWJsZUVycm9yfSBJZiB0aGUgcmV0cnkgbG9vcCBleGl0cyB3aXRob3V0IHJldHVybmluZyAoaW50ZXJuYWwgYnVnKVxuICovXG5leHBvcnQgY29uc3QgZmV0Y2hSZXRyaWVyID0gYXN5bmMgKHVybDogc3RyaW5nLCBvcHRpb25zOiBSZXF1ZXN0T3B0aW9ucyk6IFByb21pc2U8UmVzcG9uc2U+ID0+IHtcbiAgY29uc3QgeyBoZWFkZXJzLCByZXRyaWVzLCB0aW1lb3V0TXMsIGJhc2VCYWNrb2ZmTXMsIHNpZ25hbDogZXh0ZXJuYWxTaWduYWwsIHNob3VsZFJldHJ5ID0gZGVmYXVsdFNob3VsZFJldHJ5IH0gPSBvcHRpb25zO1xuXG4gIGZvciAobGV0IGF0dGVtcHQgPSAxOyBhdHRlbXB0IDw9IHJldHJpZXM7IGF0dGVtcHQrKykge1xuICAgIGlmIChleHRlcm5hbFNpZ25hbD8uYWJvcnRlZCkge1xuICAgICAgdGhyb3cgbmV3IEZldGNoUmV0cmllckFscmVhZHlBYm9ydGVkRXJyb3IoKTtcbiAgICB9XG5cbiAgICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiBjb250cm9sbGVyLmFib3J0KCksIHRpbWVvdXRNcyk7XG5cbiAgICBjb25zdCBvbkV4dGVybmFsQWJvcnQgPSAoKTogdm9pZCA9PiB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgY29udHJvbGxlci5hYm9ydCgpO1xuICAgIH07XG5cbiAgICBpZiAoZXh0ZXJuYWxTaWduYWwpIHtcbiAgICAgIGV4dGVybmFsU2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoJ2Fib3J0Jywgb25FeHRlcm5hbEFib3J0KTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2godXJsLCB7XG4gICAgICAgIGhlYWRlcnMsXG4gICAgICAgIHNpZ25hbDogY29udHJvbGxlci5zaWduYWwsXG4gICAgICB9KTtcblxuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIGV4dGVybmFsU2lnbmFsPy5yZW1vdmVFdmVudExpc3RlbmVyKCdhYm9ydCcsIG9uRXh0ZXJuYWxBYm9ydCk7XG5cbiAgICAgIGlmIChyZXMub2spIHtcbiAgICAgICAgcmV0dXJuIHJlcztcbiAgICAgIH1cblxuICAgICAgY29uc3QgdGV4dCA9IGF3YWl0IHJlcy50ZXh0KCk7XG4gICAgICBjb25zdCBpc0NvbnRpbnVlID0gc2hvdWxkUmV0cnkocmVzLCB0ZXh0KTtcblxuICAgICAgaWYgKGlzQ29udGludWUpIHtcbiAgICAgICAgaWYgKGF0dGVtcHQgPT09IHJldHJpZXMpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRmV0Y2hSZXRyaWVySHR0cEVycm9yKGBIVFRQICR7cmVzLnN0YXR1c31gLCByZXMuc3RhdHVzKTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB3YWl0KGZ1bGxKaXR0ZXIoYmFzZUJhY2tvZmZNcywgYXR0ZW1wdCkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEZldGNoUmV0cmllckh0dHBFcnJvcihgTm9uLXJldHJpYWJsZSBIVFRQIGVycm9yOiAke3Jlcy5zdGF0dXN9YCwgcmVzLnN0YXR1cyk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyOiB1bmtub3duKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgICAgZXh0ZXJuYWxTaWduYWw/LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2Fib3J0Jywgb25FeHRlcm5hbEFib3J0KTtcblxuICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIEVycm9yICYmIGVyci5uYW1lID09PSAnQWJvcnRFcnJvcicpIHtcbiAgICAgICAgaWYgKGF0dGVtcHQgPT09IHJldHJpZXMpIHRocm93IGVyciBpbnN0YW5jZW9mIEZldGNoUmV0cmllckFib3J0RXJyb3IgPyBlcnIgOiBuZXcgRmV0Y2hSZXRyaWVyQWJvcnRFcnJvcigpO1xuICAgICAgICBhd2FpdCB3YWl0KGZ1bGxKaXR0ZXIoYmFzZUJhY2tvZmZNcywgYXR0ZW1wdCkpO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIFR5cGVFcnJvcikge1xuICAgICAgICBpZiAoYXR0ZW1wdCA9PT0gcmV0cmllcykgdGhyb3cgbmV3IEZldGNoUmV0cmllck5ldHdvcmtFcnJvcignTmV0d29yayBlcnJvcicsIGVycik7XG4gICAgICAgIGF3YWl0IHdhaXQoZnVsbEppdHRlcihiYXNlQmFja29mZk1zLCBhdHRlbXB0KSk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICB9XG5cbiAgdGhyb3cgbmV3IEZldGNoUmV0cmllclVucmVhY2hhYmxlRXJyb3IoKTtcbn07XG5cbi8qKlxuICogQHBhcmFtIG1zIC0gRGVsYXkgaW4gbWlsbGlzZWNvbmRzXG4gKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciBgbXNgXG4gKi9cbmNvbnN0IHdhaXQgPSAobXM6IG51bWJlcik6IFByb21pc2U8dm9pZD4gPT4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcbn07XG5cbi8qKlxuICogRnVsbCBqaXR0ZXIgYmFja29mZjogcmFuZG9tIGRlbGF5IGluIGBbMCwgYmFzZSAqIDJeYXR0ZW1wdClgIG1zIChBV1MtcmVjb21tZW5kZWQgcGF0dGVybikuXG4gKlxuICogQHBhcmFtIGJhc2UgLSBCYXNlIGJhY2tvZmYgaW4gbWlsbGlzZWNvbmRzXG4gKiBAcGFyYW0gYXR0ZW1wdCAtIDEtYmFzZWQgYXR0ZW1wdCBpbmRleCAoZmlyc3QgcmV0cnkgdXNlcyBgYXR0ZW1wdCA9PT0gMWApXG4gKiBAcmV0dXJucyBXYWl0IGR1cmF0aW9uIGluIG1pbGxpc2Vjb25kcyBiZWZvcmUgdGhlIG5leHQgYXR0ZW1wdFxuICovXG5jb25zdCBmdWxsSml0dGVyID0gKGJhc2U6IG51bWJlciwgYXR0ZW1wdDogbnVtYmVyKTogbnVtYmVyID0+IHtcbiAgY29uc3QgY2FwID0gYmFzZSAqIE1hdGgucG93KDIsIGF0dGVtcHQpO1xuICByZXR1cm4gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogY2FwKTtcbn07XG4iXX0=
package/package.json CHANGED
@@ -42,7 +42,7 @@
42
42
  "eslint-plugin-import": "^2.32.0",
43
43
  "jest": "^30.3.0",
44
44
  "jest-junit": "^16",
45
- "projen": "^0.99.20",
45
+ "projen": "^0.99.21",
46
46
  "ts-jest": "^29.4.6",
47
47
  "ts-node": "^10.9.2",
48
48
  "typescript": "5.9.x"
@@ -55,7 +55,7 @@
55
55
  "publishConfig": {
56
56
  "access": "public"
57
57
  },
58
- "version": "0.1.8",
58
+ "version": "0.2.0",
59
59
  "jest": {
60
60
  "coverageProvider": "v8",
61
61
  "testMatch": [