velocious 1.0.684 → 1.0.686

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
@@ -2642,7 +2642,7 @@ actual state sources must still agree.
2642
2642
 
2643
2643
  `maxConcurrentInlineJobs` (default: `4`) caps how many `executionMode: "inline"` jobs a single `background-jobs-worker` process runs in parallel. Concurrency is at the JS event-loop level: every job in flight shares the worker's process and DB connection pool, so the cap should fit the pool, not the CPU count. Forking remains the right tool when you need memory isolation across long-running jobs or want to use more cores; select it with `executionMode: "forked"`.
2644
2644
 
2645
- New jobs default to `executionMode: "pooled"`: a worker runs them in warm, reusable Node child runners. `pooledRunnerCount` (default: `4`) bounds this independent per-worker pool, and `pooledRunnerConcurrency` (default: `1`) sets how many jobs each child runs at once on its own event loop, so total pooled capacity is `pooledRunnerCount × pooledRunnerConcurrency` — raise concurrency for I/O-bound jobs to get high throughput from a bounded, isolated set of processes. Workers advertise that exact free-slot count and the main consumes one slot per durable handoff, allowing one readiness notification to fill the pool. If an initialized child exits unexpectedly, the worker immediately advertises the freed capacity while failure reports retry; the replacement is spawned lazily by the next dispatch, and a pre-startup crash does not trigger a respawn loop. `pooledRunnerCount`, `pooledRunnerConcurrency`, and `pooledRunnerMaxJobs` must be finite positive integers; the RSS and lifetime limits must be finite positive numbers. A child is recycled after an acknowledged job when it reaches `pooledRunnerMaxJobs` (default: `100`), `pooledRunnerMaxRssBytes` (default: `536870912`, or 512 MiB), or `pooledRunnerMaxLifetimeMs` (default: `3600000`, or one hour). Drained recycling records `parent_retire_drained` and refuses to signal while any job remains tracked; timeout and bounded worker-stop paths retain `job_timeout` and `worker_stop` across SIGKILL escalation. Before resource teardown the child attempts one bounded lifecycle observation over IPC. `execution_mode` is the single source of truth for a job's runtime — pooled rows persist as `execution_mode = "pooled"` directly. Pooled jobs also persist acceptance evidence (`child_received_at_ms`, `child_started_at_ms`, `child_instance_id`, `child_pid`) so a handed-off job can be told apart from one whose runner never picked it up. See [execution modes and pooled runners](docs/background-jobs.md#execution-modes-and-pooled-runners).
2645
+ New jobs default to `executionMode: "pooled"`: a worker runs them in warm, reusable Node child runners. `pooledRunnerCount` (default: `4`) bounds this independent per-worker pool, and `pooledRunnerConcurrency` (default: `1`) sets how many jobs each child runs at once on its own event loop, so total pooled capacity is `pooledRunnerCount × pooledRunnerConcurrency` — raise concurrency for I/O-bound jobs to get high throughput from a bounded, isolated set of processes. Workers advertise that exact free-slot count and the main consumes one slot per durable handoff, allowing one readiness notification to fill the pool. If an initialized child exits unexpectedly, the worker immediately advertises the freed capacity while failure reports retry; the replacement is spawned lazily by the next dispatch, and a pre-startup crash does not trigger a respawn loop. `pooledRunnerCount`, `pooledRunnerConcurrency`, and `pooledRunnerMaxJobs` must be finite positive integers; the RSS and lifetime limits must be finite positive numbers. A child is recycled after an acknowledged job when it reaches `pooledRunnerMaxJobs` (default: `100`), its peak RSS (the child-reported kernel `VmHWM` high-water mark) crosses `pooledRunnerMaxRssBytes` (default: `536870912`, or 512 MiB), or `pooledRunnerMaxLifetimeMs` (default: `3600000`, or one hour). The RSS trigger uses the child's monotonic peak, not its settled RSS at job-outcome time, so a burst working-set that garbage-collects before the outcome still retires the child. Drained recycling records `parent_retire_drained` and refuses to signal while any job remains tracked; timeout and bounded worker-stop paths retain `job_timeout` and `worker_stop` across SIGKILL escalation. Before resource teardown the child attempts one bounded lifecycle observation over IPC. `execution_mode` is the single source of truth for a job's runtime — pooled rows persist as `execution_mode = "pooled"` directly. Pooled jobs also persist acceptance evidence (`child_received_at_ms`, `child_started_at_ms`, `child_instance_id`, `child_pid`) so a handed-off job can be told apart from one whose runner never picked it up. See [execution modes and pooled runners](docs/background-jobs.md#execution-modes-and-pooled-runners).
2646
2646
 
2647
2647
  Cold pooled jobs share one atomic model-bootstrap phase. If that phase fails, all
2648
2648
  waiting jobs receive the failure; a later job in the surviving child cannot run
@@ -7,6 +7,49 @@ import JsonSocket from "./json-socket.js"
7
7
 
8
8
  const DEFAULT_REQUEST_TIMEOUT_MS = 10000
9
9
  export const MAX_LIFECYCLE_REQUEST_TIMEOUT_MS = 120000
10
+ const LIFECYCLE_CONNECT_RETRY_DELAY_MS = 50
11
+ const TRANSIENT_CONNECT_ERROR_CODES = new Set(["ECONNABORTED", "ECONNREFUSED", "ENOENT"])
12
+
13
+ /**
14
+ * Whether a lifecycle request failure is a transient "endpoint not ready yet"
15
+ * connect failure (the coordinator's control socket opens asynchronously after
16
+ * its process starts) rather than a definitive protocol or state rejection.
17
+ * @param {unknown} error - The rejection reason.
18
+ * @returns {boolean} - Whether the request may be retried.
19
+ */
20
+ function isTransientConnectError(error) {
21
+ if (typeof error !== "object" || error === null) return false
22
+ const code = /** @type {{code?: unknown}} */ (error).code
23
+
24
+ return typeof code === "string" && TRANSIENT_CONNECT_ERROR_CODES.has(code)
25
+ }
26
+
27
+ /**
28
+ * Waits before the next lifecycle connect retry, rejecting with the signal
29
+ * reason when the caller-owned deadline fires mid-wait.
30
+ * @param {number} delayMs - Wait between attempts.
31
+ * @param {AbortSignal} signal - Caller-owned deadline signal.
32
+ * @returns {Promise<void>} - Resolves after the delay, or rejects on abort.
33
+ */
34
+ async function waitBeforeNextAttempt(delayMs, signal) {
35
+ await new Promise((resolve, reject) => {
36
+ if (signal.aborted) {
37
+ reject(signal.reason)
38
+
39
+ return
40
+ }
41
+ const timer = setTimeout(() => {
42
+ signal.removeEventListener("abort", onAbort)
43
+ resolve(undefined)
44
+ }, delayMs)
45
+ const onAbort = () => {
46
+ clearTimeout(timer)
47
+ reject(signal.reason)
48
+ }
49
+
50
+ signal.addEventListener("abort", onAbort)
51
+ })
52
+ }
10
53
 
11
54
  /** One-request acknowledged lifecycle client. */
12
55
  export default class BackgroundJobsLifecycleClient {
@@ -59,14 +102,35 @@ export default class BackgroundJobsLifecycleClient {
59
102
  }
60
103
 
61
104
  /**
62
- * Sends the lifecycle request under its caller-owned deadline.
105
+ * Sends the lifecycle request under its caller-owned deadline. Retries
106
+ * transient "endpoint not ready yet" connect failures (the coordinator opens
107
+ * its control socket asynchronously after its process starts) until the
108
+ * deadline; any definitive protocol or state rejection fails immediately.
63
109
  * @param {object} args - Request details.
64
110
  * @param {"activate" | "retire"} args.action - Lifecycle action.
65
111
  * @param {AbortSignal} args.signal - Request deadline signal.
66
112
  * @returns {Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>} - Resulting state.
67
113
  */
68
114
  async _runRequest({action, signal}) {
69
- const requestId = randomUUID()
115
+ while (true) {
116
+ try {
117
+ return await this._attemptRequest(action, randomUUID(), signal)
118
+ } catch (error) {
119
+ if (!isTransientConnectError(error)) throw error
120
+ if (signal.aborted) throw signal.reason
121
+ await waitBeforeNextAttempt(LIFECYCLE_CONNECT_RETRY_DELAY_MS, signal)
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Performs one lifecycle request attempt against the control socket.
128
+ * @param {"activate" | "retire"} action - Lifecycle action.
129
+ * @param {string} requestId - Request identity for acknowledgement matching.
130
+ * @param {AbortSignal} signal - Request deadline signal.
131
+ * @returns {Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>} - Resulting state.
132
+ */
133
+ async _attemptRequest(action, requestId, signal) {
70
134
  const socket = net.createConnection(this.socketPath)
71
135
  const jsonSocket = new JsonSocket(socket)
72
136
 
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import { randomUUID } from "node:crypto"
4
+ import { readFileSync } from "node:fs"
4
5
  import v8 from "node:v8"
5
6
  import timeout from "awaitery/build/timeout.js"
6
7
  import runJobPayload, { BackgroundJobPerformedFailure } from "./job-runner.js"
@@ -20,6 +21,31 @@ const MEMORY_OBSERVATION_INTERVAL_MS = 10000
20
21
  /** Bound on in-flight job ids carried in a memory observation. */
21
22
  const MEMORY_OBSERVATION_JOB_ID_LIMIT = 32
22
23
 
24
+ /**
25
+ * Reads this process's peak resident set size (the kernel high-water mark,
26
+ * `VmHWM` in `/proc/self/status`). Unlike a point-in-time RSS sample, this is
27
+ * monotonic for the life of the process: once a burst ratchets the working set
28
+ * up, `VmHWM` remembers it even after garbage collection returns the pages.
29
+ * That makes it the honest signal for recycling a pooled child — a child that
30
+ * spiked to several gigabytes during a build burst keeps its ratcheted floor
31
+ * and must be replaced, even though its settled RSS at job-outcome time looks
32
+ * modest. A read failure (no `/proc`) degrades to the current RSS sample so the
33
+ * observation is never blocked on it.
34
+ * @returns {number} Peak RSS in bytes, or the current RSS sample when unreadable.
35
+ */
36
+ function readPeakRssBytes() {
37
+ const fallbackBytes = process.memoryUsage().rss
38
+ try {
39
+ const status = readFileSync("/proc/self/status", "utf8")
40
+ const match = /^VmHWM:\s+(\d+)\s+kB$/m.exec(status)
41
+ if (!match) return fallbackBytes
42
+ const kibibytes = Number(match[1])
43
+ return Number.isFinite(kibibytes) && kibibytes > 0 ? kibibytes * 1024 : fallbackBytes
44
+ } catch {
45
+ return fallbackBytes
46
+ }
47
+ }
48
+
23
49
  setRunnerProcessTitle()
24
50
 
25
51
  /** @type {Promise<void> | undefined} */
@@ -162,7 +188,7 @@ function sendChildAcceptance(type, payload, observedAtMs) {
162
188
  * child runs 25 concurrent jobs) distinguishes a V8 heap-growth leak from
163
189
  * external/array-buffer (native resource) growth, and the in-flight job ids
164
190
  * tie the observation to the work that was running.
165
- * @returns {{activeJobIds: string[], activeJobIdsTruncatedCount: number, childInstanceId: string, childPid: number, childUptimeMs: number, heapStatistics: ReturnType<typeof v8.getHeapStatistics>, jobCount: number, memoryUsage: ReturnType<typeof process.memoryUsage>, observedAtMs: number, rssBytes: number, type: "pooled-child-memory", uptimeMs: number}} - Bounded memory observation for this child.
191
+ * @returns {{activeJobIds: string[], activeJobIdsTruncatedCount: number, childInstanceId: string, childPid: number, childUptimeMs: number, heapStatistics: ReturnType<typeof v8.getHeapStatistics>, jobCount: number, memoryUsage: ReturnType<typeof process.memoryUsage>, observedAtMs: number, peakRssBytes: number, rssBytes: number, type: "pooled-child-memory", uptimeMs: number}} - Bounded memory observation for this child.
166
192
  */
167
193
  function collectMemoryObservation() {
168
194
  const jobIds = [...runningJobIds]
@@ -179,6 +205,7 @@ function collectMemoryObservation() {
179
205
  jobCount: jobIds.length,
180
206
  memoryUsage,
181
207
  observedAtMs: Date.now(),
208
+ peakRssBytes: readPeakRssBytes(),
182
209
  rssBytes: memoryUsage.rss,
183
210
  type: "pooled-child-memory",
184
211
  uptimeMs: Math.floor(process.uptime() * 1000)
@@ -289,6 +316,7 @@ function sendOutcome({jobId, acknowledged, status, error}) {
289
316
  jobId,
290
317
  acknowledged,
291
318
  status,
319
+ peakRssBytes: readPeakRssBytes(),
292
320
  rssBytes: process.memoryUsage().rss,
293
321
  error: error?.message
294
322
  }, () => resolve(undefined))
@@ -90,6 +90,7 @@
90
90
  * @property {number} jobCount - In-flight job count.
91
91
  * @property {ReturnType<typeof import("node:process").memoryUsage>} memoryUsage - Process memory breakdown at the sample.
92
92
  * @property {number} observedAtMs - Epoch ms the child sampled.
93
+ * @property {number} peakRssBytes - Child-reported peak RSS (kernel `VmHWM` high-water mark) in bytes, monotonic for the child's life.
93
94
  * @property {number} rssBytes - Resident set size in bytes at the sample.
94
95
  * @property {"pooled-child-memory"} type - Discriminator.
95
96
  * @property {number} uptimeMs - Process uptime in ms.
@@ -1404,7 +1404,7 @@ export default class BackgroundJobsWorker {
1404
1404
  */
1405
1405
  _handlePooledChildMessage({child, message}) {
1406
1406
  if (!message || typeof message !== "object") return
1407
- const record = /** @type {{type?: ReturnType<typeof JSON.parse>, childInstanceId?: ReturnType<typeof JSON.parse>, jobId?: ReturnType<typeof JSON.parse>, acknowledged?: ReturnType<typeof JSON.parse>, rssBytes?: ReturnType<typeof JSON.parse>, error?: ReturnType<typeof JSON.parse>}} */ (message)
1407
+ const record = /** @type {{type?: ReturnType<typeof JSON.parse>, childInstanceId?: ReturnType<typeof JSON.parse>, jobId?: ReturnType<typeof JSON.parse>, acknowledged?: ReturnType<typeof JSON.parse>, rssBytes?: ReturnType<typeof JSON.parse>, peakRssBytes?: ReturnType<typeof JSON.parse>, error?: ReturnType<typeof JSON.parse>}} */ (message)
1408
1408
  const state = this.pooledChildStates.get(child)
1409
1409
  if (record.type === "ready") {
1410
1410
  if (state) {
@@ -1458,8 +1458,13 @@ export default class BackgroundJobsWorker {
1458
1458
  }
1459
1459
 
1460
1460
  const rssBytes = typeof record.rssBytes === "number" ? record.rssBytes : Number.POSITIVE_INFINITY
1461
+ // The child's peak RSS (VmHWM) is monotonic and outlives the settled
1462
+ // sample: a ratcheted burst working-set must recycle the child even when
1463
+ // settled RSS at outcome time looks modest. A child without peak support
1464
+ // falls back to the settled sample.
1465
+ const peakRssBytes = typeof record.peakRssBytes === "number" ? record.peakRssBytes : rssBytes
1461
1466
  const runnerAgeMs = Date.now() - state.createdAtMs
1462
- if (!state.retiring && (state.jobsRun >= this.pooledRunnerMaxJobs || rssBytes >= this.pooledRunnerMaxRssBytes || runnerAgeMs >= this.pooledRunnerMaxLifetimeMs || this.shouldStop)) {
1467
+ if (!state.retiring && (state.jobsRun >= this.pooledRunnerMaxJobs || peakRssBytes >= this.pooledRunnerMaxRssBytes || runnerAgeMs >= this.pooledRunnerMaxLifetimeMs || this.shouldStop)) {
1463
1468
  this._beginRetirePooledChild(child)
1464
1469
  }
1465
1470
  this._terminateIfDrained(child)
@@ -1524,6 +1529,7 @@ export default class BackgroundJobsWorker {
1524
1529
  childPid: message.childPid,
1525
1530
  childUptimeS: Math.round(message.childUptimeMs / 1000),
1526
1531
  rssMb: Math.round(message.rssBytes / (1024 * 1024)),
1532
+ peakRssMb: Math.round((typeof message.peakRssBytes === "number" ? message.peakRssBytes : message.rssBytes) / (1024 * 1024)),
1527
1533
  heapUsedMb: Math.round(heap.used_heap_size / (1024 * 1024)),
1528
1534
  heapTotalMb: Math.round(heap.total_heap_size / (1024 * 1024)),
1529
1535
  heapLimitMb: Math.round(heap.heap_size_limit / (1024 * 1024)),
@@ -194,7 +194,7 @@
194
194
  * @property {number} [pooledRunnerCount] - Number of warm, reusable child runners owned by each worker. Pooled capacity is separate from inline and forked/spawned capacity. Default: `4`.
195
195
  * @property {number} [pooledRunnerConcurrency] - Number of jobs each pooled child runs concurrently on its own event loop. Total per-worker pooled capacity is `pooledRunnerCount × pooledRunnerConcurrency`. `1` (default) keeps each child serial; raise it for I/O-bound jobs so a bounded set of isolated processes handles high concurrency (like the inline lane) without one process per concurrent job. Default: `1`.
196
196
  * @property {number} [pooledRunnerMaxJobs] - Number of sequential jobs a pooled child runs before it is replaced, bounding process-level resource accumulation. Default: `100`.
197
- * @property {number} [pooledRunnerMaxRssBytes] - RSS bytes after an acknowledged job at which a pooled child is replaced. Default: `536870912` (512 MiB).
197
+ * @property {number} [pooledRunnerMaxRssBytes] - Peak RSS (the kernel `VmHWM` high-water mark reported by the child) at which a pooled child is replaced after an acknowledged job. The child's peak, not its settled RSS at job-outcome time, is the trigger, so a burst working-set that reclaims before the outcome still retires the child. Default: `536870912` (512 MiB).
198
198
  * @property {number} [pooledRunnerMaxLifetimeMs] - Age after an acknowledged job at which a pooled child is replaced. Default: `3600000` (one hour).
199
199
  * @property {Record<string, {maxConcurrent?: number, priority?: number}>} [queues] - Per-queue
200
200
  * concurrency caps and dispatch priorities, Sidekiq-style. A job declares its queue (static
@@ -35,7 +35,10 @@ export default class BackgroundJobsLifecycleClient {
35
35
  */
36
36
  _request(action: "activate" | "retire"): Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>;
37
37
  /**
38
- * Sends the lifecycle request under its caller-owned deadline.
38
+ * Sends the lifecycle request under its caller-owned deadline. Retries
39
+ * transient "endpoint not ready yet" connect failures (the coordinator opens
40
+ * its control socket asynchronously after its process starts) until the
41
+ * deadline; any definitive protocol or state rejection fails immediately.
39
42
  * @param {object} args - Request details.
40
43
  * @param {"activate" | "retire"} args.action - Lifecycle action.
41
44
  * @param {AbortSignal} args.signal - Request deadline signal.
@@ -45,5 +48,13 @@ export default class BackgroundJobsLifecycleClient {
45
48
  action: "activate" | "retire";
46
49
  signal: AbortSignal;
47
50
  }): Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>;
51
+ /**
52
+ * Performs one lifecycle request attempt against the control socket.
53
+ * @param {"activate" | "retire"} action - Lifecycle action.
54
+ * @param {string} requestId - Request identity for acknowledgement matching.
55
+ * @param {AbortSignal} signal - Request deadline signal.
56
+ * @returns {Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>} - Resulting state.
57
+ */
58
+ _attemptRequest(action: "activate" | "retire", requestId: string, signal: AbortSignal): Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>;
48
59
  }
49
60
  //# sourceMappingURL=lifecycle-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-client.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/lifecycle-client.js"],"names":[],"mappings":"AAQA,eAAO,MAAM,gCAAgC,SAAS,CAAA;AAEtD,iDAAiD;AACjD,MAAM,CAAC,OAAO,OAAO,6BAA6B;IAezC,YAAY;IACZ,UAAU;IAIV,gBAAgB;IAnBvB;;;;;;;OAOG;IACH,YAAY,EAAC,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,gBAA6C,EAAC,EALjG;QAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO,CAC7C;QAAsB,YAAY,AAAlC,CACA,EADQ,MAAM,CACd;QAAsB,UAAU,AAAhC,CACA,EADQ,MAAM,CACd;QAAsB,gBAAgB,AAAtC,CACF,EADU,MAAM,CAChB;KACmG,EAcnG;IAED;;;OAGG;IACG,QAAQ,IAFD,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAEtB;IAE3D;;;OAGG;IACG,MAAM,IAFC,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAE1B;IAEvD;;;;OAIG;IACG,QAAQ,CAAC,MAAM,EAHV,UAAU,GAAG,QAGH,GAFR,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAOhF;IAED;;;;;;OAMG;IACG,WAAW,CAAC,EAAC,MAAM,EAAE,MAAM,EAAC,EAJ/B;QAAoC,MAAM,EAAlC,UAAU,GAAG,QAAQ,CAC7B;QAA0B,MAAM,EAAxB,WAAW,CACnB;KAE+B,GAFrB,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CA4DhF;CACF"}
1
+ {"version":3,"file":"lifecycle-client.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/lifecycle-client.js"],"names":[],"mappings":"AAQA,eAAO,MAAM,gCAAgC,SAAS,CAAA;AA6CtD,iDAAiD;AACjD,MAAM,CAAC,OAAO,OAAO,6BAA6B;IAezC,YAAY;IACZ,UAAU;IAIV,gBAAgB;IAnBvB;;;;;;;OAOG;IACH,YAAY,EAAC,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,gBAA6C,EAAC,EALjG;QAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO,CAC7C;QAAsB,YAAY,AAAlC,CACA,EADQ,MAAM,CACd;QAAsB,UAAU,AAAhC,CACA,EADQ,MAAM,CACd;QAAsB,gBAAgB,AAAtC,CACF,EADU,MAAM,CAChB;KACmG,EAcnG;IAED;;;OAGG;IACG,QAAQ,IAFD,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAEtB;IAE3D;;;OAGG;IACG,MAAM,IAFC,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAE1B;IAEvD;;;;OAIG;IACG,QAAQ,CAAC,MAAM,EAHV,UAAU,GAAG,QAGH,GAFR,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAOhF;IAED;;;;;;;;;OASG;IACG,WAAW,CAAC,EAAC,MAAM,EAAE,MAAM,EAAC,EAJ/B;QAAoC,MAAM,EAAlC,UAAU,GAAG,QAAQ,CAC7B;QAA0B,MAAM,EAAxB,WAAW,CACnB;KAE+B,GAFrB,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CAYhF;IAED;;;;;;OAMG;IACG,eAAe,CAAC,MAAM,EALjB,UAAU,GAAG,QAKI,EAAE,SAAS,EAJ5B,MAI4B,EAAE,MAAM,EAHpC,WAGoC,GAFlC,OAAO,CAAC,OAAO,YAAY,EAAE,sCAAsC,CAAC,CA2DhF;CACF"}
@@ -5,6 +5,45 @@ import timeout from "awaitery/build/timeout.js";
5
5
  import JsonSocket from "./json-socket.js";
6
6
  const DEFAULT_REQUEST_TIMEOUT_MS = 10000;
7
7
  export const MAX_LIFECYCLE_REQUEST_TIMEOUT_MS = 120000;
8
+ const LIFECYCLE_CONNECT_RETRY_DELAY_MS = 50;
9
+ const TRANSIENT_CONNECT_ERROR_CODES = new Set(["ECONNABORTED", "ECONNREFUSED", "ENOENT"]);
10
+ /**
11
+ * Whether a lifecycle request failure is a transient "endpoint not ready yet"
12
+ * connect failure (the coordinator's control socket opens asynchronously after
13
+ * its process starts) rather than a definitive protocol or state rejection.
14
+ * @param {unknown} error - The rejection reason.
15
+ * @returns {boolean} - Whether the request may be retried.
16
+ */
17
+ function isTransientConnectError(error) {
18
+ if (typeof error !== "object" || error === null)
19
+ return false;
20
+ const code = /** @type {{code?: unknown}} */ (error).code;
21
+ return typeof code === "string" && TRANSIENT_CONNECT_ERROR_CODES.has(code);
22
+ }
23
+ /**
24
+ * Waits before the next lifecycle connect retry, rejecting with the signal
25
+ * reason when the caller-owned deadline fires mid-wait.
26
+ * @param {number} delayMs - Wait between attempts.
27
+ * @param {AbortSignal} signal - Caller-owned deadline signal.
28
+ * @returns {Promise<void>} - Resolves after the delay, or rejects on abort.
29
+ */
30
+ async function waitBeforeNextAttempt(delayMs, signal) {
31
+ await new Promise((resolve, reject) => {
32
+ if (signal.aborted) {
33
+ reject(signal.reason);
34
+ return;
35
+ }
36
+ const timer = setTimeout(() => {
37
+ signal.removeEventListener("abort", onAbort);
38
+ resolve(undefined);
39
+ }, delayMs);
40
+ const onAbort = () => {
41
+ clearTimeout(timer);
42
+ reject(signal.reason);
43
+ };
44
+ signal.addEventListener("abort", onAbort);
45
+ });
46
+ }
8
47
  /** One-request acknowledged lifecycle client. */
9
48
  export default class BackgroundJobsLifecycleClient {
10
49
  /**
@@ -54,14 +93,37 @@ export default class BackgroundJobsLifecycleClient {
54
93
  }, async ({ control }) => await this._runRequest({ action, signal: control.signal }));
55
94
  }
56
95
  /**
57
- * Sends the lifecycle request under its caller-owned deadline.
96
+ * Sends the lifecycle request under its caller-owned deadline. Retries
97
+ * transient "endpoint not ready yet" connect failures (the coordinator opens
98
+ * its control socket asynchronously after its process starts) until the
99
+ * deadline; any definitive protocol or state rejection fails immediately.
58
100
  * @param {object} args - Request details.
59
101
  * @param {"activate" | "retire"} args.action - Lifecycle action.
60
102
  * @param {AbortSignal} args.signal - Request deadline signal.
61
103
  * @returns {Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>} - Resulting state.
62
104
  */
63
105
  async _runRequest({ action, signal }) {
64
- const requestId = randomUUID();
106
+ while (true) {
107
+ try {
108
+ return await this._attemptRequest(action, randomUUID(), signal);
109
+ }
110
+ catch (error) {
111
+ if (!isTransientConnectError(error))
112
+ throw error;
113
+ if (signal.aborted)
114
+ throw signal.reason;
115
+ await waitBeforeNextAttempt(LIFECYCLE_CONNECT_RETRY_DELAY_MS, signal);
116
+ }
117
+ }
118
+ }
119
+ /**
120
+ * Performs one lifecycle request attempt against the control socket.
121
+ * @param {"activate" | "retire"} action - Lifecycle action.
122
+ * @param {string} requestId - Request identity for acknowledgement matching.
123
+ * @param {AbortSignal} signal - Request deadline signal.
124
+ * @returns {Promise<import("./types.js").BackgroundJobsGenerationLifecycleState>} - Resulting state.
125
+ */
126
+ async _attemptRequest(action, requestId, signal) {
65
127
  const socket = net.createConnection(this.socketPath);
66
128
  const jsonSocket = new JsonSocket(socket);
67
129
  return await new Promise((resolve, reject) => {
@@ -123,4 +185,4 @@ export default class BackgroundJobsLifecycleClient {
123
185
  });
124
186
  }
125
187
  }
126
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlmZWN5Y2xlLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9iYWNrZ3JvdW5kLWpvYnMvbGlmZWN5Y2xlLWNsaWVudC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUN4QyxPQUFPLEdBQUcsTUFBTSxVQUFVLENBQUE7QUFDMUIsT0FBTyxPQUFPLE1BQU0sMkJBQTJCLENBQUE7QUFDL0MsT0FBTyxVQUFVLE1BQU0sa0JBQWtCLENBQUE7QUFFekMsTUFBTSwwQkFBMEIsR0FBRyxLQUFLLENBQUE7QUFDeEMsTUFBTSxDQUFDLE1BQU0sZ0NBQWdDLEdBQUcsTUFBTSxDQUFBO0FBRXRELGlEQUFpRDtBQUNqRCxNQUFNLENBQUMsT0FBTyxPQUFPLDZCQUE2QjtJQUNoRDs7Ozs7OztPQU9HO0lBQ0gsWUFBWSxFQUFDLGFBQWEsRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLGdCQUFnQixHQUFHLDBCQUEwQixFQUFDO1FBQ2xHLE1BQU0sZ0JBQWdCLEdBQUcsYUFBYSxDQUFDLHFDQUFxQyxDQUFDO1lBQzNFLFlBQVk7WUFDWixtQkFBbUIsRUFBRSxVQUFVO1lBQy9CLFVBQVUsRUFBRSwrQkFBK0I7U0FDNUMsQ0FBQyxDQUFBO1FBQ0YsSUFBSSxDQUFDLFlBQVksR0FBRyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUE7UUFDakQsSUFBSSxDQUFDLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQyxtQkFBbUIsQ0FBQTtRQUN0RCxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLGdCQUFnQixHQUFHLENBQUMsSUFBSSxnQkFBZ0IsR0FBRyxnQ0FBZ0MsRUFBRSxDQUFDO1lBQ3ZILE1BQU0sSUFBSSxTQUFTLENBQUMscURBQXFELGdDQUFnQyxFQUFFLENBQUMsQ0FBQTtRQUM5RyxDQUFDO1FBQ0QsSUFBSSxDQUFDLGdC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Q0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUE7b0JBQy9FLE1BQU0sQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUE7b0JBQy9CLE9BQU07Z0JBQ1IsQ0FBQztnQkFDRCxJQUFJLE9BQU8sQ0FBQyxJQUFJLEtBQUssK0JBQStCLElBQUksT0FBTyxDQUFDLFlBQVksS0FBSyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7b0JBQ25HLE1BQU0sQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUMsQ0FBQyxDQUFBO29CQUN4RixPQUFNO2dCQUNSLENBQUM7Z0JBQ0QsTUFBTSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUE7WUFDbkQsQ0FBQyxDQUFDLENBQUE7WUFDRixNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQTtZQUNqQyxJQUFJLE1BQU0sQ0FBQyxPQUFPO2dCQUFFLE9BQU8sRUFBRSxDQUFBO1FBQy9CLENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCB7IHJhbmRvbVVVSUQgfSBmcm9tIFwibm9kZTpjcnlwdG9cIlxuaW1wb3J0IG5ldCBmcm9tIFwibm9kZTpuZXRcIlxuaW1wb3J0IHRpbWVvdXQgZnJvbSBcImF3YWl0ZXJ5L2J1aWxkL3RpbWVvdXQuanNcIlxuaW1wb3J0IEpzb25Tb2NrZXQgZnJvbSBcIi4vanNvbi1zb2NrZXQuanNcIlxuXG5jb25zdCBERUZBVUxUX1JFUVVFU1RfVElNRU9VVF9NUyA9IDEwMDAwXG5leHBvcnQgY29uc3QgTUFYX0xJRkVDWUNMRV9SRVFVRVNUX1RJTUVPVVRfTVMgPSAxMjAwMDBcblxuLyoqIE9uZS1yZXF1ZXN0IGFja25vd2xlZGdlZCBsaWZlY3ljbGUgY2xpZW50LiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQmFja2dyb3VuZEpvYnNMaWZlY3ljbGVDbGllbnQge1xuICAvKipcbiAgICogQ3JlYXRlcyBhIGxpZmVjeWNsZSBjbGllbnQuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQ2xpZW50IG9wdGlvbnMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBhcmdzLmNvbmZpZ3VyYXRpb24gLSBDb25maWd1cmF0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MuZ2VuZXJhdGlvbklkXSAtIEV4cGxpY2l0IGdlbmVyYXRpb24gaWRlbnRpdHkuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbYXJncy5zb2NrZXRQYXRoXSAtIEV4cGxpY2l0IGNvbnRyb2wgc29ja2V0IHBhdGguXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbYXJncy5yZXF1ZXN0VGltZW91dE1zXSAtIFJlcXVlc3QgZGVhZGxpbmUgYmVsb3cgdGhlIHN1cGVydmlzb3IgaG9vayB0aW1lb3V0IChkZWZhdWx0OiAxMDAwMCkuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29uZmlndXJhdGlvbiwgZ2VuZXJhdGlvbklkLCBzb2NrZXRQYXRoLCByZXF1ZXN0VGltZW91dE1zID0gREVGQVVMVF9SRVFVRVNUX1RJTUVPVVRfTVN9KSB7XG4gICAgY29uc3QgZ2VuZXJhdGlvbkNvbmZpZyA9IGNvbmZpZ3VyYXRpb24ucmVzb2x2ZUJhY2tncm91bmRKb2JzR2VuZXJhdGlvbkNvbmZpZyh7XG4gICAgICBnZW5lcmF0aW9uSWQsXG4gICAgICBsaWZlY3ljbGVTb2NrZXRQYXRoOiBzb2NrZXRQYXRoLFxuICAgICAgc291cmNlTmFtZTogXCJCYWNrZ3JvdW5kSm9ic0xpZmVjeWNsZUNsaWVudFwiXG4gICAgfSlcbiAgICB0aGlzLmdlbmVyYXRpb25JZCA9IGdlbmVyYXRpb25Db25maWcuZ2VuZXJhdGlvbklkXG4gICAgdGhpcy5zb2NrZXRQYXRoID0gZ2VuZXJhdGlvbkNvbmZpZy5saWZlY3ljbGVTb2NrZXRQYXRoXG4gICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKHJlcXVlc3RUaW1lb3V0TXMpIHx8IHJlcXVlc3RUaW1lb3V0TXMgPCAxIHx8IHJlcXVlc3RUaW1lb3V0TXMgPiBNQVhfTElGRUNZQ0xFX1JFUVVFU1RfVElNRU9VVF9NUykge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihgcmVxdWVzdFRpbWVvdXRNcyBtdXN0IGJlIGFuIGludGVnZXIgYmV0d2VlbiAxIGFuZCAke01BWF9MSUZFQ1lDTEVfUkVRVUVTVF9USU1FT1VUX01TfWApXG4gICAgfVxuICAgIHRoaXMucmVxdWVzdFRpbWVvdXRNcyA9IHJlcXVlc3RUaW1lb3V0TXNcbiAgICBpZiAoIXRoaXMuZ2VuZXJhdGlvbklkKSB0aHJvdyBuZXcgRXJyb3IoXCJCYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIGNsaWVudCByZXF1aXJlcyBnZW5lcmF0aW9uSWRcIilcbiAgICBpZiAoIXRoaXMuc29ja2V0UGF0aCkgdGhyb3cgbmV3IEVycm9yKFwiQmFja2dyb3VuZCBqb2JzIGxpZmVjeWNsZSBjbGllbnQgcmVxdWlyZXMgbGlmZWN5Y2xlU29ja2V0UGF0aFwiKVxuICB9XG5cbiAgLyoqXG4gICAqIEFjdGl2YXRlcyB0aGUgZ2VuZXJhdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25MaWZlY3ljbGVTdGF0ZT59IC0gUmVzdWx0aW5nIHN0YXRlLlxuICAgKi9cbiAgYXN5bmMgYWN0aXZhdGUoKSB7IHJldHVybiBhd2FpdCB0aGlzLl9yZXF1ZXN0KFwiYWN0aXZhdGVcIikgfVxuXG4gIC8qKlxuICAgKiBSZXRpcmVzIHRoZSBnZW5lcmF0aW9uLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JzR2VuZXJhdGlvbkxpZmVjeWNsZVN0YXRlPn0gLSBSZXN1bHRpbmcgc3RhdGUuXG4gICAqL1xuICBhc3luYyByZXRpcmUoKSB7IHJldHVybiBhd2FpdCB0aGlzLl9yZXF1ZXN0KFwicmV0aXJlXCIpIH1cblxuICAvKipcbiAgICogU2VuZHMgZXhhY3RseSBvbmUgbGlmZWN5Y2xlIHJlcXVlc3QuXG4gICAqIEBwYXJhbSB7XCJhY3RpdmF0ZVwiIHwgXCJyZXRpcmVcIn0gYWN0aW9uIC0gTGlmZWN5Y2xlIGFjdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25MaWZlY3ljbGVTdGF0ZT59IC0gUmVzdWx0aW5nIHN0YXRlLlxuICAgKi9cbiAgYXN5bmMgX3JlcXVlc3QoYWN0aW9uKSB7XG4gICAgcmV0dXJuIGF3YWl0IHRpbWVvdXQoe1xuICAgICAgZXJyb3JNZXNzYWdlOiBgQmFja2dyb3VuZCBqb2JzICR7YWN0aW9ufSByZXF1ZXN0IGZvciAke3RoaXMuZ2VuZXJhdGlvbklkfSB0aW1lZCBvdXQgYWZ0ZXIgJHt0aGlzLnJlcXVlc3RUaW1lb3V0TXN9bXMgYXQgJHt0aGlzLnNvY2tldFBhdGh9YCxcbiAgICAgIHRpbWVvdXQ6IHRoaXMucmVxdWVzdFRpbWVvdXRNc1xuICAgIH0sIGFzeW5jICh7Y29udHJvbH0pID0+IGF3YWl0IHRoaXMuX3J1blJlcXVlc3Qoe2FjdGlvbiwgc2lnbmFsOiBjb250cm9sLnNpZ25hbH0pKVxuICB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIHRoZSBsaWZlY3ljbGUgcmVxdWVzdCB1bmRlciBpdHMgY2FsbGVyLW93bmVkIGRlYWRsaW5lLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIFJlcXVlc3QgZGV0YWlscy5cbiAgICogQHBhcmFtIHtcImFjdGl2YXRlXCIgfCBcInJldGlyZVwifSBhcmdzLmFjdGlvbiAtIExpZmVjeWNsZSBhY3Rpb24uXG4gICAqIEBwYXJhbSB7QWJvcnRTaWduYWx9IGFyZ3Muc2lnbmFsIC0gUmVxdWVzdCBkZWFkbGluZSBzaWduYWwuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIF9ydW5SZXF1ZXN0KHthY3Rpb24sIHNpZ25hbH0pIHtcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByYW5kb21VVUlEKClcbiAgICBjb25zdCBzb2NrZXQgPSBuZXQuY3JlYXRlQ29ubmVjdGlvbih0aGlzLnNvY2tldFBhdGgpXG4gICAgY29uc3QganNvblNvY2tldCA9IG5ldyBKc29uU29ja2V0KHNvY2tldClcblxuICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBsZXQgZmluaXNoZWQgPSBmYWxzZVxuICAgICAgLyoqXG4gICAgICAgKiBTZXR0bGVzIHRoZSByZXF1ZXN0IG9uY2UuXG4gICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIFRlYXJkb3duIG9wdGlvbnMuXG4gICAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmRlc3Ryb3ldIC0gRGVzdHJveSBpbnN0ZWFkIG9mIGNsb3NpbmcuXG4gICAgICAgKiBAcGFyYW0geygpID0+IHZvaWR9IGNhbGxiYWNrIC0gU2V0dGxlbWVudCBjYWxsYmFjay5cbiAgICAgICAqL1xuICAgICAgY29uc3QgZmluaXNoID0gKHtkZXN0cm95ID0gZmFsc2V9LCBjYWxsYmFjaykgPT4ge1xuICAgICAgICBpZiAoZmluaXNoZWQpIHJldHVyblxuICAgICAgICBmaW5pc2hlZCA9IHRydWVcbiAgICAgICAgc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KVxuICAgICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoXCJjb25uZWN0XCIsIG9uQ29ubmVjdClcbiAgICAgICAganNvblNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoKVxuICAgICAgICBpZiAoZGVzdHJveSkganNvblNvY2tldC5kZXN0cm95KClcbiAgICAgICAgZWxzZSBqc29uU29ja2V0LmNsb3NlKClcbiAgICAgICAgY2FsbGJhY2soKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBvbkFib3J0ID0gKCkgPT4gZmluaXNoKHtkZXN0cm95OiB0cnVlfSwgKCkgPT4gcmVqZWN0KHNpZ25hbC5yZWFzb24gaW5zdGFuY2VvZiBFcnJvciA/IHNpZ25hbC5yZWFzb24gOiBuZXcgRXJyb3IoXCJCYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIHJlcXVlc3QgYWJvcnRlZFwiKSkpXG4gICAgICBjb25zdCBvbkNvbm5lY3QgPSAoKSA9PiB7XG4gICAgICAgIGpzb25Tb2NrZXQuc2VuZCh7XG4gICAgICAgICAgdHlwZTogXCJiYWNrZ3JvdW5kLWpvYnMtbGlmZWN5Y2xlXCIsXG4gICAgICAgICAgYWN0aW9uLFxuICAgICAgICAgIGdlbmVyYXRpb25JZDogdGhpcy5nZW5lcmF0aW9uSWQsXG4gICAgICAgICAgcmVxdWVzdElkXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgb25BYm9ydClcbiAgICAgIGpzb25Tb2NrZXQub24oXCJlcnJvclwiLCAoZXJyb3IpID0+IGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KGVycm9yKSkpXG4gICAgICBqc29uU29ja2V0Lm9uKFwiY2xvc2VcIiwgKCkgPT4gZmluaXNoKHtkZXN0cm95OiB0cnVlfSwgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgc29ja2V0IGNsb3NlZCBiZWZvcmUgYWNrbm93bGVkZ2VtZW50XCIpKSkpXG4gICAgICBqc29uU29ja2V0Lm9uKFwibWVzc2FnZVwiLCAobWVzc2FnZSkgPT4ge1xuICAgICAgICBpZiAobWVzc2FnZT8ucmVxdWVzdElkICE9PSByZXF1ZXN0SWQgfHwgbWVzc2FnZS5hY3Rpb24gIT09IGFjdGlvbikge1xuICAgICAgICAgIGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgcmVzcG9uc2UgZGlkIG5vdCBtYXRjaCBpdHMgcmVxdWVzdFwiKSkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1lc3NhZ2UudHlwZSA9PT0gXCJiYWNrZ3JvdW5kLWpvYnMtbGlmZWN5Y2xlLWVycm9yXCIpIHtcbiAgICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlLmVycm9yPy5tZXNzYWdlIHx8IFwiQmFja2dyb3VuZCBqb2JzIGxpZmVjeWNsZSByZXF1ZXN0IGZhaWxlZFwiKVxuICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZS5lcnJvcj8ubmFtZSA9PT0gXCJzdHJpbmdcIikgZXJyb3IubmFtZSA9IG1lc3NhZ2UuZXJyb3IubmFtZVxuICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZS5lcnJvcj8uc3RhY2sgPT09IFwic3RyaW5nXCIpIGVycm9yLnN0YWNrID0gbWVzc2FnZS5lcnJvci5zdGFja1xuICAgICAgICAgIGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KGVycm9yKSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAobWVzc2FnZS50eXBlICE9PSBcImJhY2tncm91bmQtam9icy1saWZlY3ljbGUtYWNrXCIgfHwgbWVzc2FnZS5nZW5lcmF0aW9uSWQgIT09IHRoaXMuZ2VuZXJhdGlvbklkKSB7XG4gICAgICAgICAgZmluaXNoKHt9LCAoKSA9PiByZWplY3QobmV3IEVycm9yKFwiSW52YWxpZCBiYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIGFja25vd2xlZGdlbWVudFwiKSkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgZmluaXNoKHt9LCAoKSA9PiByZXNvbHZlKG1lc3NhZ2UubGlmZWN5Y2xlU3RhdGUpKVxuICAgICAgfSlcbiAgICAgIHNvY2tldC5vbmNlKFwiY29ubmVjdFwiLCBvbkNvbm5lY3QpXG4gICAgICBpZiAoc2lnbmFsLmFib3J0ZWQpIG9uQWJvcnQoKVxuICAgIH0pXG4gIH1cbn1cbiJdfQ==
188
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlmZWN5Y2xlLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9iYWNrZ3JvdW5kLWpvYnMvbGlmZWN5Y2xlLWNsaWVudC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUN4QyxPQUFPLEdBQUcsTUFBTSxVQUFVLENBQUE7QUFDMUIsT0FBTyxPQUFPLE1BQU0sMkJBQTJCLENBQUE7QUFDL0MsT0FBTyxVQUFVLE1BQU0sa0JBQWtCLENBQUE7QUFFekMsTUFBTSwwQkFBMEIsR0FBRyxLQUFLLENBQUE7QUFDeEMsTUFBTSxDQUFDLE1BQU0sZ0NBQWdDLEdBQUcsTUFBTSxDQUFBO0FBQ3RELE1BQU0sZ0NBQWdDLEdBQUcsRUFBRSxDQUFBO0FBQzNDLE1BQU0sNkJBQTZCLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxjQUFjLEVBQUUsY0FBYyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUE7QUFFekY7Ozs7OztHQU1HO0FBQ0gsU0FBUyx1QkFBdUIsQ0FBQyxLQUFLO0lBQ3BDLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxLQUFLLENBQUE7SUFDN0QsTUFBTSxJQUFJLEdBQUcsK0JBQStCLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUE7SUFFekQsT0FBTyxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksNkJBQTZCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFBO0FBQzVFLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxLQUFLLFVBQVUscUJBQXFCLENBQUMsT0FBTyxFQUFFLE1BQU07SUFDbEQsTUFBTSxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNwQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQixNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1lBRXJCLE9BQU07UUFDUixDQUFDO1FBQ0QsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEdBQUcsRUFBRTtZQUM1QixNQUFNLENBQUMsbUJBQW1CLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFBO1lBQzVDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQTtRQUNwQixDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFDWCxNQUFNLE9BQU8sR0FBRyxHQUFHLEVBQUU7WUFDbkIsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFBO1lBQ25CLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDdkIsQ0FBQyxDQUFBO1FBRUQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsQ0FBQTtJQUMzQyxDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUM7QUFFRCxpREFBaUQ7QUFDakQsTUFBTSxDQUFDLE9BQU8sT0FBTyw2QkFBNkI7SUFDaEQ7Ozs7Ozs7T0FPRztJQUNILFlBQVksRUFBQyxhQUFhLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxnQkFBZ0IsR0FBRywwQkFBMEIsRUFBQztRQUNsRyxNQUFNLGdCQUFnQixHQUFHLGFBQWEsQ0FBQyxxQ0FBcUMsQ0FBQztZQUMzRSxZQUFZO1lBQ1osbUJBQW1CLEVBQUUsVUFBVTtZQUMvQixVQUFVLEVBQUUsK0JBQStCO1NBQzVDLENBQUMsQ0FBQTtRQUNGLElBQUksQ0FBQyxZQUFZLEdBQUcsZ0JBQWdCLENBQUMsWUFBWSxDQUFBO1FBQ2pELElBQUksQ0FBQyxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUE7UUFDdEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxnQkFBZ0IsR0FBRyxDQUFDLElBQUksZ0JBQWdCLEdBQUcsZ0NBQWdDLEVBQUUsQ0FBQztZQUN2SCxNQUFNLElBQUksU0FBUyxDQUFDLHFEQUFxRCxnQ0FBZ0MsRUFBRSxDQUFDLENBQUE7UUFDOUcsQ0FBQztRQUNELElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQTtRQUN4QyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVk7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxDQUFDLENBQUE7UUFDakcsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQywrREFBK0QsQ0FBQyxDQUFBO0lBQ3hHLENBQUM7SUFFRDs7O09BR0c7SUFDSCxLQUFLLENBQUMsUUFBUSxLQUFLLE9BQU8sTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFBLENBQUMsQ0FBQztJQUUzRDs7O09BR0c7SUFDSCxLQUFLLENBQUMsTUFBTSxLQUFLLE9BQU8sTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFBLENBQUMsQ0FBQztJQUV2RDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLFFBQVEsQ0FBQyxNQUFNO1FBQ25CLE9BQU8sTUFBTSxPQUFPLENBQUM7WUFDbkIsWUFBWSxFQUFFLG1CQUFtQixNQUFNLGdCQUFnQixJQUFJLENBQUMsWUFBWSxvQkFBb0IsSUFBSSxDQUFDLGdCQUFnQixTQUFTLElBQUksQ0FBQyxVQUFVLEVBQUU7WUFDM0ksT0FBTyxFQUFFLElBQUksQ0FBQyxnQkFBZ0I7U0FDL0IsRUFBRSxLQUFLLEVBQUUsRUFBQyxPQUFPLEVBQUMsRUFBRSxFQUFFLENBQUMsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTSxFQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ25GLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSCxLQUFLLENBQUMsV0FBVyxDQUFDLEVBQUMsTUFBTSxFQUFFLE1BQU0sRUFBQztRQUNoQyxPQUFPLElBQUksRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDO2dCQUNILE9BQU8sTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sRUFBRSxVQUFVLEVBQUUsRUFBRSxNQUFNLENBQUMsQ0FBQTtZQUNqRSxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixJQUFJLENBQUMsdUJBQXVCLENBQUMsS0FBSyxDQUFDO29CQUFFLE1BQU0sS0FBSyxDQUFBO2dCQUNoRCxJQUFJLE1BQU0sQ0FBQyxPQUFPO29CQUFFLE1BQU0sTUFBTSxDQUFDLE1BQU0sQ0FBQTtnQkFDdkMsTUFBTSxxQkFBcUIsQ0FBQyxnQ0FBZ0MsRUFBRSxNQUFNLENBQUMsQ0FBQTtZQUN2RSxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsZUFBZSxDQUFDLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTTtRQUM3QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFBO1FBQ3BELE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FBRXpDLE9BQU8sTUFBTSxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUMzQyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUE7WUFDcEI7Ozs7O2VBS0c7WUFDSCxNQUFNLE1BQU0sR0FBRyxDQUFDLEVBQUMsT0FBTyxHQUFHLEtBQUssRUFBQyxFQUFFLFFBQVEsRUFBRSxFQUFFO2dCQUM3QyxJQUFJLFFBQVE7b0JBQUUsT0FBTTtnQkFDcEIsUUFBUSxHQUFHLElBQUksQ0FBQTtnQkFDZixNQUFNLENBQUMsbUJBQW1CLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFBO2dCQUM1QyxNQUFNLENBQUMsY0FBYyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQTtnQkFDM0MsVUFBVSxDQUFDLGtCQUFrQixFQUFFLENBQUE7Z0JBQy9CLElBQUksT0FBTztvQkFBRSxVQUFVLENBQUMsT0FBTyxFQUFFLENBQUE7O29CQUM1QixVQUFVLENBQUMsS0FBSyxFQUFFLENBQUE7Z0JBQ3ZCLFFBQVEsRUFBRSxDQUFBO1lBQ1osQ0FBQyxDQUFBO1lBRUQsTUFBTSxPQUFPLEdBQUcsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEVBQUMsT0FBTyxFQUFFLElBQUksRUFBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsMkNBQTJDLENBQUMsQ0FBQyxDQUFDLENBQUE7WUFDcEssTUFBTSxTQUFTLEdBQUcsR0FBRyxFQUFFO2dCQUNyQixVQUFVLENBQUMsSUFBSSxDQUFDO29CQUNkLElBQUksRUFBRSwyQkFBMkI7b0JBQ2pDLE1BQU07b0JBQ04sWUFBWSxFQUFFLElBQUksQ0FBQyxZQUFZO29CQUMvQixTQUFTO2lCQUNWLENBQUMsQ0FBQTtZQUNKLENBQUMsQ0FBQTtZQUVELE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUE7WUFDekMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQTtZQUNsRSxVQUFVLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBQyxPQUFPLEVBQUUsSUFBSSxFQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLGdFQUFnRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7WUFDaEosVUFBVSxDQUFDLEVBQUUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFBRTtnQkFDbkMsSUFBSSxPQUFPLEVBQUUsU0FBUyxLQUFLLFNBQVMsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLE1BQU0sRUFBRSxDQUFDO29CQUNsRSxNQUFNLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyw4REFBOEQsQ0FBQyxDQUFDLENBQUMsQ0FBQTtvQkFDbkcsT0FBTTtnQkFDUixDQUFDO2dCQUNELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxpQ0FBaUMsRUFBRSxDQUFDO29CQUN2RCxNQUFNLEtBQUssR0FBRyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLE9BQU8sSUFBSSwwQ0FBMEMsQ0FBQyxDQUFBO29CQUM3RixJQUFJLE9BQU8sT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLEtBQUssUUFBUTt3QkFBRSxLQUFLLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFBO29CQUM1RSxJQUFJLE9BQU8sT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLEtBQUssUUFBUTt3QkFBRSxLQUFLLENBQUMsS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFBO29CQUMvRSxNQUFNLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO29CQUMvQixPQUFNO2dCQUNSLENBQUM7Z0JBQ0QsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLCtCQUErQixJQUFJLE9BQU8sQ0FBQyxZQUFZLEtBQUssSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO29CQUNuRyxNQUFNLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxDQUFDLENBQUMsQ0FBQTtvQkFDeEYsT0FBTTtnQkFDUixDQUFDO2dCQUNELE1BQU0sQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFBO1lBQ25ELENBQUMsQ0FBQyxDQUFBO1lBQ0YsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUE7WUFDakMsSUFBSSxNQUFNLENBQUMsT0FBTztnQkFBRSxPQUFPLEVBQUUsQ0FBQTtRQUMvQixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgeyByYW5kb21VVUlEIH0gZnJvbSBcIm5vZGU6Y3J5cHRvXCJcbmltcG9ydCBuZXQgZnJvbSBcIm5vZGU6bmV0XCJcbmltcG9ydCB0aW1lb3V0IGZyb20gXCJhd2FpdGVyeS9idWlsZC90aW1lb3V0LmpzXCJcbmltcG9ydCBKc29uU29ja2V0IGZyb20gXCIuL2pzb24tc29ja2V0LmpzXCJcblxuY29uc3QgREVGQVVMVF9SRVFVRVNUX1RJTUVPVVRfTVMgPSAxMDAwMFxuZXhwb3J0IGNvbnN0IE1BWF9MSUZFQ1lDTEVfUkVRVUVTVF9USU1FT1VUX01TID0gMTIwMDAwXG5jb25zdCBMSUZFQ1lDTEVfQ09OTkVDVF9SRVRSWV9ERUxBWV9NUyA9IDUwXG5jb25zdCBUUkFOU0lFTlRfQ09OTkVDVF9FUlJPUl9DT0RFUyA9IG5ldyBTZXQoW1wiRUNPTk5BQk9SVEVEXCIsIFwiRUNPTk5SRUZVU0VEXCIsIFwiRU5PRU5UXCJdKVxuXG4vKipcbiAqIFdoZXRoZXIgYSBsaWZlY3ljbGUgcmVxdWVzdCBmYWlsdXJlIGlzIGEgdHJhbnNpZW50IFwiZW5kcG9pbnQgbm90IHJlYWR5IHlldFwiXG4gKiBjb25uZWN0IGZhaWx1cmUgKHRoZSBjb29yZGluYXRvcidzIGNvbnRyb2wgc29ja2V0IG9wZW5zIGFzeW5jaHJvbm91c2x5IGFmdGVyXG4gKiBpdHMgcHJvY2VzcyBzdGFydHMpIHJhdGhlciB0aGFuIGEgZGVmaW5pdGl2ZSBwcm90b2NvbCBvciBzdGF0ZSByZWplY3Rpb24uXG4gKiBAcGFyYW0ge3Vua25vd259IGVycm9yIC0gVGhlIHJlamVjdGlvbiByZWFzb24uXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBXaGV0aGVyIHRoZSByZXF1ZXN0IG1heSBiZSByZXRyaWVkLlxuICovXG5mdW5jdGlvbiBpc1RyYW5zaWVudENvbm5lY3RFcnJvcihlcnJvcikge1xuICBpZiAodHlwZW9mIGVycm9yICE9PSBcIm9iamVjdFwiIHx8IGVycm9yID09PSBudWxsKSByZXR1cm4gZmFsc2VcbiAgY29uc3QgY29kZSA9IC8qKiBAdHlwZSB7e2NvZGU/OiB1bmtub3dufX0gKi8gKGVycm9yKS5jb2RlXG5cbiAgcmV0dXJuIHR5cGVvZiBjb2RlID09PSBcInN0cmluZ1wiICYmIFRSQU5TSUVOVF9DT05ORUNUX0VSUk9SX0NPREVTLmhhcyhjb2RlKVxufVxuXG4vKipcbiAqIFdhaXRzIGJlZm9yZSB0aGUgbmV4dCBsaWZlY3ljbGUgY29ubmVjdCByZXRyeSwgcmVqZWN0aW5nIHdpdGggdGhlIHNpZ25hbFxuICogcmVhc29uIHdoZW4gdGhlIGNhbGxlci1vd25lZCBkZWFkbGluZSBmaXJlcyBtaWQtd2FpdC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBkZWxheU1zIC0gV2FpdCBiZXR3ZWVuIGF0dGVtcHRzLlxuICogQHBhcmFtIHtBYm9ydFNpZ25hbH0gc2lnbmFsIC0gQ2FsbGVyLW93bmVkIGRlYWRsaW5lIHNpZ25hbC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIHRoZSBkZWxheSwgb3IgcmVqZWN0cyBvbiBhYm9ydC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gd2FpdEJlZm9yZU5leHRBdHRlbXB0KGRlbGF5TXMsIHNpZ25hbCkge1xuICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgaWYgKHNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICByZWplY3Qoc2lnbmFsLnJlYXNvbilcblxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBzaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIG9uQWJvcnQpXG4gICAgICByZXNvbHZlKHVuZGVmaW5lZClcbiAgICB9LCBkZWxheU1zKVxuICAgIGNvbnN0IG9uQWJvcnQgPSAoKSA9PiB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpXG4gICAgICByZWplY3Qoc2lnbmFsLnJlYXNvbilcbiAgICB9XG5cbiAgICBzaWduYWwuYWRkRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIG9uQWJvcnQpXG4gIH0pXG59XG5cbi8qKiBPbmUtcmVxdWVzdCBhY2tub3dsZWRnZWQgbGlmZWN5Y2xlIGNsaWVudC4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEJhY2tncm91bmRKb2JzTGlmZWN5Y2xlQ2xpZW50IHtcbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBsaWZlY3ljbGUgY2xpZW50LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIENsaWVudCBvcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gYXJncy5jb25maWd1cmF0aW9uIC0gQ29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IFthcmdzLmdlbmVyYXRpb25JZF0gLSBFeHBsaWNpdCBnZW5lcmF0aW9uIGlkZW50aXR5LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3Muc29ja2V0UGF0aF0gLSBFeHBsaWNpdCBjb250cm9sIHNvY2tldCBwYXRoLlxuICAgKiBAcGFyYW0ge251bWJlcn0gW2FyZ3MucmVxdWVzdFRpbWVvdXRNc10gLSBSZXF1ZXN0IGRlYWRsaW5lIGJlbG93IHRoZSBzdXBlcnZpc29yIGhvb2sgdGltZW91dCAoZGVmYXVsdDogMTAwMDApLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbmZpZ3VyYXRpb24sIGdlbmVyYXRpb25JZCwgc29ja2V0UGF0aCwgcmVxdWVzdFRpbWVvdXRNcyA9IERFRkFVTFRfUkVRVUVTVF9USU1FT1VUX01TfSkge1xuICAgIGNvbnN0IGdlbmVyYXRpb25Db25maWcgPSBjb25maWd1cmF0aW9uLnJlc29sdmVCYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25Db25maWcoe1xuICAgICAgZ2VuZXJhdGlvbklkLFxuICAgICAgbGlmZWN5Y2xlU29ja2V0UGF0aDogc29ja2V0UGF0aCxcbiAgICAgIHNvdXJjZU5hbWU6IFwiQmFja2dyb3VuZEpvYnNMaWZlY3ljbGVDbGllbnRcIlxuICAgIH0pXG4gICAgdGhpcy5nZW5lcmF0aW9uSWQgPSBnZW5lcmF0aW9uQ29uZmlnLmdlbmVyYXRpb25JZFxuICAgIHRoaXMuc29ja2V0UGF0aCA9IGdlbmVyYXRpb25Db25maWcubGlmZWN5Y2xlU29ja2V0UGF0aFxuICAgIGlmICghTnVtYmVyLmlzSW50ZWdlcihyZXF1ZXN0VGltZW91dE1zKSB8fCByZXF1ZXN0VGltZW91dE1zIDwgMSB8fCByZXF1ZXN0VGltZW91dE1zID4gTUFYX0xJRkVDWUNMRV9SRVFVRVNUX1RJTUVPVVRfTVMpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYHJlcXVlc3RUaW1lb3V0TXMgbXVzdCBiZSBhbiBpbnRlZ2VyIGJldHdlZW4gMSBhbmQgJHtNQVhfTElGRUNZQ0xFX1JFUVVFU1RfVElNRU9VVF9NU31gKVxuICAgIH1cbiAgICB0aGlzLnJlcXVlc3RUaW1lb3V0TXMgPSByZXF1ZXN0VGltZW91dE1zXG4gICAgaWYgKCF0aGlzLmdlbmVyYXRpb25JZCkgdGhyb3cgbmV3IEVycm9yKFwiQmFja2dyb3VuZCBqb2JzIGxpZmVjeWNsZSBjbGllbnQgcmVxdWlyZXMgZ2VuZXJhdGlvbklkXCIpXG4gICAgaWYgKCF0aGlzLnNvY2tldFBhdGgpIHRocm93IG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgY2xpZW50IHJlcXVpcmVzIGxpZmVjeWNsZVNvY2tldFBhdGhcIilcbiAgfVxuXG4gIC8qKlxuICAgKiBBY3RpdmF0ZXMgdGhlIGdlbmVyYXRpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIGFjdGl2YXRlKCkgeyByZXR1cm4gYXdhaXQgdGhpcy5fcmVxdWVzdChcImFjdGl2YXRlXCIpIH1cblxuICAvKipcbiAgICogUmV0aXJlcyB0aGUgZ2VuZXJhdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25MaWZlY3ljbGVTdGF0ZT59IC0gUmVzdWx0aW5nIHN0YXRlLlxuICAgKi9cbiAgYXN5bmMgcmV0aXJlKCkgeyByZXR1cm4gYXdhaXQgdGhpcy5fcmVxdWVzdChcInJldGlyZVwiKSB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIGV4YWN0bHkgb25lIGxpZmVjeWNsZSByZXF1ZXN0LlxuICAgKiBAcGFyYW0ge1wiYWN0aXZhdGVcIiB8IFwicmV0aXJlXCJ9IGFjdGlvbiAtIExpZmVjeWNsZSBhY3Rpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIF9yZXF1ZXN0KGFjdGlvbikge1xuICAgIHJldHVybiBhd2FpdCB0aW1lb3V0KHtcbiAgICAgIGVycm9yTWVzc2FnZTogYEJhY2tncm91bmQgam9icyAke2FjdGlvbn0gcmVxdWVzdCBmb3IgJHt0aGlzLmdlbmVyYXRpb25JZH0gdGltZWQgb3V0IGFmdGVyICR7dGhpcy5yZXF1ZXN0VGltZW91dE1zfW1zIGF0ICR7dGhpcy5zb2NrZXRQYXRofWAsXG4gICAgICB0aW1lb3V0OiB0aGlzLnJlcXVlc3RUaW1lb3V0TXNcbiAgICB9LCBhc3luYyAoe2NvbnRyb2x9KSA9PiBhd2FpdCB0aGlzLl9ydW5SZXF1ZXN0KHthY3Rpb24sIHNpZ25hbDogY29udHJvbC5zaWduYWx9KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kcyB0aGUgbGlmZWN5Y2xlIHJlcXVlc3QgdW5kZXIgaXRzIGNhbGxlci1vd25lZCBkZWFkbGluZS4gUmV0cmllc1xuICAgKiB0cmFuc2llbnQgXCJlbmRwb2ludCBub3QgcmVhZHkgeWV0XCIgY29ubmVjdCBmYWlsdXJlcyAodGhlIGNvb3JkaW5hdG9yIG9wZW5zXG4gICAqIGl0cyBjb250cm9sIHNvY2tldCBhc3luY2hyb25vdXNseSBhZnRlciBpdHMgcHJvY2VzcyBzdGFydHMpIHVudGlsIHRoZVxuICAgKiBkZWFkbGluZTsgYW55IGRlZmluaXRpdmUgcHJvdG9jb2wgb3Igc3RhdGUgcmVqZWN0aW9uIGZhaWxzIGltbWVkaWF0ZWx5LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIFJlcXVlc3QgZGV0YWlscy5cbiAgICogQHBhcmFtIHtcImFjdGl2YXRlXCIgfCBcInJldGlyZVwifSBhcmdzLmFjdGlvbiAtIExpZmVjeWNsZSBhY3Rpb24uXG4gICAqIEBwYXJhbSB7QWJvcnRTaWduYWx9IGFyZ3Muc2lnbmFsIC0gUmVxdWVzdCBkZWFkbGluZSBzaWduYWwuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIF9ydW5SZXF1ZXN0KHthY3Rpb24sIHNpZ25hbH0pIHtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuX2F0dGVtcHRSZXF1ZXN0KGFjdGlvbiwgcmFuZG9tVVVJRCgpLCBzaWduYWwpXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoIWlzVHJhbnNpZW50Q29ubmVjdEVycm9yKGVycm9yKSkgdGhyb3cgZXJyb3JcbiAgICAgICAgaWYgKHNpZ25hbC5hYm9ydGVkKSB0aHJvdyBzaWduYWwucmVhc29uXG4gICAgICAgIGF3YWl0IHdhaXRCZWZvcmVOZXh0QXR0ZW1wdChMSUZFQ1lDTEVfQ09OTkVDVF9SRVRSWV9ERUxBWV9NUywgc2lnbmFsKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBQZXJmb3JtcyBvbmUgbGlmZWN5Y2xlIHJlcXVlc3QgYXR0ZW1wdCBhZ2FpbnN0IHRoZSBjb250cm9sIHNvY2tldC5cbiAgICogQHBhcmFtIHtcImFjdGl2YXRlXCIgfCBcInJldGlyZVwifSBhY3Rpb24gLSBMaWZlY3ljbGUgYWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcmVxdWVzdElkIC0gUmVxdWVzdCBpZGVudGl0eSBmb3IgYWNrbm93bGVkZ2VtZW50IG1hdGNoaW5nLlxuICAgKiBAcGFyYW0ge0Fib3J0U2lnbmFsfSBzaWduYWwgLSBSZXF1ZXN0IGRlYWRsaW5lIHNpZ25hbC5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25MaWZlY3ljbGVTdGF0ZT59IC0gUmVzdWx0aW5nIHN0YXRlLlxuICAgKi9cbiAgYXN5bmMgX2F0dGVtcHRSZXF1ZXN0KGFjdGlvbiwgcmVxdWVzdElkLCBzaWduYWwpIHtcbiAgICBjb25zdCBzb2NrZXQgPSBuZXQuY3JlYXRlQ29ubmVjdGlvbih0aGlzLnNvY2tldFBhdGgpXG4gICAgY29uc3QganNvblNvY2tldCA9IG5ldyBKc29uU29ja2V0KHNvY2tldClcblxuICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBsZXQgZmluaXNoZWQgPSBmYWxzZVxuICAgICAgLyoqXG4gICAgICAgKiBTZXR0bGVzIHRoZSByZXF1ZXN0IG9uY2UuXG4gICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIFRlYXJkb3duIG9wdGlvbnMuXG4gICAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmRlc3Ryb3ldIC0gRGVzdHJveSBpbnN0ZWFkIG9mIGNsb3NpbmcuXG4gICAgICAgKiBAcGFyYW0geygpID0+IHZvaWR9IGNhbGxiYWNrIC0gU2V0dGxlbWVudCBjYWxsYmFjay5cbiAgICAgICAqL1xuICAgICAgY29uc3QgZmluaXNoID0gKHtkZXN0cm95ID0gZmFsc2V9LCBjYWxsYmFjaykgPT4ge1xuICAgICAgICBpZiAoZmluaXNoZWQpIHJldHVyblxuICAgICAgICBmaW5pc2hlZCA9IHRydWVcbiAgICAgICAgc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KVxuICAgICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoXCJjb25uZWN0XCIsIG9uQ29ubmVjdClcbiAgICAgICAganNvblNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoKVxuICAgICAgICBpZiAoZGVzdHJveSkganNvblNvY2tldC5kZXN0cm95KClcbiAgICAgICAgZWxzZSBqc29uU29ja2V0LmNsb3NlKClcbiAgICAgICAgY2FsbGJhY2soKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBvbkFib3J0ID0gKCkgPT4gZmluaXNoKHtkZXN0cm95OiB0cnVlfSwgKCkgPT4gcmVqZWN0KHNpZ25hbC5yZWFzb24gaW5zdGFuY2VvZiBFcnJvciA/IHNpZ25hbC5yZWFzb24gOiBuZXcgRXJyb3IoXCJCYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIHJlcXVlc3QgYWJvcnRlZFwiKSkpXG4gICAgICBjb25zdCBvbkNvbm5lY3QgPSAoKSA9PiB7XG4gICAgICAgIGpzb25Tb2NrZXQuc2VuZCh7XG4gICAgICAgICAgdHlwZTogXCJiYWNrZ3JvdW5kLWpvYnMtbGlmZWN5Y2xlXCIsXG4gICAgICAgICAgYWN0aW9uLFxuICAgICAgICAgIGdlbmVyYXRpb25JZDogdGhpcy5nZW5lcmF0aW9uSWQsXG4gICAgICAgICAgcmVxdWVzdElkXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgb25BYm9ydClcbiAgICAgIGpzb25Tb2NrZXQub24oXCJlcnJvclwiLCAoZXJyb3IpID0+IGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KGVycm9yKSkpXG4gICAgICBqc29uU29ja2V0Lm9uKFwiY2xvc2VcIiwgKCkgPT4gZmluaXNoKHtkZXN0cm95OiB0cnVlfSwgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgc29ja2V0IGNsb3NlZCBiZWZvcmUgYWNrbm93bGVkZ2VtZW50XCIpKSkpXG4gICAgICBqc29uU29ja2V0Lm9uKFwibWVzc2FnZVwiLCAobWVzc2FnZSkgPT4ge1xuICAgICAgICBpZiAobWVzc2FnZT8ucmVxdWVzdElkICE9PSByZXF1ZXN0SWQgfHwgbWVzc2FnZS5hY3Rpb24gIT09IGFjdGlvbikge1xuICAgICAgICAgIGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgcmVzcG9uc2UgZGlkIG5vdCBtYXRjaCBpdHMgcmVxdWVzdFwiKSkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1lc3NhZ2UudHlwZSA9PT0gXCJiYWNrZ3JvdW5kLWpvYnMtbGlmZWN5Y2xlLWVycm9yXCIpIHtcbiAgICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlLmVycm9yPy5tZXNzYWdlIHx8IFwiQmFja2dyb3VuZCBqb2JzIGxpZmVjeWNsZSByZXF1ZXN0IGZhaWxlZFwiKVxuICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZS5lcnJvcj8ubmFtZSA9PT0gXCJzdHJpbmdcIikgZXJyb3IubmFtZSA9IG1lc3NhZ2UuZXJyb3IubmFtZVxuICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZS5lcnJvcj8uc3RhY2sgPT09IFwic3RyaW5nXCIpIGVycm9yLnN0YWNrID0gbWVzc2FnZS5lcnJvci5zdGFja1xuICAgICAgICAgIGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KGVycm9yKSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAobWVzc2FnZS50eXBlICE9PSBcImJhY2tncm91bmQtam9icy1saWZlY3ljbGUtYWNrXCIgfHwgbWVzc2FnZS5nZW5lcmF0aW9uSWQgIT09IHRoaXMuZ2VuZXJhdGlvbklkKSB7XG4gICAgICAgICAgZmluaXNoKHt9LCAoKSA9PiByZWplY3QobmV3IEVycm9yKFwiSW52YWxpZCBiYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIGFja25vd2xlZGdlbWVudFwiKSkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgZmluaXNoKHt9LCAoKSA9PiByZXNvbHZlKG1lc3NhZ2UubGlmZWN5Y2xlU3RhdGUpKVxuICAgICAgfSlcbiAgICAgIHNvY2tldC5vbmNlKFwiY29ubmVjdFwiLCBvbkNvbm5lY3QpXG4gICAgICBpZiAoc2lnbmFsLmFib3J0ZWQpIG9uQWJvcnQoKVxuICAgIH0pXG4gIH1cbn1cbiJdfQ==