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 +1 -1
- package/build/background-jobs/lifecycle-client.js +66 -2
- package/build/background-jobs/pooled-runner-child.js +29 -1
- package/build/background-jobs/types.js +1 -0
- package/build/background-jobs/worker.js +8 -2
- package/build/configuration-types.js +1 -1
- package/build/src/background-jobs/lifecycle-client.d.ts +12 -1
- package/build/src/background-jobs/lifecycle-client.d.ts.map +1 -1
- package/build/src/background-jobs/lifecycle-client.js +65 -3
- package/build/src/background-jobs/pooled-runner-child.js +31 -2
- package/build/src/background-jobs/types.d.ts +5 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +2 -1
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +9 -3
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.js +2 -2
- package/package.json +4 -4
- package/src/background-jobs/lifecycle-client.js +66 -2
- package/src/background-jobs/pooled-runner-child.js +29 -1
- package/src/background-jobs/types.js +1 -0
- package/src/background-jobs/worker.js +8 -2
- package/src/configuration-types.js +1 -1
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
|
-
|
|
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 ||
|
|
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
|
|
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;
|
|
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
|
-
|
|
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlmZWN5Y2xlLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9iYWNrZ3JvdW5kLWpvYnMvbGlmZWN5Y2xlLWNsaWVudC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUN4QyxPQUFPLEdBQUcsTUFBTSxVQUFVLENBQUE7QUFDMUIsT0FBTyxPQUFPLE1BQU0sMkJBQTJCLENBQUE7QUFDL0MsT0FBTyxVQUFVLE1BQU0sa0JBQWtCLENBQUE7QUFFekMsTUFBTSwwQkFBMEIsR0FBRyxLQUFLLENBQUE7QUFDeEMsTUFBTSxDQUFDLE1BQU0sZ0NBQWdDLEdBQUcsTUFBTSxDQUFBO0FBQ3RELE1BQU0sZ0NBQWdDLEdBQUcsRUFBRSxDQUFBO0FBQzNDLE1BQU0sNkJBQTZCLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxjQUFjLEVBQUUsY0FBYyxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUE7QUFFekY7Ozs7OztHQU1HO0FBQ0gsU0FBUyx1QkFBdUIsQ0FBQyxLQUFLO0lBQ3BDLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJO1FBQUUsT0FBTyxLQUFLLENBQUE7SUFDN0QsTUFBTSxJQUFJLEdBQUcsK0JBQStCLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUE7SUFFekQsT0FBTyxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksNkJBQTZCLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFBO0FBQzVFLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxLQUFLLFVBQVUscUJBQXFCLENBQUMsT0FBTyxFQUFFLE1BQU07SUFDbEQsTUFBTSxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNwQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNuQixN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QUFTLEVBQUUsU0FBUyxDQUFDLENBQUE7WUFDakMsSUFBSSxNQUFNLENBQUMsT0FBTztnQkFBRSxPQUFPLEVBQUUsQ0FBQTtRQUMvQixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgeyByYW5kb21VVUlEIH0gZnJvbSBcIm5vZGU6Y3J5cHRvXCJcbmltcG9ydCBuZXQgZnJvbSBcIm5vZGU6bmV0XCJcbmltcG9ydCB0aW1lb3V0IGZyb20gXCJhd2FpdGVyeS9idWlsZC90aW1lb3V0LmpzXCJcbmltcG9ydCBKc29uU29ja2V0IGZyb20gXCIuL2pzb24tc29ja2V0LmpzXCJcblxuY29uc3QgREVGQVVMVF9SRVFVRVNUX1RJTUVPVVRfTVMgPSAxMDAwMFxuZXhwb3J0IGNvbnN0IE1BWF9MSUZFQ1lDTEVfUkVRVUVTVF9USU1FT1VUX01TID0gMTIwMDAwXG5jb25zdCBMSUZFQ1lDTEVfQ09OTkVDVF9SRVRSWV9ERUxBWV9NUyA9IDUwXG5jb25zdCBUUkFOU0lFTlRfQ09OTkVDVF9FUlJPUl9DT0RFUyA9IG5ldyBTZXQoW1wiRUNPTk5BQk9SVEVEXCIsIFwiRUNPTk5SRUZVU0VEXCIsIFwiRU5PRU5UXCJdKVxuXG4vKipcbiAqIFdoZXRoZXIgYSBsaWZlY3ljbGUgcmVxdWVzdCBmYWlsdXJlIGlzIGEgdHJhbnNpZW50IFwiZW5kcG9pbnQgbm90IHJlYWR5IHlldFwiXG4gKiBjb25uZWN0IGZhaWx1cmUgKHRoZSBjb29yZGluYXRvcidzIGNvbnRyb2wgc29ja2V0IG9wZW5zIGFzeW5jaHJvbm91c2x5IGFmdGVyXG4gKiBpdHMgcHJvY2VzcyBzdGFydHMpIHJhdGhlciB0aGFuIGEgZGVmaW5pdGl2ZSBwcm90b2NvbCBvciBzdGF0ZSByZWplY3Rpb24uXG4gKiBAcGFyYW0ge3Vua25vd259IGVycm9yIC0gVGhlIHJlamVjdGlvbiByZWFzb24uXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBXaGV0aGVyIHRoZSByZXF1ZXN0IG1heSBiZSByZXRyaWVkLlxuICovXG5mdW5jdGlvbiBpc1RyYW5zaWVudENvbm5lY3RFcnJvcihlcnJvcikge1xuICBpZiAodHlwZW9mIGVycm9yICE9PSBcIm9iamVjdFwiIHx8IGVycm9yID09PSBudWxsKSByZXR1cm4gZmFsc2VcbiAgY29uc3QgY29kZSA9IC8qKiBAdHlwZSB7e2NvZGU/OiB1bmtub3dufX0gKi8gKGVycm9yKS5jb2RlXG5cbiAgcmV0dXJuIHR5cGVvZiBjb2RlID09PSBcInN0cmluZ1wiICYmIFRSQU5TSUVOVF9DT05ORUNUX0VSUk9SX0NPREVTLmhhcyhjb2RlKVxufVxuXG4vKipcbiAqIFdhaXRzIGJlZm9yZSB0aGUgbmV4dCBsaWZlY3ljbGUgY29ubmVjdCByZXRyeSwgcmVqZWN0aW5nIHdpdGggdGhlIHNpZ25hbFxuICogcmVhc29uIHdoZW4gdGhlIGNhbGxlci1vd25lZCBkZWFkbGluZSBmaXJlcyBtaWQtd2FpdC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBkZWxheU1zIC0gV2FpdCBiZXR3ZWVuIGF0dGVtcHRzLlxuICogQHBhcmFtIHtBYm9ydFNpZ25hbH0gc2lnbmFsIC0gQ2FsbGVyLW93bmVkIGRlYWRsaW5lIHNpZ25hbC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIHRoZSBkZWxheSwgb3IgcmVqZWN0cyBvbiBhYm9ydC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gd2FpdEJlZm9yZU5leHRBdHRlbXB0KGRlbGF5TXMsIHNpZ25hbCkge1xuICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgaWYgKHNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICByZWplY3Qoc2lnbmFsLnJlYXNvbilcblxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBzaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIG9uQWJvcnQpXG4gICAgICByZXNvbHZlKHVuZGVmaW5lZClcbiAgICB9LCBkZWxheU1zKVxuICAgIGNvbnN0IG9uQWJvcnQgPSAoKSA9PiB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpXG4gICAgICByZWplY3Qoc2lnbmFsLnJlYXNvbilcbiAgICB9XG5cbiAgICBzaWduYWwuYWRkRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIG9uQWJvcnQpXG4gIH0pXG59XG5cbi8qKiBPbmUtcmVxdWVzdCBhY2tub3dsZWRnZWQgbGlmZWN5Y2xlIGNsaWVudC4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEJhY2tncm91bmRKb2JzTGlmZWN5Y2xlQ2xpZW50IHtcbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBsaWZlY3ljbGUgY2xpZW50LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIENsaWVudCBvcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gYXJncy5jb25maWd1cmF0aW9uIC0gQ29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IFthcmdzLmdlbmVyYXRpb25JZF0gLSBFeHBsaWNpdCBnZW5lcmF0aW9uIGlkZW50aXR5LlxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3Muc29ja2V0UGF0aF0gLSBFeHBsaWNpdCBjb250cm9sIHNvY2tldCBwYXRoLlxuICAgKiBAcGFyYW0ge251bWJlcn0gW2FyZ3MucmVxdWVzdFRpbWVvdXRNc10gLSBSZXF1ZXN0IGRlYWRsaW5lIGJlbG93IHRoZSBzdXBlcnZpc29yIGhvb2sgdGltZW91dCAoZGVmYXVsdDogMTAwMDApLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbmZpZ3VyYXRpb24sIGdlbmVyYXRpb25JZCwgc29ja2V0UGF0aCwgcmVxdWVzdFRpbWVvdXRNcyA9IERFRkFVTFRfUkVRVUVTVF9USU1FT1VUX01TfSkge1xuICAgIGNvbnN0IGdlbmVyYXRpb25Db25maWcgPSBjb25maWd1cmF0aW9uLnJlc29sdmVCYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25Db25maWcoe1xuICAgICAgZ2VuZXJhdGlvbklkLFxuICAgICAgbGlmZWN5Y2xlU29ja2V0UGF0aDogc29ja2V0UGF0aCxcbiAgICAgIHNvdXJjZU5hbWU6IFwiQmFja2dyb3VuZEpvYnNMaWZlY3ljbGVDbGllbnRcIlxuICAgIH0pXG4gICAgdGhpcy5nZW5lcmF0aW9uSWQgPSBnZW5lcmF0aW9uQ29uZmlnLmdlbmVyYXRpb25JZFxuICAgIHRoaXMuc29ja2V0UGF0aCA9IGdlbmVyYXRpb25Db25maWcubGlmZWN5Y2xlU29ja2V0UGF0aFxuICAgIGlmICghTnVtYmVyLmlzSW50ZWdlcihyZXF1ZXN0VGltZW91dE1zKSB8fCByZXF1ZXN0VGltZW91dE1zIDwgMSB8fCByZXF1ZXN0VGltZW91dE1zID4gTUFYX0xJRkVDWUNMRV9SRVFVRVNUX1RJTUVPVVRfTVMpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYHJlcXVlc3RUaW1lb3V0TXMgbXVzdCBiZSBhbiBpbnRlZ2VyIGJldHdlZW4gMSBhbmQgJHtNQVhfTElGRUNZQ0xFX1JFUVVFU1RfVElNRU9VVF9NU31gKVxuICAgIH1cbiAgICB0aGlzLnJlcXVlc3RUaW1lb3V0TXMgPSByZXF1ZXN0VGltZW91dE1zXG4gICAgaWYgKCF0aGlzLmdlbmVyYXRpb25JZCkgdGhyb3cgbmV3IEVycm9yKFwiQmFja2dyb3VuZCBqb2JzIGxpZmVjeWNsZSBjbGllbnQgcmVxdWlyZXMgZ2VuZXJhdGlvbklkXCIpXG4gICAgaWYgKCF0aGlzLnNvY2tldFBhdGgpIHRocm93IG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgY2xpZW50IHJlcXVpcmVzIGxpZmVjeWNsZVNvY2tldFBhdGhcIilcbiAgfVxuXG4gIC8qKlxuICAgKiBBY3RpdmF0ZXMgdGhlIGdlbmVyYXRpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIGFjdGl2YXRlKCkgeyByZXR1cm4gYXdhaXQgdGhpcy5fcmVxdWVzdChcImFjdGl2YXRlXCIpIH1cblxuICAvKipcbiAgICogUmV0aXJlcyB0aGUgZ2VuZXJhdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25MaWZlY3ljbGVTdGF0ZT59IC0gUmVzdWx0aW5nIHN0YXRlLlxuICAgKi9cbiAgYXN5bmMgcmV0aXJlKCkgeyByZXR1cm4gYXdhaXQgdGhpcy5fcmVxdWVzdChcInJldGlyZVwiKSB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIGV4YWN0bHkgb25lIGxpZmVjeWNsZSByZXF1ZXN0LlxuICAgKiBAcGFyYW0ge1wiYWN0aXZhdGVcIiB8IFwicmV0aXJlXCJ9IGFjdGlvbiAtIExpZmVjeWNsZSBhY3Rpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIF9yZXF1ZXN0KGFjdGlvbikge1xuICAgIHJldHVybiBhd2FpdCB0aW1lb3V0KHtcbiAgICAgIGVycm9yTWVzc2FnZTogYEJhY2tncm91bmQgam9icyAke2FjdGlvbn0gcmVxdWVzdCBmb3IgJHt0aGlzLmdlbmVyYXRpb25JZH0gdGltZWQgb3V0IGFmdGVyICR7dGhpcy5yZXF1ZXN0VGltZW91dE1zfW1zIGF0ICR7dGhpcy5zb2NrZXRQYXRofWAsXG4gICAgICB0aW1lb3V0OiB0aGlzLnJlcXVlc3RUaW1lb3V0TXNcbiAgICB9LCBhc3luYyAoe2NvbnRyb2x9KSA9PiBhd2FpdCB0aGlzLl9ydW5SZXF1ZXN0KHthY3Rpb24sIHNpZ25hbDogY29udHJvbC5zaWduYWx9KSlcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kcyB0aGUgbGlmZWN5Y2xlIHJlcXVlc3QgdW5kZXIgaXRzIGNhbGxlci1vd25lZCBkZWFkbGluZS4gUmV0cmllc1xuICAgKiB0cmFuc2llbnQgXCJlbmRwb2ludCBub3QgcmVhZHkgeWV0XCIgY29ubmVjdCBmYWlsdXJlcyAodGhlIGNvb3JkaW5hdG9yIG9wZW5zXG4gICAqIGl0cyBjb250cm9sIHNvY2tldCBhc3luY2hyb25vdXNseSBhZnRlciBpdHMgcHJvY2VzcyBzdGFydHMpIHVudGlsIHRoZVxuICAgKiBkZWFkbGluZTsgYW55IGRlZmluaXRpdmUgcHJvdG9jb2wgb3Igc3RhdGUgcmVqZWN0aW9uIGZhaWxzIGltbWVkaWF0ZWx5LlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIFJlcXVlc3QgZGV0YWlscy5cbiAgICogQHBhcmFtIHtcImFjdGl2YXRlXCIgfCBcInJldGlyZVwifSBhcmdzLmFjdGlvbiAtIExpZmVjeWNsZSBhY3Rpb24uXG4gICAqIEBwYXJhbSB7QWJvcnRTaWduYWx9IGFyZ3Muc2lnbmFsIC0gUmVxdWVzdCBkZWFkbGluZSBzaWduYWwuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYnNHZW5lcmF0aW9uTGlmZWN5Y2xlU3RhdGU+fSAtIFJlc3VsdGluZyBzdGF0ZS5cbiAgICovXG4gIGFzeW5jIF9ydW5SZXF1ZXN0KHthY3Rpb24sIHNpZ25hbH0pIHtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuX2F0dGVtcHRSZXF1ZXN0KGFjdGlvbiwgcmFuZG9tVVVJRCgpLCBzaWduYWwpXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoIWlzVHJhbnNpZW50Q29ubmVjdEVycm9yKGVycm9yKSkgdGhyb3cgZXJyb3JcbiAgICAgICAgaWYgKHNpZ25hbC5hYm9ydGVkKSB0aHJvdyBzaWduYWwucmVhc29uXG4gICAgICAgIGF3YWl0IHdhaXRCZWZvcmVOZXh0QXR0ZW1wdChMSUZFQ1lDTEVfQ09OTkVDVF9SRVRSWV9ERUxBWV9NUywgc2lnbmFsKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBQZXJmb3JtcyBvbmUgbGlmZWN5Y2xlIHJlcXVlc3QgYXR0ZW1wdCBhZ2FpbnN0IHRoZSBjb250cm9sIHNvY2tldC5cbiAgICogQHBhcmFtIHtcImFjdGl2YXRlXCIgfCBcInJldGlyZVwifSBhY3Rpb24gLSBMaWZlY3ljbGUgYWN0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gcmVxdWVzdElkIC0gUmVxdWVzdCBpZGVudGl0eSBmb3IgYWNrbm93bGVkZ2VtZW50IG1hdGNoaW5nLlxuICAgKiBAcGFyYW0ge0Fib3J0U2lnbmFsfSBzaWduYWwgLSBSZXF1ZXN0IGRlYWRsaW5lIHNpZ25hbC5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9ic0dlbmVyYXRpb25MaWZlY3ljbGVTdGF0ZT59IC0gUmVzdWx0aW5nIHN0YXRlLlxuICAgKi9cbiAgYXN5bmMgX2F0dGVtcHRSZXF1ZXN0KGFjdGlvbiwgcmVxdWVzdElkLCBzaWduYWwpIHtcbiAgICBjb25zdCBzb2NrZXQgPSBuZXQuY3JlYXRlQ29ubmVjdGlvbih0aGlzLnNvY2tldFBhdGgpXG4gICAgY29uc3QganNvblNvY2tldCA9IG5ldyBKc29uU29ja2V0KHNvY2tldClcblxuICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBsZXQgZmluaXNoZWQgPSBmYWxzZVxuICAgICAgLyoqXG4gICAgICAgKiBTZXR0bGVzIHRoZSByZXF1ZXN0IG9uY2UuXG4gICAgICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIFRlYXJkb3duIG9wdGlvbnMuXG4gICAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmRlc3Ryb3ldIC0gRGVzdHJveSBpbnN0ZWFkIG9mIGNsb3NpbmcuXG4gICAgICAgKiBAcGFyYW0geygpID0+IHZvaWR9IGNhbGxiYWNrIC0gU2V0dGxlbWVudCBjYWxsYmFjay5cbiAgICAgICAqL1xuICAgICAgY29uc3QgZmluaXNoID0gKHtkZXN0cm95ID0gZmFsc2V9LCBjYWxsYmFjaykgPT4ge1xuICAgICAgICBpZiAoZmluaXNoZWQpIHJldHVyblxuICAgICAgICBmaW5pc2hlZCA9IHRydWVcbiAgICAgICAgc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KVxuICAgICAgICBzb2NrZXQucmVtb3ZlTGlzdGVuZXIoXCJjb25uZWN0XCIsIG9uQ29ubmVjdClcbiAgICAgICAganNvblNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoKVxuICAgICAgICBpZiAoZGVzdHJveSkganNvblNvY2tldC5kZXN0cm95KClcbiAgICAgICAgZWxzZSBqc29uU29ja2V0LmNsb3NlKClcbiAgICAgICAgY2FsbGJhY2soKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBvbkFib3J0ID0gKCkgPT4gZmluaXNoKHtkZXN0cm95OiB0cnVlfSwgKCkgPT4gcmVqZWN0KHNpZ25hbC5yZWFzb24gaW5zdGFuY2VvZiBFcnJvciA/IHNpZ25hbC5yZWFzb24gOiBuZXcgRXJyb3IoXCJCYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIHJlcXVlc3QgYWJvcnRlZFwiKSkpXG4gICAgICBjb25zdCBvbkNvbm5lY3QgPSAoKSA9PiB7XG4gICAgICAgIGpzb25Tb2NrZXQuc2VuZCh7XG4gICAgICAgICAgdHlwZTogXCJiYWNrZ3JvdW5kLWpvYnMtbGlmZWN5Y2xlXCIsXG4gICAgICAgICAgYWN0aW9uLFxuICAgICAgICAgIGdlbmVyYXRpb25JZDogdGhpcy5nZW5lcmF0aW9uSWQsXG4gICAgICAgICAgcmVxdWVzdElkXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgb25BYm9ydClcbiAgICAgIGpzb25Tb2NrZXQub24oXCJlcnJvclwiLCAoZXJyb3IpID0+IGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KGVycm9yKSkpXG4gICAgICBqc29uU29ja2V0Lm9uKFwiY2xvc2VcIiwgKCkgPT4gZmluaXNoKHtkZXN0cm95OiB0cnVlfSwgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgc29ja2V0IGNsb3NlZCBiZWZvcmUgYWNrbm93bGVkZ2VtZW50XCIpKSkpXG4gICAgICBqc29uU29ja2V0Lm9uKFwibWVzc2FnZVwiLCAobWVzc2FnZSkgPT4ge1xuICAgICAgICBpZiAobWVzc2FnZT8ucmVxdWVzdElkICE9PSByZXF1ZXN0SWQgfHwgbWVzc2FnZS5hY3Rpb24gIT09IGFjdGlvbikge1xuICAgICAgICAgIGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KG5ldyBFcnJvcihcIkJhY2tncm91bmQgam9icyBsaWZlY3ljbGUgcmVzcG9uc2UgZGlkIG5vdCBtYXRjaCBpdHMgcmVxdWVzdFwiKSkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1lc3NhZ2UudHlwZSA9PT0gXCJiYWNrZ3JvdW5kLWpvYnMtbGlmZWN5Y2xlLWVycm9yXCIpIHtcbiAgICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlLmVycm9yPy5tZXNzYWdlIHx8IFwiQmFja2dyb3VuZCBqb2JzIGxpZmVjeWNsZSByZXF1ZXN0IGZhaWxlZFwiKVxuICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZS5lcnJvcj8ubmFtZSA9PT0gXCJzdHJpbmdcIikgZXJyb3IubmFtZSA9IG1lc3NhZ2UuZXJyb3IubmFtZVxuICAgICAgICAgIGlmICh0eXBlb2YgbWVzc2FnZS5lcnJvcj8uc3RhY2sgPT09IFwic3RyaW5nXCIpIGVycm9yLnN0YWNrID0gbWVzc2FnZS5lcnJvci5zdGFja1xuICAgICAgICAgIGZpbmlzaCh7fSwgKCkgPT4gcmVqZWN0KGVycm9yKSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAobWVzc2FnZS50eXBlICE9PSBcImJhY2tncm91bmQtam9icy1saWZlY3ljbGUtYWNrXCIgfHwgbWVzc2FnZS5nZW5lcmF0aW9uSWQgIT09IHRoaXMuZ2VuZXJhdGlvbklkKSB7XG4gICAgICAgICAgZmluaXNoKHt9LCAoKSA9PiByZWplY3QobmV3IEVycm9yKFwiSW52YWxpZCBiYWNrZ3JvdW5kIGpvYnMgbGlmZWN5Y2xlIGFja25vd2xlZGdlbWVudFwiKSkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgZmluaXNoKHt9LCAoKSA9PiByZXNvbHZlKG1lc3NhZ2UubGlmZWN5Y2xlU3RhdGUpKVxuICAgICAgfSlcbiAgICAgIHNvY2tldC5vbmNlKFwiY29ubmVjdFwiLCBvbkNvbm5lY3QpXG4gICAgICBpZiAoc2lnbmFsLmFib3J0ZWQpIG9uQWJvcnQoKVxuICAgIH0pXG4gIH1cbn1cbiJdfQ==
|