velocious 1.0.606 → 1.0.607
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 +44 -1
- package/build/background-jobs/job-semantics.js +145 -0
- package/build/background-jobs/local-adapter.js +161 -0
- package/build/background-jobs/local-dispatcher.js +418 -0
- package/build/background-jobs/local-job-registry.js +59 -0
- package/build/background-jobs/local-store.js +1219 -0
- package/build/background-jobs/store.js +33 -97
- package/build/background-jobs/types.js +26 -1
- package/build/configuration-types.js +3 -1
- package/build/configuration.js +16 -1
- package/build/database/drivers/sqlite/index.js +46 -41
- package/build/environment-handlers/browser.js +10 -0
- package/build/src/background-jobs/job-semantics.d.ts +65 -0
- package/build/src/background-jobs/job-semantics.d.ts.map +1 -0
- package/build/src/background-jobs/job-semantics.js +125 -0
- package/build/src/background-jobs/local-adapter.d.ts +161 -0
- package/build/src/background-jobs/local-adapter.d.ts.map +1 -0
- package/build/src/background-jobs/local-adapter.js +141 -0
- package/build/src/background-jobs/local-dispatcher.d.ts +185 -0
- package/build/src/background-jobs/local-dispatcher.d.ts.map +1 -0
- package/build/src/background-jobs/local-dispatcher.js +383 -0
- package/build/src/background-jobs/local-job-registry.d.ts +26 -0
- package/build/src/background-jobs/local-job-registry.d.ts.map +1 -0
- package/build/src/background-jobs/local-job-registry.js +51 -0
- package/build/src/background-jobs/local-store.d.ts +364 -0
- package/build/src/background-jobs/local-store.d.ts.map +1 -0
- package/build/src/background-jobs/local-store.js +1078 -0
- package/build/src/background-jobs/store.d.ts +0 -9
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +18 -88
- package/build/src/background-jobs/types.d.ts +97 -2
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +27 -2
- package/build/src/configuration-types.d.ts +9 -2
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +4 -2
- package/build/src/configuration.d.ts +5 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +15 -2
- package/build/src/database/drivers/sqlite/index.d.ts +8 -0
- package/build/src/database/drivers/sqlite/index.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.js +47 -41
- package/build/src/environment-handlers/browser.d.ts +9 -0
- package/build/src/environment-handlers/browser.d.ts.map +1 -1
- package/build/src/environment-handlers/browser.js +10 -1
- package/build/src/testing/test-runner.d.ts +42 -10
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +135 -35
- package/build/testing/test-runner.js +141 -33
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/test-browser.js +5 -1
- package/src/background-jobs/job-semantics.js +145 -0
- package/src/background-jobs/local-adapter.js +161 -0
- package/src/background-jobs/local-dispatcher.js +418 -0
- package/src/background-jobs/local-job-registry.js +59 -0
- package/src/background-jobs/local-store.js +1219 -0
- package/src/background-jobs/store.js +33 -97
- package/src/background-jobs/types.js +26 -1
- package/src/configuration-types.js +3 -1
- package/src/configuration.js +16 -1
- package/src/database/drivers/sqlite/index.js +46 -41
- package/src/environment-handlers/browser.js +10 -0
- package/src/testing/test-runner.js +141 -33
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
export type LocalBackgroundJobAcknowledgement = {
|
|
2
|
+
type: "completed";
|
|
3
|
+
} | {
|
|
4
|
+
type: "failed";
|
|
5
|
+
error: ReturnType<typeof JSON.parse>;
|
|
6
|
+
} | {
|
|
7
|
+
type: "rescheduled";
|
|
8
|
+
delayMs: number;
|
|
9
|
+
};
|
|
10
|
+
export type PendingLocalBackgroundJobAcknowledgement = {
|
|
11
|
+
/**
|
|
12
|
+
* - Durable transition still owned by this dispatcher.
|
|
13
|
+
*/
|
|
14
|
+
acknowledgement: LocalBackgroundJobAcknowledgement;
|
|
15
|
+
/**
|
|
16
|
+
* - Fenced handoff being acknowledged.
|
|
17
|
+
*/
|
|
18
|
+
handoff: import("./types.js").BackgroundJobHandoff;
|
|
19
|
+
/**
|
|
20
|
+
* - Claimed job snapshot.
|
|
21
|
+
*/
|
|
22
|
+
job: import("./types.js").BackgroundJobRow;
|
|
23
|
+
};
|
|
24
|
+
/** Configuration-owned, event-driven in-process local dispatcher. */
|
|
25
|
+
export default class LocalBackgroundJobsDispatcher {
|
|
26
|
+
clock: import("./types.js").LocalBackgroundJobsClock;
|
|
27
|
+
configuration: import("../configuration.js").default;
|
|
28
|
+
registry: import("./local-job-registry.js").default;
|
|
29
|
+
store: import("./local-store.js").default;
|
|
30
|
+
_accepting: boolean;
|
|
31
|
+
_started: boolean;
|
|
32
|
+
/** @type {Promise<void> | null} */
|
|
33
|
+
_startPromise: Promise<void> | null;
|
|
34
|
+
/** @type {Promise<void> | null} */
|
|
35
|
+
_drainPromise: Promise<void> | null;
|
|
36
|
+
_redrain: boolean;
|
|
37
|
+
_wakeQueued: boolean;
|
|
38
|
+
/** @type {Set<Promise<void>>} */
|
|
39
|
+
_inFlight: Set<Promise<void>>;
|
|
40
|
+
/** @type {Map<string, PendingLocalBackgroundJobAcknowledgement>} */
|
|
41
|
+
_pendingAcknowledgements: Map<string, PendingLocalBackgroundJobAcknowledgement>;
|
|
42
|
+
/** @type {Set<() => void>} */
|
|
43
|
+
_idleWaiters: Set<() => void>;
|
|
44
|
+
/** @type {ReturnType<typeof setTimeout> | number | undefined} */
|
|
45
|
+
_scheduledTimer: ReturnType<typeof setTimeout> | number | undefined;
|
|
46
|
+
/** @type {ReturnType<typeof setTimeout> | number | undefined} */
|
|
47
|
+
_recoveryTimer: ReturnType<typeof setTimeout> | number | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a dispatcher owned by one configuration and local store.
|
|
50
|
+
* @param {object} args - Dispatcher options.
|
|
51
|
+
* @param {import("../configuration.js").default} args.configuration - Owning configuration.
|
|
52
|
+
* @param {import("./types.js").LocalBackgroundJobsClock} args.clock - Dispatcher clock.
|
|
53
|
+
* @param {import("./local-job-registry.js").default} args.registry - Static job registry.
|
|
54
|
+
* @param {import("./local-store.js").default} args.store - Durable local store.
|
|
55
|
+
*/
|
|
56
|
+
constructor({ configuration, clock, registry, store }: {
|
|
57
|
+
configuration: import("../configuration.js").default;
|
|
58
|
+
clock: import("./types.js").LocalBackgroundJobsClock;
|
|
59
|
+
registry: import("./local-job-registry.js").default;
|
|
60
|
+
store: import("./local-store.js").default;
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Starts, recovers, and catches up the local dispatcher.
|
|
64
|
+
* @returns {Promise<void>} - Resolves after admission starts.
|
|
65
|
+
*/
|
|
66
|
+
start(): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Coalesces a dispatcher wake onto one tracked microtask.
|
|
69
|
+
* @returns {void} - No return value.
|
|
70
|
+
*/
|
|
71
|
+
wake(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Fills local capacity with short durable claims.
|
|
74
|
+
* @returns {Promise<void>} - Resolves after one stable drain pass.
|
|
75
|
+
*/
|
|
76
|
+
_drain(): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Runs one claimed performance without retaining its claim transaction connection.
|
|
79
|
+
* @param {{handoff: import("./types.js").BackgroundJobHandoff, job: import("./types.js").BackgroundJobRow}} args - Claimed job.
|
|
80
|
+
* @returns {void} - No return value.
|
|
81
|
+
*/
|
|
82
|
+
_startPerformance({ handoff, job }: {
|
|
83
|
+
handoff: import("./types.js").BackgroundJobHandoff;
|
|
84
|
+
job: import("./types.js").BackgroundJobRow;
|
|
85
|
+
}): void;
|
|
86
|
+
/**
|
|
87
|
+
* Performs and acknowledges one durable handoff.
|
|
88
|
+
* @param {{handoff: import("./types.js").BackgroundJobHandoff, job: import("./types.js").BackgroundJobRow}} args - Claimed job.
|
|
89
|
+
* @returns {Promise<void>} - Resolves after acknowledgement.
|
|
90
|
+
*/
|
|
91
|
+
_perform({ handoff, job }: {
|
|
92
|
+
handoff: import("./types.js").BackgroundJobHandoff;
|
|
93
|
+
job: import("./types.js").BackgroundJobRow;
|
|
94
|
+
}): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Applies one fenced durable acknowledgement.
|
|
97
|
+
* @param {PendingLocalBackgroundJobAcknowledgement} args - Owned acknowledgement.
|
|
98
|
+
* @returns {Promise<void>} - Resolves after the durable transition is settled.
|
|
99
|
+
*/
|
|
100
|
+
_acknowledge({ acknowledgement, handoff, job }: PendingLocalBackgroundJobAcknowledgement): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Replays each retained acknowledgement once at an event-driven wake boundary.
|
|
103
|
+
* @param {{throwOnError?: boolean}} [args] - Recovery behavior.
|
|
104
|
+
* @returns {Promise<void>} - Resolves after one bounded recovery pass.
|
|
105
|
+
*/
|
|
106
|
+
_retryPendingAcknowledgements({ throwOnError }?: {
|
|
107
|
+
throwOnError?: boolean;
|
|
108
|
+
}): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Arms the exact next future job timer, chunking platform-sized delays.
|
|
111
|
+
* @returns {Promise<void>} - Resolves after timer reconciliation.
|
|
112
|
+
*/
|
|
113
|
+
_armScheduledTimer(): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Arms one bounded retry after an unexpected drain failure.
|
|
116
|
+
* @returns {void} - No return value.
|
|
117
|
+
*/
|
|
118
|
+
_armRecoveryTimer(): void;
|
|
119
|
+
/**
|
|
120
|
+
* Waits for admission and every in-flight acknowledgement without polling.
|
|
121
|
+
* @returns {Promise<void>} - Resolves when idle.
|
|
122
|
+
*/
|
|
123
|
+
waitForIdle(): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Stops claims and waits for in-flight acknowledgement.
|
|
126
|
+
* @returns {Promise<void>} - Resolves after a graceful stop.
|
|
127
|
+
*/
|
|
128
|
+
stop(): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Reports whether dispatcher admission has started.
|
|
131
|
+
* @returns {boolean} - Whether dispatcher admission has started.
|
|
132
|
+
*/
|
|
133
|
+
isReady(): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Reads the configuration-owned in-process performance cap.
|
|
136
|
+
* @returns {number} - Configuration-owned in-process performance cap.
|
|
137
|
+
*/
|
|
138
|
+
_maxConcurrentJobs(): number;
|
|
139
|
+
/**
|
|
140
|
+
* Counts performances whose durable acknowledgement is still owned locally.
|
|
141
|
+
* @returns {number} - Active or pending-acknowledgement performances.
|
|
142
|
+
*/
|
|
143
|
+
_ownedPerformanceCount(): number;
|
|
144
|
+
/**
|
|
145
|
+
* Reports whether no admission or acknowledgement work remains.
|
|
146
|
+
* @returns {boolean} - Whether the dispatcher is idle.
|
|
147
|
+
*/
|
|
148
|
+
_isIdle(): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Resolves event-based idle waiters at a stable idle boundary.
|
|
151
|
+
* @returns {void} - No return value.
|
|
152
|
+
*/
|
|
153
|
+
_resolveIdleWaiters(): void;
|
|
154
|
+
/**
|
|
155
|
+
* Emits an expected job failure through the standard job/all-error channels.
|
|
156
|
+
* @param {{error: ReturnType<typeof JSON.parse>, job: import("./types.js").BackgroundJobRow}} args - Failure transition.
|
|
157
|
+
* @returns {void} - No return value.
|
|
158
|
+
*/
|
|
159
|
+
_emitBackgroundJobFailed({ error, job }: {
|
|
160
|
+
error: ReturnType<typeof JSON.parse>;
|
|
161
|
+
job: import("./types.js").BackgroundJobRow;
|
|
162
|
+
}): void;
|
|
163
|
+
/**
|
|
164
|
+
* Reports one failed durable acknowledgement attempt with its fence context.
|
|
165
|
+
* @param {PendingLocalBackgroundJobAcknowledgement & {error: ReturnType<typeof JSON.parse>}} args - Failed acknowledgement.
|
|
166
|
+
* @returns {void} - No return value.
|
|
167
|
+
*/
|
|
168
|
+
_reportAcknowledgementError({ acknowledgement, error, handoff, job }: PendingLocalBackgroundJobAcknowledgement & {
|
|
169
|
+
error: ReturnType<typeof JSON.parse>;
|
|
170
|
+
}): void;
|
|
171
|
+
/**
|
|
172
|
+
* Reports an unexpected dispatcher failure through framework channels.
|
|
173
|
+
* @param {object} args - Unexpected failure.
|
|
174
|
+
* @param {Record<string, ReturnType<typeof JSON.parse>>} [args.context] - Additional failure context.
|
|
175
|
+
* @param {ReturnType<typeof JSON.parse>} args.error - Unexpected error.
|
|
176
|
+
* @param {string} args.stage - Dispatcher stage.
|
|
177
|
+
* @returns {void} - No return value.
|
|
178
|
+
*/
|
|
179
|
+
_reportFrameworkError({ context, error, stage }: {
|
|
180
|
+
context?: Record<string, ReturnType<typeof JSON.parse>>;
|
|
181
|
+
error: ReturnType<typeof JSON.parse>;
|
|
182
|
+
stage: string;
|
|
183
|
+
}): void;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=local-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-dispatcher.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/local-dispatcher.js"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import BackgroundJobRescheduleSignal from "./reschedule-signal.js";
|
|
3
|
+
import performBackgroundJob from "./perform-job.js";
|
|
4
|
+
/** @typedef {{type: "completed"} | {type: "failed", error: ReturnType<typeof JSON.parse>} | {type: "rescheduled", delayMs: number}} LocalBackgroundJobAcknowledgement */
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {object} PendingLocalBackgroundJobAcknowledgement
|
|
7
|
+
* @property {LocalBackgroundJobAcknowledgement} acknowledgement - Durable transition still owned by this dispatcher.
|
|
8
|
+
* @property {import("./types.js").BackgroundJobHandoff} handoff - Fenced handoff being acknowledged.
|
|
9
|
+
* @property {import("./types.js").BackgroundJobRow} job - Claimed job snapshot.
|
|
10
|
+
*/
|
|
11
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
12
|
+
const ERROR_RECOVERY_DELAY_MS = 1_000;
|
|
13
|
+
/** Configuration-owned, event-driven in-process local dispatcher. */
|
|
14
|
+
export default class LocalBackgroundJobsDispatcher {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a dispatcher owned by one configuration and local store.
|
|
17
|
+
* @param {object} args - Dispatcher options.
|
|
18
|
+
* @param {import("../configuration.js").default} args.configuration - Owning configuration.
|
|
19
|
+
* @param {import("./types.js").LocalBackgroundJobsClock} args.clock - Dispatcher clock.
|
|
20
|
+
* @param {import("./local-job-registry.js").default} args.registry - Static job registry.
|
|
21
|
+
* @param {import("./local-store.js").default} args.store - Durable local store.
|
|
22
|
+
*/
|
|
23
|
+
constructor({ configuration, clock, registry, store }) {
|
|
24
|
+
this.clock = clock;
|
|
25
|
+
this.configuration = configuration;
|
|
26
|
+
this.registry = registry;
|
|
27
|
+
this.store = store;
|
|
28
|
+
this._accepting = false;
|
|
29
|
+
this._started = false;
|
|
30
|
+
/** @type {Promise<void> | null} */
|
|
31
|
+
this._startPromise = null;
|
|
32
|
+
/** @type {Promise<void> | null} */
|
|
33
|
+
this._drainPromise = null;
|
|
34
|
+
this._redrain = false;
|
|
35
|
+
this._wakeQueued = false;
|
|
36
|
+
/** @type {Set<Promise<void>>} */
|
|
37
|
+
this._inFlight = new Set();
|
|
38
|
+
/** @type {Map<string, PendingLocalBackgroundJobAcknowledgement>} */
|
|
39
|
+
this._pendingAcknowledgements = new Map();
|
|
40
|
+
/** @type {Set<() => void>} */
|
|
41
|
+
this._idleWaiters = new Set();
|
|
42
|
+
/** @type {ReturnType<typeof setTimeout> | number | undefined} */
|
|
43
|
+
this._scheduledTimer = undefined;
|
|
44
|
+
/** @type {ReturnType<typeof setTimeout> | number | undefined} */
|
|
45
|
+
this._recoveryTimer = undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Starts, recovers, and catches up the local dispatcher.
|
|
49
|
+
* @returns {Promise<void>} - Resolves after admission starts.
|
|
50
|
+
*/
|
|
51
|
+
async start() {
|
|
52
|
+
if (this._started)
|
|
53
|
+
return;
|
|
54
|
+
if (this._startPromise)
|
|
55
|
+
return await this._startPromise;
|
|
56
|
+
this._startPromise = (async () => {
|
|
57
|
+
this.configuration.setCurrent();
|
|
58
|
+
this.registry.ensureReady();
|
|
59
|
+
await this.configuration.initialize({ type: "local-background-jobs" });
|
|
60
|
+
await this.store.ensureReady();
|
|
61
|
+
await this.store.reconcileQueueConcurrency();
|
|
62
|
+
const recoveredJobs = await this.store.recoverHandedOffJobs();
|
|
63
|
+
for (const job of recoveredJobs) {
|
|
64
|
+
this._emitBackgroundJobFailed({
|
|
65
|
+
error: new Error(job.lastError || "Local background job recovered after an interrupted dispatcher"),
|
|
66
|
+
job
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
this._accepting = true;
|
|
70
|
+
this._started = true;
|
|
71
|
+
this.wake();
|
|
72
|
+
})();
|
|
73
|
+
try {
|
|
74
|
+
await this._startPromise;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
this._reportFrameworkError({ error, stage: "local-background-jobs-start" });
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
this._startPromise = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Coalesces a dispatcher wake onto one tracked microtask.
|
|
86
|
+
* @returns {void} - No return value.
|
|
87
|
+
*/
|
|
88
|
+
wake() {
|
|
89
|
+
if (!this._accepting)
|
|
90
|
+
return;
|
|
91
|
+
if (this._drainPromise || this._wakeQueued) {
|
|
92
|
+
this._redrain = true;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this._wakeQueued = true;
|
|
96
|
+
const drain = Promise.resolve().then(async () => {
|
|
97
|
+
this._wakeQueued = false;
|
|
98
|
+
await this._drain();
|
|
99
|
+
});
|
|
100
|
+
const drainPromise = drain
|
|
101
|
+
.catch((error) => {
|
|
102
|
+
this._reportFrameworkError({ error, stage: "local-background-jobs-drain" });
|
|
103
|
+
this._armRecoveryTimer();
|
|
104
|
+
})
|
|
105
|
+
.finally(() => {
|
|
106
|
+
if (this._drainPromise === drainPromise)
|
|
107
|
+
this._drainPromise = null;
|
|
108
|
+
if (this._redrain && this._accepting) {
|
|
109
|
+
this._redrain = false;
|
|
110
|
+
this.wake();
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
this._resolveIdleWaiters();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
this._drainPromise = drainPromise;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Fills local capacity with short durable claims.
|
|
120
|
+
* @returns {Promise<void>} - Resolves after one stable drain pass.
|
|
121
|
+
*/
|
|
122
|
+
async _drain() {
|
|
123
|
+
await this._retryPendingAcknowledgements();
|
|
124
|
+
while (this._accepting && this._ownedPerformanceCount() < this._maxConcurrentJobs()) {
|
|
125
|
+
const job = await this.store.nextAvailableJob();
|
|
126
|
+
if (!job)
|
|
127
|
+
break;
|
|
128
|
+
const handoff = await this.store.markHandedOff({ jobId: job.id });
|
|
129
|
+
if (!handoff) {
|
|
130
|
+
this._redrain = true;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
this._startPerformance({ handoff, job });
|
|
134
|
+
}
|
|
135
|
+
await this._armScheduledTimer();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Runs one claimed performance without retaining its claim transaction connection.
|
|
139
|
+
* @param {{handoff: import("./types.js").BackgroundJobHandoff, job: import("./types.js").BackgroundJobRow}} args - Claimed job.
|
|
140
|
+
* @returns {void} - No return value.
|
|
141
|
+
*/
|
|
142
|
+
_startPerformance({ handoff, job }) {
|
|
143
|
+
const performance = this._perform({ handoff, job });
|
|
144
|
+
this._inFlight.add(performance);
|
|
145
|
+
void performance
|
|
146
|
+
.catch((error) => this._reportFrameworkError({ error, stage: "local-background-jobs-performance" }))
|
|
147
|
+
.finally(() => {
|
|
148
|
+
this._inFlight.delete(performance);
|
|
149
|
+
if (this._accepting)
|
|
150
|
+
this.wake();
|
|
151
|
+
this._resolveIdleWaiters();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Performs and acknowledges one durable handoff.
|
|
156
|
+
* @param {{handoff: import("./types.js").BackgroundJobHandoff, job: import("./types.js").BackgroundJobRow}} args - Claimed job.
|
|
157
|
+
* @returns {Promise<void>} - Resolves after acknowledgement.
|
|
158
|
+
*/
|
|
159
|
+
async _perform({ handoff, job }) {
|
|
160
|
+
/** @type {LocalBackgroundJobAcknowledgement} */
|
|
161
|
+
let acknowledgement = { type: "completed" };
|
|
162
|
+
try {
|
|
163
|
+
const JobClass = this.registry.resolve(job.jobName);
|
|
164
|
+
await performBackgroundJob({
|
|
165
|
+
configuration: this.configuration,
|
|
166
|
+
JobClass,
|
|
167
|
+
jobArgs: job.args,
|
|
168
|
+
name: `Local background job: ${job.jobName}`
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (error instanceof BackgroundJobRescheduleSignal) {
|
|
173
|
+
acknowledgement = { delayMs: error.delayMs, type: "rescheduled" };
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
acknowledgement = { error, type: "failed" };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
await this._acknowledge({ acknowledgement, handoff, job });
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
this._pendingAcknowledgements.set(job.id, { acknowledgement, handoff, job });
|
|
184
|
+
this._reportAcknowledgementError({ acknowledgement, error, handoff, job });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Applies one fenced durable acknowledgement.
|
|
189
|
+
* @param {PendingLocalBackgroundJobAcknowledgement} args - Owned acknowledgement.
|
|
190
|
+
* @returns {Promise<void>} - Resolves after the durable transition is settled.
|
|
191
|
+
*/
|
|
192
|
+
async _acknowledge({ acknowledgement, handoff, job }) {
|
|
193
|
+
if (acknowledgement.type === "rescheduled") {
|
|
194
|
+
await this.store.markRescheduled({ delayMs: acknowledgement.delayMs, handoffId: handoff.handoffId, jobId: job.id });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (acknowledgement.type === "failed") {
|
|
198
|
+
const updatedJob = await this.store.markFailed({ error: acknowledgement.error, handoffId: handoff.handoffId, jobId: job.id });
|
|
199
|
+
if (updatedJob)
|
|
200
|
+
this._emitBackgroundJobFailed({ error: acknowledgement.error, job: updatedJob });
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
await this.store.markCompleted({ handoffId: handoff.handoffId, jobId: job.id });
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Replays each retained acknowledgement once at an event-driven wake boundary.
|
|
207
|
+
* @param {{throwOnError?: boolean}} [args] - Recovery behavior.
|
|
208
|
+
* @returns {Promise<void>} - Resolves after one bounded recovery pass.
|
|
209
|
+
*/
|
|
210
|
+
async _retryPendingAcknowledgements({ throwOnError = false } = {}) {
|
|
211
|
+
for (const [jobId, pendingAcknowledgement] of [...this._pendingAcknowledgements]) {
|
|
212
|
+
try {
|
|
213
|
+
await this._acknowledge(pendingAcknowledgement);
|
|
214
|
+
this._pendingAcknowledgements.delete(jobId);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
this._reportAcknowledgementError({ ...pendingAcknowledgement, error });
|
|
218
|
+
if (throwOnError)
|
|
219
|
+
throw error;
|
|
220
|
+
this._armRecoveryTimer();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Arms the exact next future job timer, chunking platform-sized delays.
|
|
226
|
+
* @returns {Promise<void>} - Resolves after timer reconciliation.
|
|
227
|
+
*/
|
|
228
|
+
async _armScheduledTimer() {
|
|
229
|
+
if (this._scheduledTimer !== undefined) {
|
|
230
|
+
this.clock.clearTimeout(this._scheduledTimer);
|
|
231
|
+
this._scheduledTimer = undefined;
|
|
232
|
+
}
|
|
233
|
+
if (!this._accepting)
|
|
234
|
+
return;
|
|
235
|
+
const nextJob = await this.store.nextScheduledJob();
|
|
236
|
+
if (!nextJob || nextJob.scheduledAtMs === null)
|
|
237
|
+
return;
|
|
238
|
+
const delayMs = Math.max(0, Math.min(nextJob.scheduledAtMs - this.clock.now(), MAX_TIMER_MS));
|
|
239
|
+
this._scheduledTimer = this.clock.setTimeout(() => {
|
|
240
|
+
this._scheduledTimer = undefined;
|
|
241
|
+
this.wake();
|
|
242
|
+
}, delayMs);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Arms one bounded retry after an unexpected drain failure.
|
|
246
|
+
* @returns {void} - No return value.
|
|
247
|
+
*/
|
|
248
|
+
_armRecoveryTimer() {
|
|
249
|
+
if (!this._accepting || this._recoveryTimer !== undefined)
|
|
250
|
+
return;
|
|
251
|
+
this._recoveryTimer = this.clock.setTimeout(() => {
|
|
252
|
+
this._recoveryTimer = undefined;
|
|
253
|
+
this.wake();
|
|
254
|
+
}, ERROR_RECOVERY_DELAY_MS);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Waits for admission and every in-flight acknowledgement without polling.
|
|
258
|
+
* @returns {Promise<void>} - Resolves when idle.
|
|
259
|
+
*/
|
|
260
|
+
async waitForIdle() {
|
|
261
|
+
if (this._isIdle())
|
|
262
|
+
return;
|
|
263
|
+
await new Promise((resolve) => this._idleWaiters.add(() => resolve(undefined)));
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Stops claims and waits for in-flight acknowledgement.
|
|
267
|
+
* @returns {Promise<void>} - Resolves after a graceful stop.
|
|
268
|
+
*/
|
|
269
|
+
async stop() {
|
|
270
|
+
this._accepting = false;
|
|
271
|
+
this._redrain = false;
|
|
272
|
+
if (this._scheduledTimer !== undefined) {
|
|
273
|
+
this.clock.clearTimeout(this._scheduledTimer);
|
|
274
|
+
this._scheduledTimer = undefined;
|
|
275
|
+
}
|
|
276
|
+
if (this._recoveryTimer !== undefined) {
|
|
277
|
+
this.clock.clearTimeout(this._recoveryTimer);
|
|
278
|
+
this._recoveryTimer = undefined;
|
|
279
|
+
}
|
|
280
|
+
if (this._drainPromise)
|
|
281
|
+
await this._drainPromise;
|
|
282
|
+
if (this._inFlight.size > 0)
|
|
283
|
+
await Promise.all([...this._inFlight]);
|
|
284
|
+
await this._retryPendingAcknowledgements({ throwOnError: true });
|
|
285
|
+
this._wakeQueued = false;
|
|
286
|
+
this._started = false;
|
|
287
|
+
this._resolveIdleWaiters();
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Reports whether dispatcher admission has started.
|
|
291
|
+
* @returns {boolean} - Whether dispatcher admission has started.
|
|
292
|
+
*/
|
|
293
|
+
isReady() { return this._started && this._accepting; }
|
|
294
|
+
/**
|
|
295
|
+
* Reads the configuration-owned in-process performance cap.
|
|
296
|
+
* @returns {number} - Configuration-owned in-process performance cap.
|
|
297
|
+
*/
|
|
298
|
+
_maxConcurrentJobs() { return this.configuration.getBackgroundJobsConfig().maxConcurrentInlineJobs; }
|
|
299
|
+
/**
|
|
300
|
+
* Counts performances whose durable acknowledgement is still owned locally.
|
|
301
|
+
* @returns {number} - Active or pending-acknowledgement performances.
|
|
302
|
+
*/
|
|
303
|
+
_ownedPerformanceCount() { return this._inFlight.size + this._pendingAcknowledgements.size; }
|
|
304
|
+
/**
|
|
305
|
+
* Reports whether no admission or acknowledgement work remains.
|
|
306
|
+
* @returns {boolean} - Whether the dispatcher is idle.
|
|
307
|
+
*/
|
|
308
|
+
_isIdle() {
|
|
309
|
+
return !this._wakeQueued && !this._drainPromise && this._inFlight.size === 0 && this._pendingAcknowledgements.size === 0;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Resolves event-based idle waiters at a stable idle boundary.
|
|
313
|
+
* @returns {void} - No return value.
|
|
314
|
+
*/
|
|
315
|
+
_resolveIdleWaiters() {
|
|
316
|
+
if (!this._isIdle())
|
|
317
|
+
return;
|
|
318
|
+
const waiters = [...this._idleWaiters];
|
|
319
|
+
this._idleWaiters.clear();
|
|
320
|
+
for (const resolve of waiters)
|
|
321
|
+
resolve();
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Emits an expected job failure through the standard job/all-error channels.
|
|
325
|
+
* @param {{error: ReturnType<typeof JSON.parse>, job: import("./types.js").BackgroundJobRow}} args - Failure transition.
|
|
326
|
+
* @returns {void} - No return value.
|
|
327
|
+
*/
|
|
328
|
+
_emitBackgroundJobFailed({ error, job }) {
|
|
329
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
330
|
+
const payload = {
|
|
331
|
+
context: {
|
|
332
|
+
attempts: job.attempts,
|
|
333
|
+
jobArgs: job.args,
|
|
334
|
+
jobId: job.id,
|
|
335
|
+
jobName: job.jobName,
|
|
336
|
+
maxRetries: job.maxRetries,
|
|
337
|
+
stage: "background-job-failed",
|
|
338
|
+
status: job.status,
|
|
339
|
+
terminal: job.status === "failed",
|
|
340
|
+
willRetry: job.status === "queued",
|
|
341
|
+
workerId: "local"
|
|
342
|
+
},
|
|
343
|
+
error: normalizedError
|
|
344
|
+
};
|
|
345
|
+
const errorEvents = this.configuration.getErrorEvents();
|
|
346
|
+
errorEvents.emit("background-job-failed", payload);
|
|
347
|
+
errorEvents.emit("all-error", { ...payload, errorType: "background-job-failed" });
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Reports one failed durable acknowledgement attempt with its fence context.
|
|
351
|
+
* @param {PendingLocalBackgroundJobAcknowledgement & {error: ReturnType<typeof JSON.parse>}} args - Failed acknowledgement.
|
|
352
|
+
* @returns {void} - No return value.
|
|
353
|
+
*/
|
|
354
|
+
_reportAcknowledgementError({ acknowledgement, error, handoff, job }) {
|
|
355
|
+
this._reportFrameworkError({
|
|
356
|
+
context: {
|
|
357
|
+
acknowledgementType: acknowledgement.type,
|
|
358
|
+
handoffId: handoff.handoffId,
|
|
359
|
+
jobId: job.id,
|
|
360
|
+
jobName: job.jobName,
|
|
361
|
+
workerId: "local"
|
|
362
|
+
},
|
|
363
|
+
error,
|
|
364
|
+
stage: "local-background-jobs-acknowledgement"
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Reports an unexpected dispatcher failure through framework channels.
|
|
369
|
+
* @param {object} args - Unexpected failure.
|
|
370
|
+
* @param {Record<string, ReturnType<typeof JSON.parse>>} [args.context] - Additional failure context.
|
|
371
|
+
* @param {ReturnType<typeof JSON.parse>} args.error - Unexpected error.
|
|
372
|
+
* @param {string} args.stage - Dispatcher stage.
|
|
373
|
+
* @returns {void} - No return value.
|
|
374
|
+
*/
|
|
375
|
+
_reportFrameworkError({ context = {}, error, stage }) {
|
|
376
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
377
|
+
const payload = { context: { ...context, stage }, error: normalizedError };
|
|
378
|
+
const errorEvents = this.configuration.getErrorEvents();
|
|
379
|
+
errorEvents.emit("framework-error", payload);
|
|
380
|
+
errorEvents.emit("all-error", { ...payload, errorType: "framework-error" });
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwtZGlzcGF0Y2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9iYWNrZ3JvdW5kLWpvYnMvbG9jYWwtZGlzcGF0Y2hlci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyw2QkFBNkIsTUFBTSx3QkFBd0IsQ0FBQTtBQUNsRSxPQUFPLG9CQUFvQixNQUFNLGtCQUFrQixDQUFBO0FBRW5ELHlLQUF5SztBQUV6Szs7Ozs7R0FLRztBQUVILE1BQU0sWUFBWSxHQUFHLGFBQWEsQ0FBQTtBQUNsQyxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQTtBQUVyQyxxRUFBcUU7QUFDckUsTUFBTSxDQUFDLE9BQU8sT0FBTyw2QkFBNkI7SUFDaEQ7Ozs7Ozs7T0FPRztJQUNILFlBQVksRUFBQyxhQUFhLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUM7UUFDakQsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUE7UUFDbEIsSUFBSSxDQUFDLGFBQWEsR0FBRyxhQUFhLENBQUE7UUFDbEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUE7UUFDeEIsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUE7UUFDbEIsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUE7UUFDdkIsSUFBSSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUE7UUFDckIsbUNBQW1DO1FBQ25DLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFBO1FBQ3pCLG1DQUFtQztRQUNuQyxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQTtRQUN6QixJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQTtRQUNyQixJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQTtRQUN4QixpQ0FBaUM7UUFDakMsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBQzFCLG9FQUFvRTtRQUNwRSxJQUFJ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QUUsR0FBRyxDQUFDLEVBQUU7Z0JBQ2IsT0FBTyxFQUFFLEdBQUcsQ0FBQyxPQUFPO2dCQUNwQixRQUFRLEVBQUUsT0FBTzthQUNsQjtZQUNELEtBQUs7WUFDTCxLQUFLLEVBQUUsdUNBQXVDO1NBQy9DLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gscUJBQXFCLENBQUMsRUFBQyxPQUFPLEdBQUcsRUFBRSxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUM7UUFDaEQsTUFBTSxlQUFlLEdBQUcsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQTtRQUNqRixNQUFNLE9BQU8sR0FBRyxFQUFDLE9BQU8sRUFBRSxFQUFDLEdBQUcsT0FBTyxFQUFFLEtBQUssRUFBQyxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUMsQ0FBQTtRQUN0RSxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLGNBQWMsRUFBRSxDQUFBO1FBRXZELFdBQVcsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLENBQUE7UUFDNUMsV0FBVyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsRUFBQyxHQUFHLE9BQU8sRUFBRSxTQUFTLEVBQUUsaUJBQWlCLEVBQUMsQ0FBQyxDQUFBO0lBQzNFLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgQmFja2dyb3VuZEpvYlJlc2NoZWR1bGVTaWduYWwgZnJvbSBcIi4vcmVzY2hlZHVsZS1zaWduYWwuanNcIlxuaW1wb3J0IHBlcmZvcm1CYWNrZ3JvdW5kSm9iIGZyb20gXCIuL3BlcmZvcm0tam9iLmpzXCJcblxuLyoqIEB0eXBlZGVmIHt7dHlwZTogXCJjb21wbGV0ZWRcIn0gfCB7dHlwZTogXCJmYWlsZWRcIiwgZXJyb3I6IFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fSB8IHt0eXBlOiBcInJlc2NoZWR1bGVkXCIsIGRlbGF5TXM6IG51bWJlcn19IExvY2FsQmFja2dyb3VuZEpvYkFja25vd2xlZGdlbWVudCAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHtvYmplY3R9IFBlbmRpbmdMb2NhbEJhY2tncm91bmRKb2JBY2tub3dsZWRnZW1lbnRcbiAqIEBwcm9wZXJ0eSB7TG9jYWxCYWNrZ3JvdW5kSm9iQWNrbm93bGVkZ2VtZW50fSBhY2tub3dsZWRnZW1lbnQgLSBEdXJhYmxlIHRyYW5zaXRpb24gc3RpbGwgb3duZWQgYnkgdGhpcyBkaXNwYXRjaGVyLlxuICogQHByb3BlcnR5IHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JIYW5kb2ZmfSBoYW5kb2ZmIC0gRmVuY2VkIGhhbmRvZmYgYmVpbmcgYWNrbm93bGVkZ2VkLlxuICogQHByb3BlcnR5IHtpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JSb3d9IGpvYiAtIENsYWltZWQgam9iIHNuYXBzaG90LlxuICovXG5cbmNvbnN0IE1BWF9USU1FUl9NUyA9IDJfMTQ3XzQ4M182NDdcbmNvbnN0IEVSUk9SX1JFQ09WRVJZX0RFTEFZX01TID0gMV8wMDBcblxuLyoqIENvbmZpZ3VyYXRpb24tb3duZWQsIGV2ZW50LWRyaXZlbiBpbi1wcm9jZXNzIGxvY2FsIGRpc3BhdGNoZXIuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBMb2NhbEJhY2tncm91bmRKb2JzRGlzcGF0Y2hlciB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgZGlzcGF0Y2hlciBvd25lZCBieSBvbmUgY29uZmlndXJhdGlvbiBhbmQgbG9jYWwgc3RvcmUuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gRGlzcGF0Y2hlciBvcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gYXJncy5jb25maWd1cmF0aW9uIC0gT3duaW5nIGNvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi90eXBlcy5qc1wiKS5Mb2NhbEJhY2tncm91bmRKb2JzQ2xvY2t9IGFyZ3MuY2xvY2sgLSBEaXNwYXRjaGVyIGNsb2NrLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vbG9jYWwtam9iLXJlZ2lzdHJ5LmpzXCIpLmRlZmF1bHR9IGFyZ3MucmVnaXN0cnkgLSBTdGF0aWMgam9iIHJlZ2lzdHJ5LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vbG9jYWwtc3RvcmUuanNcIikuZGVmYXVsdH0gYXJncy5zdG9yZSAtIER1cmFibGUgbG9jYWwgc3RvcmUuXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29uZmlndXJhdGlvbiwgY2xvY2ssIHJlZ2lzdHJ5LCBzdG9yZX0pIHtcbiAgICB0aGlzLmNsb2NrID0gY2xvY2tcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uXG4gICAgdGhpcy5yZWdpc3RyeSA9IHJlZ2lzdHJ5XG4gICAgdGhpcy5zdG9yZSA9IHN0b3JlXG4gICAgdGhpcy5fYWNjZXB0aW5nID0gZmFsc2VcbiAgICB0aGlzLl9zdGFydGVkID0gZmFsc2VcbiAgICAvKiogQHR5cGUge1Byb21pc2U8dm9pZD4gfCBudWxsfSAqL1xuICAgIHRoaXMuX3N0YXJ0UHJvbWlzZSA9IG51bGxcbiAgICAvKiogQHR5cGUge1Byb21pc2U8dm9pZD4gfCBudWxsfSAqL1xuICAgIHRoaXMuX2RyYWluUHJvbWlzZSA9IG51bGxcbiAgICB0aGlzLl9yZWRyYWluID0gZmFsc2VcbiAgICB0aGlzLl93YWtlUXVldWVkID0gZmFsc2VcbiAgICAvKiogQHR5cGUge1NldDxQcm9taXNlPHZvaWQ+Pn0gKi9cbiAgICB0aGlzLl9pbkZsaWdodCA9IG5ldyBTZXQoKVxuICAgIC8qKiBAdHlwZSB7TWFwPHN0cmluZywgUGVuZGluZ0xvY2FsQmFja2dyb3VuZEpvYkFja25vd2xlZGdlbWVudD59ICovXG4gICAgdGhpcy5fcGVuZGluZ0Fja25vd2xlZGdlbWVudHMgPSBuZXcgTWFwKClcbiAgICAvKiogQHR5cGUge1NldDwoKSA9PiB2b2lkPn0gKi9cbiAgICB0aGlzLl9pZGxlV2FpdGVycyA9IG5ldyBTZXQoKVxuICAgIC8qKiBAdHlwZSB7UmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCBudW1iZXIgfCB1bmRlZmluZWR9ICovXG4gICAgdGhpcy5fc2NoZWR1bGVkVGltZXIgPSB1bmRlZmluZWRcbiAgICAvKiogQHR5cGUge1JldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVtYmVyIHwgdW5kZWZpbmVkfSAqL1xuICAgIHRoaXMuX3JlY292ZXJ5VGltZXIgPSB1bmRlZmluZWRcbiAgfVxuXG4gIC8qKlxuICAgKiBTdGFydHMsIHJlY292ZXJzLCBhbmQgY2F0Y2hlcyB1cCB0aGUgbG9jYWwgZGlzcGF0Y2hlci5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgYWRtaXNzaW9uIHN0YXJ0cy5cbiAgICovXG4gIGFzeW5jIHN0YXJ0KCkge1xuICAgIGlmICh0aGlzLl9zdGFydGVkKSByZXR1cm5cbiAgICBpZiAodGhpcy5fc3RhcnRQcm9taXNlKSByZXR1cm4gYXdhaXQgdGhpcy5fc3RhcnRQcm9taXNlXG5cbiAgICB0aGlzLl9zdGFydFByb21pc2UgPSAoYXN5bmMgKCkgPT4ge1xuICAgICAgdGhpcy5jb25maWd1cmF0aW9uLnNldEN1cnJlbnQoKVxuICAgICAgdGhpcy5yZWdpc3RyeS5lbnN1cmVSZWFkeSgpXG4gICAgICBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uaW5pdGlhbGl6ZSh7dHlwZTogXCJsb2NhbC1iYWNrZ3JvdW5kLWpvYnNcIn0pXG4gICAgICBhd2FpdCB0aGlzLnN0b3JlLmVuc3VyZVJlYWR5KClcbiAgICAgIGF3YWl0IHRoaXMuc3RvcmUucmVjb25jaWxlUXVldWVDb25jdXJyZW5jeSgpXG5cbiAgICAgIGNvbnN0IHJlY292ZXJlZEpvYnMgPSBhd2FpdCB0aGlzLnN0b3JlLnJlY292ZXJIYW5kZWRPZmZKb2JzKClcblxuICAgICAgZm9yIChjb25zdCBqb2Igb2YgcmVjb3ZlcmVkSm9icykge1xuICAgICAgICB0aGlzLl9lbWl0QmFja2dyb3VuZEpvYkZhaWxlZCh7XG4gICAgICAgICAgZXJyb3I6IG5ldyBFcnJvcihqb2IubGFzdEVycm9yIHx8IFwiTG9jYWwgYmFja2dyb3VuZCBqb2IgcmVjb3ZlcmVkIGFmdGVyIGFuIGludGVycnVwdGVkIGRpc3BhdGNoZXJcIiksXG4gICAgICAgICAgam9iXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIHRoaXMuX2FjY2VwdGluZyA9IHRydWVcbiAgICAgIHRoaXMuX3N0YXJ0ZWQgPSB0cnVlXG4gICAgICB0aGlzLndha2UoKVxuICAgIH0pKClcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9zdGFydFByb21pc2VcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5fcmVwb3J0RnJhbWV3b3JrRXJyb3Ioe2Vycm9yLCBzdGFnZTogXCJsb2NhbC1iYWNrZ3JvdW5kLWpvYnMtc3RhcnRcIn0pXG4gICAgICB0aHJvdyBlcnJvclxuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLl9zdGFydFByb21pc2UgPSBudWxsXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENvYWxlc2NlcyBhIGRpc3BhdGNoZXIgd2FrZSBvbnRvIG9uZSB0cmFja2VkIG1pY3JvdGFzay5cbiAgICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgd2FrZSgpIHtcbiAgICBpZiAoIXRoaXMuX2FjY2VwdGluZykgcmV0dXJuXG5cbiAgICBpZiAodGhpcy5fZHJhaW5Qcm9taXNlIHx8IHRoaXMuX3dha2VRdWV1ZWQpIHtcbiAgICAgIHRoaXMuX3JlZHJhaW4gPSB0cnVlXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB0aGlzLl93YWtlUXVldWVkID0gdHJ1ZVxuICAgIGNvbnN0IGRyYWluID0gUHJvbWlzZS5yZXNvbHZlKCkudGhlbihhc3luYyAoKSA9PiB7XG4gICAgICB0aGlzLl93YWtlUXVldWVkID0gZmFsc2VcbiAgICAgIGF3YWl0IHRoaXMuX2RyYWluKClcbiAgICB9KVxuICAgIGNvbnN0IGRyYWluUHJvbWlzZSA9IGRyYWluXG4gICAgICAuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgICAgIHRoaXMuX3JlcG9ydEZyYW1ld29ya0Vycm9yKHtlcnJvciwgc3RhZ2U6IFwibG9jYWwtYmFja2dyb3VuZC1qb2JzLWRyYWluXCJ9KVxuICAgICAgICB0aGlzLl9hcm1SZWNvdmVyeVRpbWVyKClcbiAgICAgIH0pXG4gICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLl9kcmFpblByb21pc2UgPT09IGRyYWluUHJvbWlzZSkgdGhpcy5fZHJhaW5Qcm9taXNlID0gbnVsbFxuXG4gICAgICAgIGlmICh0aGlzLl9yZWRyYWluICYmIHRoaXMuX2FjY2VwdGluZykge1xuICAgICAgICAgIHRoaXMuX3JlZHJhaW4gPSBmYWxzZVxuICAgICAgICAgIHRoaXMud2FrZSgpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5fcmVzb2x2ZUlkbGVXYWl0ZXJzKClcbiAgICAgICAgfVxuICAgICAgfSlcblxuICAgIHRoaXMuX2RyYWluUHJvbWlzZSA9IGRyYWluUHJvbWlzZVxuICB9XG5cbiAgLyoqXG4gICAqIEZpbGxzIGxvY2FsIGNhcGFjaXR5IHdpdGggc2hvcnQgZHVyYWJsZSBjbGFpbXMuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIG9uZSBzdGFibGUgZHJhaW4gcGFzcy5cbiAgICovXG4gIGFzeW5jIF9kcmFpbigpIHtcbiAgICBhd2FpdCB0aGlzLl9yZXRyeVBlbmRpbmdBY2tub3dsZWRnZW1lbnRzKClcblxuICAgIHdoaWxlICh0aGlzLl9hY2NlcHRpbmcgJiYgdGhpcy5fb3duZWRQZXJmb3JtYW5jZUNvdW50KCkgPCB0aGlzLl9tYXhDb25jdXJyZW50Sm9icygpKSB7XG4gICAgICBjb25zdCBqb2IgPSBhd2FpdCB0aGlzLnN0b3JlLm5leHRBdmFpbGFibGVKb2IoKVxuXG4gICAgICBpZiAoIWpvYikgYnJlYWtcblxuICAgICAgY29uc3QgaGFuZG9mZiA9IGF3YWl0IHRoaXMuc3RvcmUubWFya0hhbmRlZE9mZih7am9iSWQ6IGpvYi5pZH0pXG5cbiAgICAgIGlmICghaGFuZG9mZikge1xuICAgICAgICB0aGlzLl9yZWRyYWluID0gdHJ1ZVxuICAgICAgICBicmVha1xuICAgICAgfVxuXG4gICAgICB0aGlzLl9zdGFydFBlcmZvcm1hbmNlKHtoYW5kb2ZmLCBqb2J9KVxuICAgIH1cblxuICAgIGF3YWl0IHRoaXMuX2FybVNjaGVkdWxlZFRpbWVyKClcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIG9uZSBjbGFpbWVkIHBlcmZvcm1hbmNlIHdpdGhvdXQgcmV0YWluaW5nIGl0cyBjbGFpbSB0cmFuc2FjdGlvbiBjb25uZWN0aW9uLlxuICAgKiBAcGFyYW0ge3toYW5kb2ZmOiBpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JIYW5kb2ZmLCBqb2I6IGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvd319IGFyZ3MgLSBDbGFpbWVkIGpvYi5cbiAgICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgX3N0YXJ0UGVyZm9ybWFuY2Uoe2hhbmRvZmYsIGpvYn0pIHtcbiAgICBjb25zdCBwZXJmb3JtYW5jZSA9IHRoaXMuX3BlcmZvcm0oe2hhbmRvZmYsIGpvYn0pXG5cbiAgICB0aGlzLl9pbkZsaWdodC5hZGQocGVyZm9ybWFuY2UpXG4gICAgdm9pZCBwZXJmb3JtYW5jZVxuICAgICAgLmNhdGNoKChlcnJvcikgPT4gdGhpcy5fcmVwb3J0RnJhbWV3b3JrRXJyb3Ioe2Vycm9yLCBzdGFnZTogXCJsb2NhbC1iYWNrZ3JvdW5kLWpvYnMtcGVyZm9ybWFuY2VcIn0pKVxuICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICB0aGlzLl9pbkZsaWdodC5kZWxldGUocGVyZm9ybWFuY2UpXG4gICAgICAgIGlmICh0aGlzLl9hY2NlcHRpbmcpIHRoaXMud2FrZSgpXG4gICAgICAgIHRoaXMuX3Jlc29sdmVJZGxlV2FpdGVycygpXG4gICAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFBlcmZvcm1zIGFuZCBhY2tub3dsZWRnZXMgb25lIGR1cmFibGUgaGFuZG9mZi5cbiAgICogQHBhcmFtIHt7aGFuZG9mZjogaW1wb3J0KFwiLi90eXBlcy5qc1wiKS5CYWNrZ3JvdW5kSm9iSGFuZG9mZiwgam9iOiBpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JSb3d9fSBhcmdzIC0gQ2xhaW1lZCBqb2IuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIGFja25vd2xlZGdlbWVudC5cbiAgICovXG4gIGFzeW5jIF9wZXJmb3JtKHtoYW5kb2ZmLCBqb2J9KSB7XG4gICAgLyoqIEB0eXBlIHtMb2NhbEJhY2tncm91bmRKb2JBY2tub3dsZWRnZW1lbnR9ICovXG4gICAgbGV0IGFja25vd2xlZGdlbWVudCA9IHt0eXBlOiBcImNvbXBsZXRlZFwifVxuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IEpvYkNsYXNzID0gdGhpcy5yZWdpc3RyeS5yZXNvbHZlKGpvYi5qb2JOYW1lKVxuXG4gICAgICBhd2FpdCBwZXJmb3JtQmFja2dyb3VuZEpvYih7XG4gICAgICAgIGNvbmZpZ3VyYXRpb246IHRoaXMuY29uZmlndXJhdGlvbixcbiAgICAgICAgSm9iQ2xhc3MsXG4gICAgICAgIGpvYkFyZ3M6IGpvYi5hcmdzLFxuICAgICAgICBuYW1lOiBgTG9jYWwgYmFja2dyb3VuZCBqb2I6ICR7am9iLmpvYk5hbWV9YFxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgQmFja2dyb3VuZEpvYlJlc2NoZWR1bGVTaWduYWwpIHtcbiAgICAgICAgYWNrbm93bGVkZ2VtZW50ID0ge2RlbGF5TXM6IGVycm9yLmRlbGF5TXMsIHR5cGU6IFwicmVzY2hlZHVsZWRcIn1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGFja25vd2xlZGdlbWVudCA9IHtlcnJvciwgdHlwZTogXCJmYWlsZWRcIn1cbiAgICAgIH1cbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5fYWNrbm93bGVkZ2Uoe2Fja25vd2xlZGdlbWVudCwgaGFuZG9mZiwgam9ifSlcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5fcGVuZGluZ0Fja25vd2xlZGdlbWVudHMuc2V0KGpvYi5pZCwge2Fja25vd2xlZGdlbWVudCwgaGFuZG9mZiwgam9ifSlcbiAgICAgIHRoaXMuX3JlcG9ydEFja25vd2xlZGdlbWVudEVycm9yKHthY2tub3dsZWRnZW1lbnQsIGVycm9yLCBoYW5kb2ZmLCBqb2J9KVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBsaWVzIG9uZSBmZW5jZWQgZHVyYWJsZSBhY2tub3dsZWRnZW1lbnQuXG4gICAqIEBwYXJhbSB7UGVuZGluZ0xvY2FsQmFja2dyb3VuZEpvYkFja25vd2xlZGdlbWVudH0gYXJncyAtIE93bmVkIGFja25vd2xlZGdlbWVudC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gUmVzb2x2ZXMgYWZ0ZXIgdGhlIGR1cmFibGUgdHJhbnNpdGlvbiBpcyBzZXR0bGVkLlxuICAgKi9cbiAgYXN5bmMgX2Fja25vd2xlZGdlKHthY2tub3dsZWRnZW1lbnQsIGhhbmRvZmYsIGpvYn0pIHtcbiAgICBpZiAoYWNrbm93bGVkZ2VtZW50LnR5cGUgPT09IFwicmVzY2hlZHVsZWRcIikge1xuICAgICAgYXdhaXQgdGhpcy5zdG9yZS5tYXJrUmVzY2hlZHVsZWQoe2RlbGF5TXM6IGFja25vd2xlZGdlbWVudC5kZWxheU1zLCBoYW5kb2ZmSWQ6IGhhbmRvZmYuaGFuZG9mZklkLCBqb2JJZDogam9iLmlkfSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChhY2tub3dsZWRnZW1lbnQudHlwZSA9PT0gXCJmYWlsZWRcIikge1xuICAgICAgY29uc3QgdXBkYXRlZEpvYiA9IGF3YWl0IHRoaXMuc3RvcmUubWFya0ZhaWxlZCh7ZXJyb3I6IGFja25vd2xlZGdlbWVudC5lcnJvciwgaGFuZG9mZklkOiBoYW5kb2ZmLmhhbmRvZmZJZCwgam9iSWQ6IGpvYi5pZH0pXG5cbiAgICAgIGlmICh1cGRhdGVkSm9iKSB0aGlzLl9lbWl0QmFja2dyb3VuZEpvYkZhaWxlZCh7ZXJyb3I6IGFja25vd2xlZGdlbWVudC5lcnJvciwgam9iOiB1cGRhdGVkSm9ifSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGF3YWl0IHRoaXMuc3RvcmUubWFya0NvbXBsZXRlZCh7aGFuZG9mZklkOiBoYW5kb2ZmLmhhbmRvZmZJZCwgam9iSWQ6IGpvYi5pZH0pXG4gIH1cblxuICAvKipcbiAgICogUmVwbGF5cyBlYWNoIHJldGFpbmVkIGFja25vd2xlZGdlbWVudCBvbmNlIGF0IGFuIGV2ZW50LWRyaXZlbiB3YWtlIGJvdW5kYXJ5LlxuICAgKiBAcGFyYW0ge3t0aHJvd09uRXJyb3I/OiBib29sZWFufX0gW2FyZ3NdIC0gUmVjb3ZlcnkgYmVoYXZpb3IuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIG9uZSBib3VuZGVkIHJlY292ZXJ5IHBhc3MuXG4gICAqL1xuICBhc3luYyBfcmV0cnlQZW5kaW5nQWNrbm93bGVkZ2VtZW50cyh7dGhyb3dPbkVycm9yID0gZmFsc2V9ID0ge30pIHtcbiAgICBmb3IgKGNvbnN0IFtqb2JJZCwgcGVuZGluZ0Fja25vd2xlZGdlbWVudF0gb2YgWy4uLnRoaXMuX3BlbmRpbmdBY2tub3dsZWRnZW1lbnRzXSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5fYWNrbm93bGVkZ2UocGVuZGluZ0Fja25vd2xlZGdlbWVudClcbiAgICAgICAgdGhpcy5fcGVuZGluZ0Fja25vd2xlZGdlbWVudHMuZGVsZXRlKGpvYklkKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhpcy5fcmVwb3J0QWNrbm93bGVkZ2VtZW50RXJyb3Ioey4uLnBlbmRpbmdBY2tub3dsZWRnZW1lbnQsIGVycm9yfSlcbiAgICAgICAgaWYgKHRocm93T25FcnJvcikgdGhyb3cgZXJyb3JcbiAgICAgICAgdGhpcy5fYXJtUmVjb3ZlcnlUaW1lcigpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFybXMgdGhlIGV4YWN0IG5leHQgZnV0dXJlIGpvYiB0aW1lciwgY2h1bmtpbmcgcGxhdGZvcm0tc2l6ZWQgZGVsYXlzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyBhZnRlciB0aW1lciByZWNvbmNpbGlhdGlvbi5cbiAgICovXG4gIGFzeW5jIF9hcm1TY2hlZHVsZWRUaW1lcigpIHtcbiAgICBpZiAodGhpcy5fc2NoZWR1bGVkVGltZXIgIT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5jbG9jay5jbGVhclRpbWVvdXQodGhpcy5fc2NoZWR1bGVkVGltZXIpXG4gICAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHVuZGVmaW5lZFxuICAgIH1cbiAgICBpZiAoIXRoaXMuX2FjY2VwdGluZykgcmV0dXJuXG5cbiAgICBjb25zdCBuZXh0Sm9iID0gYXdhaXQgdGhpcy5zdG9yZS5uZXh0U2NoZWR1bGVkSm9iKClcblxuICAgIGlmICghbmV4dEpvYiB8fCBuZXh0Sm9iLnNjaGVkdWxlZEF0TXMgPT09IG51bGwpIHJldHVyblxuXG4gICAgY29uc3QgZGVsYXlNcyA9IE1hdGgubWF4KDAsIE1hdGgubWluKG5leHRKb2Iuc2NoZWR1bGVkQXRNcyAtIHRoaXMuY2xvY2subm93KCksIE1BWF9USU1FUl9NUykpXG5cbiAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHRoaXMuY2xvY2suc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLl9zY2hlZHVsZWRUaW1lciA9IHVuZGVmaW5lZFxuICAgICAgdGhpcy53YWtlKClcbiAgICB9LCBkZWxheU1zKVxuICB9XG5cbiAgLyoqXG4gICAqIEFybXMgb25lIGJvdW5kZWQgcmV0cnkgYWZ0ZXIgYW4gdW5leHBlY3RlZCBkcmFpbiBmYWlsdXJlLlxuICAgKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gICAqL1xuICBfYXJtUmVjb3ZlcnlUaW1lcigpIHtcbiAgICBpZiAoIXRoaXMuX2FjY2VwdGluZyB8fCB0aGlzLl9yZWNvdmVyeVRpbWVyICE9PSB1bmRlZmluZWQpIHJldHVyblxuXG4gICAgdGhpcy5fcmVjb3ZlcnlUaW1lciA9IHRoaXMuY2xvY2suc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB0aGlzLl9yZWNvdmVyeVRpbWVyID0gdW5kZWZpbmVkXG4gICAgICB0aGlzLndha2UoKVxuICAgIH0sIEVSUk9SX1JFQ09WRVJZX0RFTEFZX01TKVxuICB9XG5cbiAgLyoqXG4gICAqIFdhaXRzIGZvciBhZG1pc3Npb24gYW5kIGV2ZXJ5IGluLWZsaWdodCBhY2tub3dsZWRnZW1lbnQgd2l0aG91dCBwb2xsaW5nLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBSZXNvbHZlcyB3aGVuIGlkbGUuXG4gICAqL1xuICBhc3luYyB3YWl0Rm9ySWRsZSgpIHtcbiAgICBpZiAodGhpcy5faXNJZGxlKCkpIHJldHVyblxuXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHRoaXMuX2lkbGVXYWl0ZXJzLmFkZCgoKSA9PiByZXNvbHZlKHVuZGVmaW5lZCkpKVxuICB9XG5cbiAgLyoqXG4gICAqIFN0b3BzIGNsYWltcyBhbmQgd2FpdHMgZm9yIGluLWZsaWdodCBhY2tub3dsZWRnZW1lbnQuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIGFmdGVyIGEgZ3JhY2VmdWwgc3RvcC5cbiAgICovXG4gIGFzeW5jIHN0b3AoKSB7XG4gICAgdGhpcy5fYWNjZXB0aW5nID0gZmFsc2VcbiAgICB0aGlzLl9yZWRyYWluID0gZmFsc2VcblxuICAgIGlmICh0aGlzLl9zY2hlZHVsZWRUaW1lciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aGlzLmNsb2NrLmNsZWFyVGltZW91dCh0aGlzLl9zY2hlZHVsZWRUaW1lcilcbiAgICAgIHRoaXMuX3NjaGVkdWxlZFRpbWVyID0gdW5kZWZpbmVkXG4gICAgfVxuICAgIGlmICh0aGlzLl9yZWNvdmVyeVRpbWVyICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuY2xvY2suY2xlYXJUaW1lb3V0KHRoaXMuX3JlY292ZXJ5VGltZXIpXG4gICAgICB0aGlzLl9yZWNvdmVyeVRpbWVyID0gdW5kZWZpbmVkXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX2RyYWluUHJvbWlzZSkgYXdhaXQgdGhpcy5fZHJhaW5Qcm9taXNlXG4gICAgaWYgKHRoaXMuX2luRmxpZ2h0LnNpemUgPiAwKSBhd2FpdCBQcm9taXNlLmFsbChbLi4udGhpcy5faW5GbGlnaHRdKVxuICAgIGF3YWl0IHRoaXMuX3JldHJ5UGVuZGluZ0Fja25vd2xlZGdlbWVudHMoe3Rocm93T25FcnJvcjogdHJ1ZX0pXG5cbiAgICB0aGlzLl93YWtlUXVldWVkID0gZmFsc2VcbiAgICB0aGlzLl9zdGFydGVkID0gZmFsc2VcbiAgICB0aGlzLl9yZXNvbHZlSWRsZVdhaXRlcnMoKVxuICB9XG5cbiAgLyoqXG4gICAqIFJlcG9ydHMgd2hldGhlciBkaXNwYXRjaGVyIGFkbWlzc2lvbiBoYXMgc3RhcnRlZC5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gV2hldGhlciBkaXNwYXRjaGVyIGFkbWlzc2lvbiBoYXMgc3RhcnRlZC5cbiAgICovXG4gIGlzUmVhZHkoKSB7IHJldHVybiB0aGlzLl9zdGFydGVkICYmIHRoaXMuX2FjY2VwdGluZyB9XG5cbiAgLyoqXG4gICAqIFJlYWRzIHRoZSBjb25maWd1cmF0aW9uLW93bmVkIGluLXByb2Nlc3MgcGVyZm9ybWFuY2UgY2FwLlxuICAgKiBAcmV0dXJucyB7bnVtYmVyfSAtIENvbmZpZ3VyYXRpb24tb3duZWQgaW4tcHJvY2VzcyBwZXJmb3JtYW5jZSBjYXAuXG4gICAqL1xuICBfbWF4Q29uY3VycmVudEpvYnMoKSB7IHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb24uZ2V0QmFja2dyb3VuZEpvYnNDb25maWcoKS5tYXhDb25jdXJyZW50SW5saW5lSm9icyB9XG5cbiAgLyoqXG4gICAqIENvdW50cyBwZXJmb3JtYW5jZXMgd2hvc2UgZHVyYWJsZSBhY2tub3dsZWRnZW1lbnQgaXMgc3RpbGwgb3duZWQgbG9jYWxseS5cbiAgICogQHJldHVybnMge251bWJlcn0gLSBBY3RpdmUgb3IgcGVuZGluZy1hY2tub3dsZWRnZW1lbnQgcGVyZm9ybWFuY2VzLlxuICAgKi9cbiAgX293bmVkUGVyZm9ybWFuY2VDb3VudCgpIHsgcmV0dXJuIHRoaXMuX2luRmxpZ2h0LnNpemUgKyB0aGlzLl9wZW5kaW5nQWNrbm93bGVkZ2VtZW50cy5zaXplIH1cblxuICAvKipcbiAgICogUmVwb3J0cyB3aGV0aGVyIG5vIGFkbWlzc2lvbiBvciBhY2tub3dsZWRnZW1lbnQgd29yayByZW1haW5zLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBXaGV0aGVyIHRoZSBkaXNwYXRjaGVyIGlzIGlkbGUuXG4gICAqL1xuICBfaXNJZGxlKCkge1xuICAgIHJldHVybiAhdGhpcy5fd2FrZVF1ZXVlZCAmJiAhdGhpcy5fZHJhaW5Qcm9taXNlICYmIHRoaXMuX2luRmxpZ2h0LnNpemUgPT09IDAgJiYgdGhpcy5fcGVuZGluZ0Fja25vd2xlZGdlbWVudHMuc2l6ZSA9PT0gMFxuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmVzIGV2ZW50LWJhc2VkIGlkbGUgd2FpdGVycyBhdCBhIHN0YWJsZSBpZGxlIGJvdW5kYXJ5LlxuICAgKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gICAqL1xuICBfcmVzb2x2ZUlkbGVXYWl0ZXJzKCkge1xuICAgIGlmICghdGhpcy5faXNJZGxlKCkpIHJldHVyblxuXG4gICAgY29uc3Qgd2FpdGVycyA9IFsuLi50aGlzLl9pZGxlV2FpdGVyc11cblxuICAgIHRoaXMuX2lkbGVXYWl0ZXJzLmNsZWFyKClcbiAgICBmb3IgKGNvbnN0IHJlc29sdmUgb2Ygd2FpdGVycykgcmVzb2x2ZSgpXG4gIH1cblxuICAvKipcbiAgICogRW1pdHMgYW4gZXhwZWN0ZWQgam9iIGZhaWx1cmUgdGhyb3VnaCB0aGUgc3RhbmRhcmQgam9iL2FsbC1lcnJvciBjaGFubmVscy5cbiAgICogQHBhcmFtIHt7ZXJyb3I6IFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+LCBqb2I6IGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYlJvd319IGFyZ3MgLSBGYWlsdXJlIHRyYW5zaXRpb24uXG4gICAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAgICovXG4gIF9lbWl0QmFja2dyb3VuZEpvYkZhaWxlZCh7ZXJyb3IsIGpvYn0pIHtcbiAgICBjb25zdCBub3JtYWxpemVkRXJyb3IgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IgOiBuZXcgRXJyb3IoU3RyaW5nKGVycm9yKSlcbiAgICBjb25zdCBwYXlsb2FkID0ge1xuICAgICAgY29udGV4dDoge1xuICAgICAgICBhdHRlbXB0czogam9iLmF0dGVtcHRzLFxuICAgICAgICBqb2JBcmdzOiBqb2IuYXJncyxcbiAgICAgICAgam9iSWQ6IGpvYi5pZCxcbiAgICAgICAgam9iTmFtZTogam9iLmpvYk5hbWUsXG4gICAgICAgIG1heFJldHJpZXM6IGpvYi5tYXhSZXRyaWVzLFxuICAgICAgICBzdGFnZTogXCJiYWNrZ3JvdW5kLWpvYi1mYWlsZWRcIixcbiAgICAgICAgc3RhdHVzOiBqb2Iuc3RhdHVzLFxuICAgICAgICB0ZXJtaW5hbDogam9iLnN0YXR1cyA9PT0gXCJmYWlsZWRcIixcbiAgICAgICAgd2lsbFJldHJ5OiBqb2Iuc3RhdHVzID09PSBcInF1ZXVlZFwiLFxuICAgICAgICB3b3JrZXJJZDogXCJsb2NhbFwiXG4gICAgICB9LFxuICAgICAgZXJyb3I6IG5vcm1hbGl6ZWRFcnJvclxuICAgIH1cbiAgICBjb25zdCBlcnJvckV2ZW50cyA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXRFcnJvckV2ZW50cygpXG5cbiAgICBlcnJvckV2ZW50cy5lbWl0KFwiYmFja2dyb3VuZC1qb2ItZmFpbGVkXCIsIHBheWxvYWQpXG4gICAgZXJyb3JFdmVudHMuZW1pdChcImFsbC1lcnJvclwiLCB7Li4ucGF5bG9hZCwgZXJyb3JUeXBlOiBcImJhY2tncm91bmQtam9iLWZhaWxlZFwifSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBvcnRzIG9uZSBmYWlsZWQgZHVyYWJsZSBhY2tub3dsZWRnZW1lbnQgYXR0ZW1wdCB3aXRoIGl0cyBmZW5jZSBjb250ZXh0LlxuICAgKiBAcGFyYW0ge1BlbmRpbmdMb2NhbEJhY2tncm91bmRKb2JBY2tub3dsZWRnZW1lbnQgJiB7ZXJyb3I6IFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+fX0gYXJncyAtIEZhaWxlZCBhY2tub3dsZWRnZW1lbnQuXG4gICAqIEByZXR1cm5zIHt2b2lkfSAtIE5vIHJldHVybiB2YWx1ZS5cbiAgICovXG4gIF9yZXBvcnRBY2tub3dsZWRnZW1lbnRFcnJvcih7YWNrbm93bGVkZ2VtZW50LCBlcnJvciwgaGFuZG9mZiwgam9ifSkge1xuICAgIHRoaXMuX3JlcG9ydEZyYW1ld29ya0Vycm9yKHtcbiAgICAgIGNvbnRleHQ6IHtcbiAgICAgICAgYWNrbm93bGVkZ2VtZW50VHlwZTogYWNrbm93bGVkZ2VtZW50LnR5cGUsXG4gICAgICAgIGhhbmRvZmZJZDogaGFuZG9mZi5oYW5kb2ZmSWQsXG4gICAgICAgIGpvYklkOiBqb2IuaWQsXG4gICAgICAgIGpvYk5hbWU6IGpvYi5qb2JOYW1lLFxuICAgICAgICB3b3JrZXJJZDogXCJsb2NhbFwiXG4gICAgICB9LFxuICAgICAgZXJyb3IsXG4gICAgICBzdGFnZTogXCJsb2NhbC1iYWNrZ3JvdW5kLWpvYnMtYWNrbm93bGVkZ2VtZW50XCJcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJlcG9ydHMgYW4gdW5leHBlY3RlZCBkaXNwYXRjaGVyIGZhaWx1cmUgdGhyb3VnaCBmcmFtZXdvcmsgY2hhbm5lbHMuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gVW5leHBlY3RlZCBmYWlsdXJlLlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gW2FyZ3MuY29udGV4dF0gLSBBZGRpdGlvbmFsIGZhaWx1cmUgY29udGV4dC5cbiAgICogQHBhcmFtIHtSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPn0gYXJncy5lcnJvciAtIFVuZXhwZWN0ZWQgZXJyb3IuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLnN0YWdlIC0gRGlzcGF0Y2hlciBzdGFnZS5cbiAgICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICAgKi9cbiAgX3JlcG9ydEZyYW1ld29ya0Vycm9yKHtjb250ZXh0ID0ge30sIGVycm9yLCBzdGFnZX0pIHtcbiAgICBjb25zdCBub3JtYWxpemVkRXJyb3IgPSBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IgOiBuZXcgRXJyb3IoU3RyaW5nKGVycm9yKSlcbiAgICBjb25zdCBwYXlsb2FkID0ge2NvbnRleHQ6IHsuLi5jb250ZXh0LCBzdGFnZX0sIGVycm9yOiBub3JtYWxpemVkRXJyb3J9XG4gICAgY29uc3QgZXJyb3JFdmVudHMgPSB0aGlzLmNvbmZpZ3VyYXRpb24uZ2V0RXJyb3JFdmVudHMoKVxuXG4gICAgZXJyb3JFdmVudHMuZW1pdChcImZyYW1ld29yay1lcnJvclwiLCBwYXlsb2FkKVxuICAgIGVycm9yRXZlbnRzLmVtaXQoXCJhbGwtZXJyb3JcIiwgey4uLnBheWxvYWQsIGVycm9yVHlwZTogXCJmcmFtZXdvcmstZXJyb3JcIn0pXG4gIH1cbn1cbiJdfQ==
|