footprintjs 9.4.0 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +2 -0
- package/README.md +14 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/lib/builder/FlowChartBuilder.js +15 -2
- package/dist/esm/lib/capture/envelope.js +187 -0
- package/dist/esm/lib/capture/index.js +2 -2
- package/dist/esm/lib/engine/handlers/ContinuationResolver.js +23 -4
- package/dist/esm/lib/engine/types.js +1 -1
- package/dist/esm/lib/observer-queue/deferredDispatcher.js +226 -0
- package/dist/esm/lib/observer-queue/flushDriver.js +163 -0
- package/dist/esm/lib/observer-queue/index.js +22 -0
- package/dist/esm/lib/observer-queue/mergedQueue.js +91 -0
- package/dist/esm/lib/observer-queue/ring.js +122 -0
- package/dist/esm/lib/recorder/CombinedRecorder.js +4 -4
- package/dist/esm/lib/recorder/invokeHook.js +38 -0
- package/dist/esm/lib/runner/DeferredObserverTier.js +366 -0
- package/dist/esm/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/esm/lib/runner/FlowChartExecutor.js +152 -22
- package/dist/esm/lib/runner/index.js +1 -1
- package/dist/esm/lib/scope/ScopeFacade.js +11 -8
- package/dist/index.js +1 -1
- package/dist/lib/builder/FlowChartBuilder.js +15 -2
- package/dist/lib/capture/envelope.js +192 -0
- package/dist/lib/capture/index.js +3 -3
- package/dist/lib/engine/handlers/ContinuationResolver.js +23 -4
- package/dist/lib/engine/types.js +1 -1
- package/dist/lib/observer-queue/deferredDispatcher.js +230 -0
- package/dist/lib/observer-queue/flushDriver.js +167 -0
- package/dist/lib/observer-queue/index.js +36 -0
- package/dist/lib/observer-queue/mergedQueue.js +95 -0
- package/dist/lib/observer-queue/ring.js +126 -0
- package/dist/lib/recorder/CombinedRecorder.js +8 -8
- package/dist/lib/recorder/invokeHook.js +42 -0
- package/dist/lib/runner/DeferredObserverTier.js +370 -0
- package/dist/lib/runner/ExecutionRuntime.js +1 -1
- package/dist/lib/runner/FlowChartExecutor.js +152 -22
- package/dist/lib/runner/index.js +1 -1
- package/dist/lib/scope/ScopeFacade.js +11 -8
- package/dist/types/index.d.ts +24 -0
- package/dist/types/lib/capture/envelope.d.ts +169 -0
- package/dist/types/lib/capture/index.d.ts +1 -1
- package/dist/types/lib/engine/handlers/ContinuationResolver.d.ts +15 -2
- package/dist/types/lib/engine/types.d.ts +3 -0
- package/dist/types/lib/observer-queue/deferredDispatcher.d.ts +169 -0
- package/dist/types/lib/observer-queue/flushDriver.d.ts +124 -0
- package/dist/types/lib/observer-queue/index.d.ts +25 -0
- package/dist/types/lib/observer-queue/mergedQueue.d.ts +85 -0
- package/dist/types/lib/observer-queue/ring.d.ts +99 -0
- package/dist/types/lib/recorder/CombinedRecorder.d.ts +36 -0
- package/dist/types/lib/recorder/invokeHook.d.ts +32 -0
- package/dist/types/lib/runner/DeferredObserverTier.d.ts +204 -0
- package/dist/types/lib/runner/ExecutionRuntime.d.ts +8 -0
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +49 -10
- package/dist/types/lib/runner/index.d.ts +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* observer-queue/deferredDispatcher.ts — RFC-001 Block 5: deferred delivery façade.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: capture → enqueue → (microtask) flush → invoke, with per-listener
|
|
5
|
+
* error isolation. Composes the whole pure pipeline: MergedQueue
|
|
6
|
+
* (Block 3, which captures via Block 1) + FlushDriver (Block 4) +
|
|
7
|
+
* a listener registry with timing/inflight accounting.
|
|
8
|
+
* Role: The object the engine wiring (Block 6) will hold. Producers call
|
|
9
|
+
* `capture()` (cheap, never throws, never blocks); listeners
|
|
10
|
+
* receive envelopes at the next checkpoint, "one beat behind".
|
|
11
|
+
* Pure module — zero engine imports.
|
|
12
|
+
*
|
|
13
|
+
* Delivery semantics (normative, RFC-001 §5 + amendments A2/A4):
|
|
14
|
+
* - Per-listener FIFO: every listener sees envelopes in seq order
|
|
15
|
+
* (invocation order; an async listener's COMPLETION order is its own
|
|
16
|
+
* concern) — EXCEPT under `'block'` overflow, where a refused enqueue
|
|
17
|
+
* is delivered inline and overtakes the queued backlog. `seq` always
|
|
18
|
+
* records true arrival order, so order-sensitive consumers re-sort;
|
|
19
|
+
* see the `'block'` caveat below.
|
|
20
|
+
* - Error isolation: a throwing listener (sync) or rejecting listener
|
|
21
|
+
* (async) never affects siblings or the producer. Both failure modes
|
|
22
|
+
* route to the injected `onError`; a throwing `onError` is itself
|
|
23
|
+
* swallowed.
|
|
24
|
+
* - The flush NEVER awaits a listener. Async continuations are tracked in
|
|
25
|
+
* an inflight set; `drain({ timeoutMs })` settles them
|
|
26
|
+
* (`Promise.allSettled` + deadline, shaped like `flushAllDetached`).
|
|
27
|
+
* - `'block'` overflow: a refused enqueue is delivered synchronously
|
|
28
|
+
* INLINE from `capture()` — re-introducing blocking delivery by the
|
|
29
|
+
* consumer's explicit choice. Ordering caveat (documented + tested): an
|
|
30
|
+
* inline event overtakes the queued backlog — `'block'` trades global
|
|
31
|
+
* ordering for zero loss and bounded memory. `seq` still tells the
|
|
32
|
+
* true arrival order.
|
|
33
|
+
* - Listener registry is idempotent by id (same id replaces, different
|
|
34
|
+
* ids coexist) — mirrors the repo-wide recorder ID contract. Stats
|
|
35
|
+
* accumulate per id across replacement; `removeListener` keeps the
|
|
36
|
+
* id's accumulated stats for post-run reports.
|
|
37
|
+
* - Events captured BEFORE any listener attaches stay queued — a listener
|
|
38
|
+
* attached before the next checkpoint still receives the backlog.
|
|
39
|
+
*
|
|
40
|
+
* Per-listener time accounting (amendment A2 — "name the hog"): cumulative
|
|
41
|
+
* `totalMs` and per-checkpoint `lastFlushMs` of SYNC time per listener id —
|
|
42
|
+
* the time that actually blocks the flush. An async listener's continuation
|
|
43
|
+
* time is intentionally not attributed (it does not block delivery).
|
|
44
|
+
*/
|
|
45
|
+
import { FlushDriver } from './flushDriver.js';
|
|
46
|
+
import { MergedQueue } from './mergedQueue.js';
|
|
47
|
+
const defaultNow = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
48
|
+
function isThenable(value) {
|
|
49
|
+
return typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
50
|
+
}
|
|
51
|
+
export class DeferredDispatcher {
|
|
52
|
+
queue;
|
|
53
|
+
driver;
|
|
54
|
+
listeners = new Map();
|
|
55
|
+
listenerStats = new Map();
|
|
56
|
+
/** Tracked async continuations — resolve `true` (ok) / `false` (failed). */
|
|
57
|
+
inflight = new Set();
|
|
58
|
+
onError;
|
|
59
|
+
now;
|
|
60
|
+
inlineDeliveries = 0;
|
|
61
|
+
constructor(opts) {
|
|
62
|
+
this.onError = opts?.onError;
|
|
63
|
+
this.now = opts?.now ?? defaultNow;
|
|
64
|
+
this.queue = new MergedQueue({
|
|
65
|
+
maxQueue: opts?.maxQueue,
|
|
66
|
+
overflow: opts?.overflow,
|
|
67
|
+
sampleEvery: opts?.sampleEvery,
|
|
68
|
+
capturePolicy: opts?.capturePolicy,
|
|
69
|
+
hooks: opts?.hooks,
|
|
70
|
+
});
|
|
71
|
+
this.driver = new FlushDriver({
|
|
72
|
+
depth: () => this.queue.depth,
|
|
73
|
+
processNext: () => this.deliverNext(),
|
|
74
|
+
flushBudgetMs: opts?.flushBudgetMs,
|
|
75
|
+
now: opts?.now,
|
|
76
|
+
schedule: opts?.schedule,
|
|
77
|
+
onFlushStart: () => {
|
|
78
|
+
for (const stats of this.listenerStats.values())
|
|
79
|
+
stats.lastFlushMs = 0;
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** Idempotent by id — same id replaces (stats continue), ids coexist. */
|
|
84
|
+
addListener(id, listener) {
|
|
85
|
+
this.listeners.set(id, listener);
|
|
86
|
+
if (!this.listenerStats.has(id)) {
|
|
87
|
+
this.listenerStats.set(id, { events: 0, totalMs: 0, lastFlushMs: 0 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Stop delivering to `id`. Accumulated stats are kept for reports. */
|
|
91
|
+
removeListener(id) {
|
|
92
|
+
this.listeners.delete(id);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Producer entry point: capture the event (seq-stamped, payload per
|
|
96
|
+
* policy) and stage it for the next checkpoint. Cheap; NEVER throws;
|
|
97
|
+
* never blocks — except under `'block'` overflow, where a refused
|
|
98
|
+
* enqueue is delivered synchronously inline (explicit consumer choice).
|
|
99
|
+
*/
|
|
100
|
+
capture(input, policy) {
|
|
101
|
+
const result = this.queue.enqueue(input, policy);
|
|
102
|
+
if (result.outcome === 'queued') {
|
|
103
|
+
this.driver.arm();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (result.outcome === 'inline') {
|
|
107
|
+
this.inlineDeliveries += 1;
|
|
108
|
+
this.deliver(result.envelope);
|
|
109
|
+
}
|
|
110
|
+
// 'dropped': counted by the queue; loss surfaces in stats + seq gaps.
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Terminal flush — synchronously deliver everything queued (end of run /
|
|
114
|
+
* shutdown). Async listener continuations are NOT awaited; follow with
|
|
115
|
+
* `drain()` for that.
|
|
116
|
+
*/
|
|
117
|
+
flushNow(opts) {
|
|
118
|
+
return this.driver.flushSync(opts);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Flush the backlog, then settle all inflight async continuations —
|
|
122
|
+
* `Promise.allSettled` under a deadline, shaped like `flushAllDetached`.
|
|
123
|
+
* Loops while continuations spawn new captures, until quiescent or the
|
|
124
|
+
* deadline expires.
|
|
125
|
+
*/
|
|
126
|
+
async drain(opts) {
|
|
127
|
+
const timeoutMs = opts?.timeoutMs ?? 30_000;
|
|
128
|
+
const startedAt = Date.now();
|
|
129
|
+
let done = 0;
|
|
130
|
+
let failed = 0;
|
|
131
|
+
this.flushNow();
|
|
132
|
+
while (this.inflight.size > 0) {
|
|
133
|
+
const remainingMs = timeoutMs - (Date.now() - startedAt);
|
|
134
|
+
if (remainingMs <= 0)
|
|
135
|
+
return { done, failed, pending: this.inflight.size + this.queue.depth };
|
|
136
|
+
const batch = [...this.inflight];
|
|
137
|
+
let timerId;
|
|
138
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
139
|
+
timerId = setTimeout(() => resolve('__drain_timeout__'), remainingMs);
|
|
140
|
+
});
|
|
141
|
+
const settled = await Promise.race([Promise.allSettled(batch), timeoutPromise]);
|
|
142
|
+
if (timerId !== undefined)
|
|
143
|
+
clearTimeout(timerId);
|
|
144
|
+
if (settled === '__drain_timeout__') {
|
|
145
|
+
return { done, failed, pending: this.inflight.size + this.queue.depth };
|
|
146
|
+
}
|
|
147
|
+
for (const r of settled) {
|
|
148
|
+
// Tracked promises never reject — they resolve true (ok) / false.
|
|
149
|
+
if (r.status === 'fulfilled' && r.value === false)
|
|
150
|
+
failed += 1;
|
|
151
|
+
else
|
|
152
|
+
done += 1;
|
|
153
|
+
}
|
|
154
|
+
// Continuations may have captured more events — flush and re-check.
|
|
155
|
+
this.flushNow();
|
|
156
|
+
}
|
|
157
|
+
return { done, failed, pending: this.queue.depth };
|
|
158
|
+
}
|
|
159
|
+
/** A4 — the stats object Block 9 consumes. Pure getter, fresh snapshot. */
|
|
160
|
+
getStats() {
|
|
161
|
+
const counters = this.queue.getCounters();
|
|
162
|
+
const driverStats = this.driver.getStats();
|
|
163
|
+
const perListener = {};
|
|
164
|
+
for (const [id, stats] of this.listenerStats) {
|
|
165
|
+
perListener[id] = { events: stats.events, totalMs: stats.totalMs, lastFlushMs: stats.lastFlushMs };
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
depth: this.queue.depth,
|
|
169
|
+
drops: counters.drops,
|
|
170
|
+
flushes: driverStats.flushes,
|
|
171
|
+
budgetExhausted: driverStats.budgetExhausted,
|
|
172
|
+
p95FlushMs: driverStats.p95FlushMs,
|
|
173
|
+
inlineDeliveries: this.inlineDeliveries,
|
|
174
|
+
inflight: this.inflight.size,
|
|
175
|
+
perListener,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
deliverNext() {
|
|
179
|
+
const envelope = this.queue.shift();
|
|
180
|
+
if (envelope === undefined)
|
|
181
|
+
return;
|
|
182
|
+
this.deliver(envelope);
|
|
183
|
+
}
|
|
184
|
+
/** Invoke every listener with full error isolation + time accounting. */
|
|
185
|
+
deliver(envelope) {
|
|
186
|
+
for (const [id, listener] of this.listeners) {
|
|
187
|
+
const stats = this.listenerStats.get(id);
|
|
188
|
+
const start = this.now();
|
|
189
|
+
try {
|
|
190
|
+
const result = listener(envelope);
|
|
191
|
+
if (isThenable(result))
|
|
192
|
+
this.track(result, id, envelope);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
this.safeOnError(error, { listenerId: id, envelope, phase: 'sync' });
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
const elapsed = this.now() - start;
|
|
199
|
+
stats.events += 1;
|
|
200
|
+
stats.totalMs += elapsed;
|
|
201
|
+
stats.lastFlushMs += elapsed;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Track an async continuation; route its rejection; never reject. */
|
|
206
|
+
track(promise, listenerId, envelope) {
|
|
207
|
+
const tracked = promise.then(() => true, (error) => {
|
|
208
|
+
this.safeOnError(error, { listenerId, envelope, phase: 'async' });
|
|
209
|
+
return false;
|
|
210
|
+
});
|
|
211
|
+
this.inflight.add(tracked);
|
|
212
|
+
// Self-cleanup — `tracked` never rejects, so this chain cannot float an
|
|
213
|
+
// unhandled rejection.
|
|
214
|
+
tracked.then(() => this.inflight.delete(tracked));
|
|
215
|
+
}
|
|
216
|
+
/** The error sink must never become an error source. */
|
|
217
|
+
safeOnError(error, context) {
|
|
218
|
+
try {
|
|
219
|
+
this.onError?.(error, context);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Swallow — isolation is absolute.
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVmZXJyZWREaXNwYXRjaGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9vYnNlcnZlci1xdWV1ZS9kZWZlcnJlZERpc3BhdGNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQ0c7QUFHSCxPQUFPLEVBQXdCLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3JFLE9BQU8sRUFBcUIsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUF3RmxFLE1BQU0sVUFBVSxHQUFHLEdBQVcsRUFBRSxDQUFDLENBQUMsT0FBTyxXQUFXLEtBQUssV0FBVyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBRXZHLFNBQVMsVUFBVSxDQUFDLEtBQTJCO0lBQzdDLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksT0FBUSxLQUF1QixDQUFDLElBQUksS0FBSyxVQUFVLENBQUM7QUFDNUcsQ0FBQztBQUVELE1BQU0sT0FBTyxrQkFBa0I7SUFDWixLQUFLLENBQWM7SUFDbkIsTUFBTSxDQUFjO0lBQ3BCLFNBQVMsR0FBRyxJQUFJLEdBQUcsRUFBNEIsQ0FBQztJQUNoRCxhQUFhLEdBQUcsSUFBSSxHQUFHLEVBQWdDLENBQUM7SUFDekUsNEVBQTRFO0lBQzNELFFBQVEsR0FBRyxJQUFJLEdBQUcsRUFBb0IsQ0FBQztJQUN2QyxPQUFPLENBQXdCO0lBQy9CLEdBQUcsQ0FBZTtJQUMzQixnQkFBZ0IsR0FBRyxDQUFDLENBQUM7SUFFN0IsWUFBWSxJQUFnQztRQUMxQyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksRUFBRSxPQUFPLENBQUM7UUFDN0IsSUFBSSxDQUFDLEdBQUcsR0FBRyxJQUFJLEVB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QUFDLE9BQXNCLEVBQUUsVUFBa0IsRUFBRSxRQUF5QjtRQUNqRixNQUFNLE9BQU8sR0FBcUIsT0FBTyxDQUFDLElBQUksQ0FDNUMsR0FBRyxFQUFFLENBQUMsSUFBSSxFQUNWLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDUixJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDbEUsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDLENBQ0YsQ0FBQztRQUNGLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzNCLHdFQUF3RTtRQUN4RSx1QkFBdUI7UUFDdkIsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFFRCx3REFBd0Q7SUFDaEQsV0FBVyxDQUFDLEtBQWMsRUFBRSxPQUE2QjtRQUMvRCxJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ2pDLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxtQ0FBbUM7UUFDckMsQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogb2JzZXJ2ZXItcXVldWUvZGVmZXJyZWREaXNwYXRjaGVyLnRzIOKAlCBSRkMtMDAxIEJsb2NrIDU6IGRlZmVycmVkIGRlbGl2ZXJ5IGZhw6dhZGUuXG4gKlxuICogUGF0dGVybjogIGNhcHR1cmUg4oaSIGVucXVldWUg4oaSIChtaWNyb3Rhc2spIGZsdXNoIOKGkiBpbnZva2UsIHdpdGggcGVyLWxpc3RlbmVyXG4gKiAgICAgICAgICAgZXJyb3IgaXNvbGF0aW9uLiBDb21wb3NlcyB0aGUgd2hvbGUgcHVyZSBwaXBlbGluZTogTWVyZ2VkUXVldWVcbiAqICAgICAgICAgICAoQmxvY2sgMywgd2hpY2ggY2FwdHVyZXMgdmlhIEJsb2NrIDEpICsgRmx1c2hEcml2ZXIgKEJsb2NrIDQpICtcbiAqICAgICAgICAgICBhIGxpc3RlbmVyIHJlZ2lzdHJ5IHdpdGggdGltaW5nL2luZmxpZ2h0IGFjY291bnRpbmcuXG4gKiBSb2xlOiAgICAgVGhlIG9iamVjdCB0aGUgZW5naW5lIHdpcmluZyAoQmxvY2sgNikgd2lsbCBob2xkLiBQcm9kdWNlcnMgY2FsbFxuICogICAgICAgICAgIGBjYXB0dXJlKClgIChjaGVhcCwgbmV2ZXIgdGhyb3dzLCBuZXZlciBibG9ja3MpOyBsaXN0ZW5lcnNcbiAqICAgICAgICAgICByZWNlaXZlIGVudmVsb3BlcyBhdCB0aGUgbmV4dCBjaGVja3BvaW50LCBcIm9uZSBiZWF0IGJlaGluZFwiLlxuICogICAgICAgICAgIFB1cmUgbW9kdWxlIOKAlCB6ZXJvIGVuZ2luZSBpbXBvcnRzLlxuICpcbiAqIERlbGl2ZXJ5IHNlbWFudGljcyAobm9ybWF0aXZlLCBSRkMtMDAxIMKnNSArIGFtZW5kbWVudHMgQTIvQTQpOlxuICogICAtIFBlci1saXN0ZW5lciBGSUZPOiBldmVyeSBsaXN0ZW5lciBzZWVzIGVudmVsb3BlcyBpbiBzZXEgb3JkZXJcbiAqICAgICAoaW52b2NhdGlvbiBvcmRlcjsgYW4gYXN5bmMgbGlzdGVuZXIncyBDT01QTEVUSU9OIG9yZGVyIGlzIGl0cyBvd25cbiAqICAgICBjb25jZXJuKSDigJQgRVhDRVBUIHVuZGVyIGAnYmxvY2snYCBvdmVyZmxvdywgd2hlcmUgYSByZWZ1c2VkIGVucXVldWVcbiAqICAgICBpcyBkZWxpdmVyZWQgaW5saW5lIGFuZCBvdmVydGFrZXMgdGhlIHF1ZXVlZCBiYWNrbG9nLiBgc2VxYCBhbHdheXNcbiAqICAgICByZWNvcmRzIHRydWUgYXJyaXZhbCBvcmRlciwgc28gb3JkZXItc2Vuc2l0aXZlIGNvbnN1bWVycyByZS1zb3J0O1xuICogICAgIHNlZSB0aGUgYCdibG9jaydgIGNhdmVhdCBiZWxvdy5cbiAqICAgLSBFcnJvciBpc29sYXRpb246IGEgdGhyb3dpbmcgbGlzdGVuZXIgKHN5bmMpIG9yIHJlamVjdGluZyBsaXN0ZW5lclxuICogICAgIChhc3luYykgbmV2ZXIgYWZmZWN0cyBzaWJsaW5ncyBvciB0aGUgcHJvZHVjZXIuIEJvdGggZmFpbHVyZSBtb2Rlc1xuICogICAgIHJvdXRlIHRvIHRoZSBpbmplY3RlZCBgb25FcnJvcmA7IGEgdGhyb3dpbmcgYG9uRXJyb3JgIGlzIGl0c2VsZlxuICogICAgIHN3YWxsb3dlZC5cbiAqICAgLSBUaGUgZmx1c2ggTkVWRVIgYXdhaXRzIGEgbGlzdGVuZXIuIEFzeW5jIGNvbnRpbnVhdGlvbnMgYXJlIHRyYWNrZWQgaW5cbiAqICAgICBhbiBpbmZsaWdodCBzZXQ7IGBkcmFpbih7IHRpbWVvdXRNcyB9KWAgc2V0dGxlcyB0aGVtXG4gKiAgICAgKGBQcm9taXNlLmFsbFNldHRsZWRgICsgZGVhZGxpbmUsIHNoYXBlZCBsaWtlIGBmbHVzaEFsbERldGFjaGVkYCkuXG4gKiAgIC0gYCdibG9jaydgIG92ZXJmbG93OiBhIHJlZnVzZWQgZW5xdWV1ZSBpcyBkZWxpdmVyZWQgc3luY2hyb25vdXNseVxuICogICAgIElOTElORSBmcm9tIGBjYXB0dXJlKClgIOKAlCByZS1pbnRyb2R1Y2luZyBibG9ja2luZyBkZWxpdmVyeSBieSB0aGVcbiAqICAgICBjb25zdW1lcidzIGV4cGxpY2l0IGNob2ljZS4gT3JkZXJpbmcgY2F2ZWF0IChkb2N1bWVudGVkICsgdGVzdGVkKTogYW5cbiAqICAgICBpbmxpbmUgZXZlbnQgb3ZlcnRha2VzIHRoZSBxdWV1ZWQgYmFja2xvZyDigJQgYCdibG9jaydgIHRyYWRlcyBnbG9iYWxcbiAqICAgICBvcmRlcmluZyBmb3IgemVybyBsb3NzIGFuZCBib3VuZGVkIG1lbW9yeS4gYHNlcWAgc3RpbGwgdGVsbHMgdGhlXG4gKiAgICAgdHJ1ZSBhcnJpdmFsIG9yZGVyLlxuICogICAtIExpc3RlbmVyIHJlZ2lzdHJ5IGlzIGlkZW1wb3RlbnQgYnkgaWQgKHNhbWUgaWQgcmVwbGFjZXMsIGRpZmZlcmVudFxuICogICAgIGlkcyBjb2V4aXN0KSDigJQgbWlycm9ycyB0aGUgcmVwby13aWRlIHJlY29yZGVyIElEIGNvbnRyYWN0LiBTdGF0c1xuICogICAgIGFjY3VtdWxhdGUgcGVyIGlkIGFjcm9zcyByZXBsYWNlbWVudDsgYHJlbW92ZUxpc3RlbmVyYCBrZWVwcyB0aGVcbiAqICAgICBpZCdzIGFjY3VtdWxhdGVkIHN0YXRzIGZvciBwb3N0LXJ1biByZXBvcnRzLlxuICogICAtIEV2ZW50cyBjYXB0dXJlZCBCRUZPUkUgYW55IGxpc3RlbmVyIGF0dGFjaGVzIHN0YXkgcXVldWVkIOKAlCBhIGxpc3RlbmVyXG4gKiAgICAgYXR0YWNoZWQgYmVmb3JlIHRoZSBuZXh0IGNoZWNrcG9pbnQgc3RpbGwgcmVjZWl2ZXMgdGhlIGJhY2tsb2cuXG4gKlxuICogUGVyLWxpc3RlbmVyIHRpbWUgYWNjb3VudGluZyAoYW1lbmRtZW50IEEyIOKAlCBcIm5hbWUgdGhlIGhvZ1wiKTogY3VtdWxhdGl2ZVxuICogYHRvdGFsTXNgIGFuZCBwZXItY2hlY2twb2ludCBgbGFzdEZsdXNoTXNgIG9mIFNZTkMgdGltZSBwZXIgbGlzdGVuZXIgaWQg4oCUXG4gKiB0aGUgdGltZSB0aGF0IGFjdHVhbGx5IGJsb2NrcyB0aGUgZmx1c2guIEFuIGFzeW5jIGxpc3RlbmVyJ3MgY29udGludWF0aW9uXG4gKiB0aW1lIGlzIGludGVudGlvbmFsbHkgbm90IGF0dHJpYnV0ZWQgKGl0IGRvZXMgbm90IGJsb2NrIGRlbGl2ZXJ5KS5cbiAqL1xuXG5pbXBvcnQgeyB0eXBlIENhcHR1cmVFbnZlbG9wZSwgdHlwZSBDYXB0dXJlSG9va3MsIHR5cGUgQ2FwdHVyZVBvbGljeSB9IGZyb20gJy4uL2NhcHR1cmUvZW52ZWxvcGUuanMnO1xuaW1wb3J0IHsgdHlwZSBGbHVzaFN5bmNSZXN1bHQsIEZsdXNoRHJpdmVyIH0gZnJvbSAnLi9mbHVzaERyaXZlci5qcyc7XG5pbXBvcnQgeyB0eXBlIEVucXVldWVJbnB1dCwgTWVyZ2VkUXVldWUgfSBmcm9tICcuL21lcmdlZFF1ZXVlLmpzJztcbmltcG9ydCB7IHR5cGUgT3ZlcmZsb3dQb2xpY3kgfSBmcm9tICcuL3JpbmcuanMnO1xuXG4vKipcbiAqIE9uZSBkZWZlcnJlZCBvYnNlcnZlci4gTWF5IHJldHVybiBhIFByb21pc2Ug4oCUIHRoZSBkaXNwYXRjaGVyIHRyYWNrcyBpdCBpblxuICogdGhlIGluZmxpZ2h0IHNldCBidXQgTkVWRVIgYXdhaXRzIGl0IGR1cmluZyBhIGZsdXNoLlxuICovXG5leHBvcnQgdHlwZSBEZWZlcnJlZExpc3RlbmVyID0gKGVudmVsb3BlOiBDYXB0dXJlRW52ZWxvcGUpID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+O1xuXG5leHBvcnQgaW50ZXJmYWNlIERpc3BhdGNoRXJyb3JDb250ZXh0IHtcbiAgcmVhZG9ubHkgbGlzdGVuZXJJZDogc3RyaW5nO1xuICByZWFkb25seSBlbnZlbG9wZTogQ2FwdHVyZUVudmVsb3BlO1xuICAvKiogYCdzeW5jJ2AgPSBsaXN0ZW5lciB0aHJldzsgYCdhc3luYydgID0gcmV0dXJuZWQgcHJvbWlzZSByZWplY3RlZC4gKi9cbiAgcmVhZG9ubHkgcGhhc2U6ICdzeW5jJyB8ICdhc3luYyc7XG59XG5cbi8qKiBJbmplY3RlZCBlcnJvciBzaW5rIOKAlCB0aGUgd2lyaW5nIGxheWVyIHJvdXRlcyB0aGVzZSAoQmxvY2sgNikuICovXG5leHBvcnQgdHlwZSBEaXNwYXRjaEVycm9ySGFuZGxlciA9IChlcnJvcjogdW5rbm93biwgY29udGV4dDogRGlzcGF0Y2hFcnJvckNvbnRleHQpID0+IHZvaWQ7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRGVmZXJyZWREaXNwYXRjaGVyT3B0aW9ucyB7XG4gIC8qKiBRdWV1ZSBib3VuZCDigJQgZGVmYXVsdCAxMCAwMDAgKHNlZSBgTWVyZ2VkUXVldWVgKS4gKi9cbiAgcmVhZG9ubHkgbWF4UXVldWU/OiBudW1iZXI7XG4gIC8qKiBPdmVyZmxvdyBwb2xpY3kg4oCUIGRlZmF1bHQgYCdkcm9wLW9sZGVzdCdgLiAqL1xuICByZWFkb25seSBvdmVyZmxvdz86IE92ZXJmbG93UG9saWN5O1xuICAvKiogYCdzYW1wbGUnYCBvdmVyZmxvdyBvbmx5IOKAlCBhZG1pdCAxIGluIHRoaXMgbWFueSBzYXR1cmF0ZWQgYXJyaXZhbHMuICovXG4gIHJlYWRvbmx5IHNhbXBsZUV2ZXJ5PzogbnVtYmVyO1xuICAvKiogRGVmYXVsdCBjYXB0dXJlIHBvbGljeSDigJQgZGVmYXVsdCBgJ3N1bW1hcnknYC4gKi9cbiAgcmVhZG9ubHkgY2FwdHVyZVBvbGljeT86IENhcHR1cmVQb2xpY3k7XG4gIC8qKiBQZXItZmx1c2ggdGltZSBidWRnZXQsIG1zIChBMSkg4oCUIGRlZmF1bHQgMjsgYEluZmluaXR5YCA9IGZ1bGwgZHJhaW4uICovXG4gIHJlYWRvbmx5IGZsdXNoQnVkZ2V0TXM/OiBudW1iZXI7XG4gIC8qKiBMaXN0ZW5lci1mYWlsdXJlIHNpbmsuIE5vIGRlZmF1bHQg4oCUIHdpdGhvdXQgaXQsIGZhaWx1cmVzIGFyZSBzaWxlbnQuICovXG4gIHJlYWRvbmx5IG9uRXJyb3I/OiBEaXNwYXRjaEVycm9ySGFuZGxlcjtcbiAgLyoqIENhcHR1cmUgc2VhbXMgKGRldi13YXJuLCBjYXB0dXJlZEF0IGNsb2NrKSDigJQgc2VlIGBDYXB0dXJlSG9va3NgLiAqL1xuICByZWFkb25seSBob29rcz86IENhcHR1cmVIb29rcztcbiAgLyoqIFRpbWluZyBjbG9jayBmb3IgYnVkZ2V0ICsgcGVyLWxpc3RlbmVyIGFjY291bnRpbmcuIEluamVjdGFibGUuICovXG4gIHJlYWRvbmx5IG5vdz86ICgpID0+IG51bWJlcjtcbiAgLyoqIENoZWNrcG9pbnQgcHJpbWl0aXZlIOKAlCBkZWZhdWx0IGBxdWV1ZU1pY3JvdGFza2AuIEluamVjdGFibGUuICovXG4gIHJlYWRvbmx5IHNjaGVkdWxlPzogKGNiOiAoKSA9PiB2b2lkKSA9PiB2b2lkO1xufVxuXG4vKiogUGVyLWxpc3RlbmVyIGFjY291bnRpbmcgKEEyL0E0KS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTGlzdGVuZXJTdGF0cyB7XG4gIC8qKiBFbnZlbG9wZXMgZGVsaXZlcmVkIChpbnZvY2F0aW9ucywgaW5jbHVkaW5nIG9uZXMgdGhhdCB0aHJldykuICovXG4gIHJlYWRvbmx5IGV2ZW50czogbnVtYmVyO1xuICAvKiogQ3VtdWxhdGl2ZSBzeW5jIGRlbGl2ZXJ5IHRpbWUsIG1zLiAqL1xuICByZWFkb25seSB0b3RhbE1zOiBudW1iZXI7XG4gIC8qKiBTeW5jIGRlbGl2ZXJ5IHRpbWUgc2luY2UgdGhlIGxhc3QgZmx1c2ggc3RhcnRlZCwgbXMuICovXG4gIHJlYWRvbmx5IGxhc3RGbHVzaE1zOiBudW1iZXI7XG59XG5cbi8qKiBUaGUgQmxvY2sgOSBvYnNlcnZhYmlsaXR5IHN1cmZhY2UgKGFtZW5kbWVudCBBNCkg4oCUIHB1cmUgZ2V0dGVyLiAqL1xuZXhwb3J0IGludGVyZmFjZSBEaXNwYXRjaGVyU3RhdHMge1xuICAvKiogQ3VycmVudCBiYWNrbG9nLiAqL1xuICByZWFkb25seSBkZXB0aDogbnVtYmVyO1xuICAvKiogRXZlbnRzIExPU1QgKG92ZXJmbG93KSDigJQgbmV2ZXIgc2lsZW50OyBhbHNvIHZpc2libGUgYXMgc2VxIGdhcHMuICovXG4gIHJlYWRvbmx5IGRyb3BzOiBudW1iZXI7XG4gIC8qKiBDb21wbGV0ZWQgY2hlY2twb2ludCBmbHVzaGVzLiAqL1xuICByZWFkb25seSBmbHVzaGVzOiBudW1iZXI7XG4gIC8qKiBGbHVzaGVzIGN1dCBzaG9ydCBieSBgZmx1c2hCdWRnZXRNc2AgKEExKS4gKi9cbiAgcmVhZG9ubHkgYnVkZ2V0RXhoYXVzdGVkOiBudW1iZXI7XG4gIC8qKiBwOTUgZmx1c2ggZHVyYXRpb24sIG1zIChyb2xsaW5nIHdpbmRvdykuICovXG4gIHJlYWRvbmx5IHA5NUZsdXNoTXM6IG51bWJlcjtcbiAgLyoqIGAnYmxvY2snYC1wb2xpY3kgcmVmdXNhbHMgZGVsaXZlcmVkIHN5bmNocm9ub3VzbHkgaW5saW5lLiAqL1xuICByZWFkb25seSBpbmxpbmVEZWxpdmVyaWVzOiBudW1iZXI7XG4gIC8qKiBBc3luYyBsaXN0ZW5lciBjb250aW51YXRpb25zIG5vdCB5ZXQgc2V0dGxlZC4gKi9cbiAgcmVhZG9ubHkgaW5mbGlnaHQ6IG51bWJlcjtcbiAgLyoqIFBlci1saXN0ZW5lciB0aW1lIGFjY291bnRpbmcg4oCUIFwibmFtZSB0aGUgaG9nXCIgKEEyKS4gKi9cbiAgcmVhZG9ubHkgcGVyTGlzdGVuZXI6IFJlYWRvbmx5PFJlY29yZDxzdHJpbmcsIExpc3RlbmVyU3RhdHM+Pjtcbn1cblxuLyoqIFJlc3VsdCBvZiB7QGxpbmsgRGVmZXJyZWREaXNwYXRjaGVyLmRyYWlufSDigJQgYGZsdXNoQWxsRGV0YWNoZWRgIHNoYXBlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBEcmFpblJlc3VsdCB7XG4gIC8qKiBBc3luYyBjb250aW51YXRpb25zIHNlZW4gc2V0dGxpbmcgZnVsZmlsbGVkLiBCZXN0LWVmZm9ydCBjb3VudCDigJQgYVxuICAgKiAgY29udGludWF0aW9uIHRoYXQgc2V0dGxlcyBiZXR3ZWVuIGNoZWNrcyBpcyBkcmFpbmVkIGJ1dCBtYXkgbm90IGJlXG4gICAqICBjb3VudGVkIChzYW1lIHNlbWFudGljcyBhcyBgZmx1c2hBbGxEZXRhY2hlZGApLiAqL1xuICByZWFkb25seSBkb25lOiBudW1iZXI7XG4gIC8qKiBDb250aW51YXRpb25zIHdob3NlIGxpc3RlbmVyIHByb21pc2UgcmVqZWN0ZWQgKHJvdXRlZCB0byBvbkVycm9yKS4gKi9cbiAgcmVhZG9ubHkgZmFpbGVkOiBudW1iZXI7XG4gIC8qKiBTdGlsbCBpbiBmbGlnaHQgKG9yIHF1ZXVlZCkgd2hlbiB0aGUgZGVhZGxpbmUgZXhwaXJlZC4gYDBgID0gZHJhaW5lZC4gKi9cbiAgcmVhZG9ubHkgcGVuZGluZzogbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgTXV0YWJsZUxpc3RlbmVyU3RhdHMge1xuICBldmVudHM6IG51bWJlcjtcbiAgdG90YWxNczogbnVtYmVyO1xuICBsYXN0Rmx1c2hNczogbnVtYmVyO1xufVxuXG5jb25zdCBkZWZhdWx0Tm93ID0gKCk6IG51bWJlciA9PiAodHlwZW9mIHBlcmZvcm1hbmNlICE9PSAndW5kZWZpbmVkJyA/IHBlcmZvcm1hbmNlLm5vdygpIDogRGF0ZS5ub3coKSk7XG5cbmZ1bmN0aW9uIGlzVGhlbmFibGUodmFsdWU6IHZvaWQgfCBQcm9taXNlPHZvaWQ+KTogdmFsdWUgaXMgUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmIHZhbHVlICE9PSBudWxsICYmIHR5cGVvZiAodmFsdWUgYXMgUHJvbWlzZTx2b2lkPikudGhlbiA9PT0gJ2Z1bmN0aW9uJztcbn1cblxuZXhwb3J0IGNsYXNzIERlZmVycmVkRGlzcGF0Y2hlciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgcXVldWU6IE1lcmdlZFF1ZXVlO1xuICBwcml2YXRlIHJlYWRvbmx5IGRyaXZlcjogRmx1c2hEcml2ZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbGlzdGVuZXJzID0gbmV3IE1hcDxzdHJpbmcsIERlZmVycmVkTGlzdGVuZXI+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgbGlzdGVuZXJTdGF0cyA9IG5ldyBNYXA8c3RyaW5nLCBNdXRhYmxlTGlzdGVuZXJTdGF0cz4oKTtcbiAgLyoqIFRyYWNrZWQgYXN5bmMgY29udGludWF0aW9ucyDigJQgcmVzb2x2ZSBgdHJ1ZWAgKG9rKSAvIGBmYWxzZWAgKGZhaWxlZCkuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaW5mbGlnaHQgPSBuZXcgU2V0PFByb21pc2U8Ym9vbGVhbj4+KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgb25FcnJvcj86IERpc3BhdGNoRXJyb3JIYW5kbGVyO1xuICBwcml2YXRlIHJlYWRvbmx5IG5vdzogKCkgPT4gbnVtYmVyO1xuICBwcml2YXRlIGlubGluZURlbGl2ZXJpZXMgPSAwO1xuXG4gIGNvbnN0cnVjdG9yKG9wdHM/OiBEZWZlcnJlZERpc3BhdGNoZXJPcHRpb25zKSB7XG4gICAgdGhpcy5vbkVycm9yID0gb3B0cz8ub25FcnJvcjtcbiAgICB0aGlzLm5vdyA9IG9wdHM/Lm5vdyA/PyBkZWZhdWx0Tm93O1xuICAgIHRoaXMucXVldWUgPSBuZXcgTWVyZ2VkUXVldWUoe1xuICAgICAgbWF4UXVldWU6IG9wdHM/Lm1heFF1ZXVlLFxuICAgICAgb3ZlcmZsb3c6IG9wdHM/Lm92ZXJmbG93LFxuICAgICAgc2FtcGxlRXZlcnk6IG9wdHM/LnNhbXBsZUV2ZXJ5LFxuICAgICAgY2FwdHVyZVBvbGljeTogb3B0cz8uY2FwdHVyZVBvbGljeSxcbiAgICAgIGhvb2tzOiBvcHRzPy5ob29rcyxcbiAgICB9KTtcbiAgICB0aGlzLmRyaXZlciA9IG5ldyBGbHVzaERyaXZlcih7XG4gICAgICBkZXB0aDogKCkgPT4gdGhpcy5xdWV1ZS5kZXB0aCxcbiAgICAgIHByb2Nlc3NOZXh0OiAoKSA9PiB0aGlzLmRlbGl2ZXJOZXh0KCksXG4gICAgICBmbHVzaEJ1ZGdldE1zOiBvcHRzPy5mbHVzaEJ1ZGdldE1zLFxuICAgICAgbm93OiBvcHRzPy5ub3csXG4gICAgICBzY2hlZHVsZTogb3B0cz8uc2NoZWR1bGUsXG4gICAgICBvbkZsdXNoU3RhcnQ6ICgpID0+IHtcbiAgICAgICAgZm9yIChjb25zdCBzdGF0cyBvZiB0aGlzLmxpc3RlbmVyU3RhdHMudmFsdWVzKCkpIHN0YXRzLmxhc3RGbHVzaE1zID0gMDtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvKiogSWRlbXBvdGVudCBieSBpZCDigJQgc2FtZSBpZCByZXBsYWNlcyAoc3RhdHMgY29udGludWUpLCBpZHMgY29leGlzdC4gKi9cbiAgYWRkTGlzdGVuZXIoaWQ6IHN0cmluZywgbGlzdGVuZXI6IERlZmVycmVkTGlzdGVuZXIpOiB2b2lkIHtcbiAgICB0aGlzLmxpc3RlbmVycy5zZXQoaWQsIGxpc3RlbmVyKTtcbiAgICBpZiAoIXRoaXMubGlzdGVuZXJTdGF0cy5oYXMoaWQpKSB7XG4gICAgICB0aGlzLmxpc3RlbmVyU3RhdHMuc2V0KGlkLCB7IGV2ZW50czogMCwgdG90YWxNczogMCwgbGFzdEZsdXNoTXM6IDAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqIFN0b3AgZGVsaXZlcmluZyB0byBgaWRgLiBBY2N1bXVsYXRlZCBzdGF0cyBhcmUga2VwdCBmb3IgcmVwb3J0cy4gKi9cbiAgcmVtb3ZlTGlzdGVuZXIoaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMubGlzdGVuZXJzLmRlbGV0ZShpZCk7XG4gIH1cblxuICAvKipcbiAgICogUHJvZHVjZXIgZW50cnkgcG9pbnQ6IGNhcHR1cmUgdGhlIGV2ZW50IChzZXEtc3RhbXBlZCwgcGF5bG9hZCBwZXJcbiAgICogcG9saWN5KSBhbmQgc3RhZ2UgaXQgZm9yIHRoZSBuZXh0IGNoZWNrcG9pbnQuIENoZWFwOyBORVZFUiB0aHJvd3M7XG4gICAqIG5ldmVyIGJsb2NrcyDigJQgZXhjZXB0IHVuZGVyIGAnYmxvY2snYCBvdmVyZmxvdywgd2hlcmUgYSByZWZ1c2VkXG4gICAqIGVucXVldWUgaXMgZGVsaXZlcmVkIHN5bmNocm9ub3VzbHkgaW5saW5lIChleHBsaWNpdCBjb25zdW1lciBjaG9pY2UpLlxuICAgKi9cbiAgY2FwdHVyZShpbnB1dDogRW5xdWV1ZUlucHV0LCBwb2xpY3k/OiBDYXB0dXJlUG9saWN5KTogdm9pZCB7XG4gICAgY29uc3QgcmVzdWx0ID0gdGhpcy5xdWV1ZS5lbnF1ZXVlKGlucHV0LCBwb2xpY3kpO1xuICAgIGlmIChyZXN1bHQub3V0Y29tZSA9PT0gJ3F1ZXVlZCcpIHtcbiAgICAgIHRoaXMuZHJpdmVyLmFybSgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAocmVzdWx0Lm91dGNvbWUgPT09ICdpbmxpbmUnKSB7XG4gICAgICB0aGlzLmlubGluZURlbGl2ZXJpZXMgKz0gMTtcbiAgICAgIHRoaXMuZGVsaXZlcihyZXN1bHQuZW52ZWxvcGUpO1xuICAgIH1cbiAgICAvLyAnZHJvcHBlZCc6IGNvdW50ZWQgYnkgdGhlIHF1ZXVlOyBsb3NzIHN1cmZhY2VzIGluIHN0YXRzICsgc2VxIGdhcHMuXG4gIH1cblxuICAvKipcbiAgICogVGVybWluYWwgZmx1c2gg4oCUIHN5bmNocm9ub3VzbHkgZGVsaXZlciBldmVyeXRoaW5nIHF1ZXVlZCAoZW5kIG9mIHJ1biAvXG4gICAqIHNodXRkb3duKS4gQXN5bmMgbGlzdGVuZXIgY29udGludWF0aW9ucyBhcmUgTk9UIGF3YWl0ZWQ7IGZvbGxvdyB3aXRoXG4gICAqIGBkcmFpbigpYCBmb3IgdGhhdC5cbiAgICovXG4gIGZsdXNoTm93KG9wdHM/OiB7IG1heFJvdW5kcz86IG51bWJlciB9KTogRmx1c2hTeW5jUmVzdWx0IHtcbiAgICByZXR1cm4gdGhpcy5kcml2ZXIuZmx1c2hTeW5jKG9wdHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZsdXNoIHRoZSBiYWNrbG9nLCB0aGVuIHNldHRsZSBhbGwgaW5mbGlnaHQgYXN5bmMgY29udGludWF0aW9ucyDigJRcbiAgICogYFByb21pc2UuYWxsU2V0dGxlZGAgdW5kZXIgYSBkZWFkbGluZSwgc2hhcGVkIGxpa2UgYGZsdXNoQWxsRGV0YWNoZWRgLlxuICAgKiBMb29wcyB3aGlsZSBjb250aW51YXRpb25zIHNwYXduIG5ldyBjYXB0dXJlcywgdW50aWwgcXVpZXNjZW50IG9yIHRoZVxuICAgKiBkZWFkbGluZSBleHBpcmVzLlxuICAgKi9cbiAgYXN5bmMgZHJhaW4ob3B0cz86IHsgdGltZW91dE1zPzogbnVtYmVyIH0pOiBQcm9taXNlPERyYWluUmVzdWx0PiB7XG4gICAgY29uc3QgdGltZW91dE1zID0gb3B0cz8udGltZW91dE1zID8/IDMwXzAwMDtcbiAgICBjb25zdCBzdGFydGVkQXQgPSBEYXRlLm5vdygpO1xuICAgIGxldCBkb25lID0gMDtcbiAgICBsZXQgZmFpbGVkID0gMDtcblxuICAgIHRoaXMuZmx1c2hOb3coKTtcbiAgICB3aGlsZSAodGhpcy5pbmZsaWdodC5zaXplID4gMCkge1xuICAgICAgY29uc3QgcmVtYWluaW5nTXMgPSB0aW1lb3V0TXMgLSAoRGF0ZS5ub3coKSAtIHN0YXJ0ZWRBdCk7XG4gICAgICBpZiAocmVtYWluaW5nTXMgPD0gMCkgcmV0dXJuIHsgZG9uZSwgZmFpbGVkLCBwZW5kaW5nOiB0aGlzLmluZmxpZ2h0LnNpemUgKyB0aGlzLnF1ZXVlLmRlcHRoIH07XG5cbiAgICAgIGNvbnN0IGJhdGNoID0gWy4uLnRoaXMuaW5mbGlnaHRdO1xuICAgICAgbGV0IHRpbWVySWQ6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgdW5kZWZpbmVkO1xuICAgICAgY29uc3QgdGltZW91dFByb21pc2UgPSBuZXcgUHJvbWlzZTwnX19kcmFpbl90aW1lb3V0X18nPigocmVzb2x2ZSkgPT4ge1xuICAgICAgICB0aW1lcklkID0gc2V0VGltZW91dCgoKSA9PiByZXNvbHZlKCdfX2RyYWluX3RpbWVvdXRfXycpLCByZW1haW5pbmdNcyk7XG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHNldHRsZWQgPSBhd2FpdCBQcm9taXNlLnJhY2UoW1Byb21pc2UuYWxsU2V0dGxlZChiYXRjaCksIHRpbWVvdXRQcm9taXNlXSk7XG4gICAgICBpZiAodGltZXJJZCAhPT0gdW5kZWZpbmVkKSBjbGVhclRpbWVvdXQodGltZXJJZCk7XG4gICAgICBpZiAoc2V0dGxlZCA9PT0gJ19fZHJhaW5fdGltZW91dF9fJykge1xuICAgICAgICByZXR1cm4geyBkb25lLCBmYWlsZWQsIHBlbmRpbmc6IHRoaXMuaW5mbGlnaHQuc2l6ZSArIHRoaXMucXVldWUuZGVwdGggfTtcbiAgICAgIH1cbiAgICAgIGZvciAoY29uc3QgciBvZiBzZXR0bGVkKSB7XG4gICAgICAgIC8vIFRyYWNrZWQgcHJvbWlzZXMgbmV2ZXIgcmVqZWN0IOKAlCB0aGV5IHJlc29sdmUgdHJ1ZSAob2spIC8gZmFsc2UuXG4gICAgICAgIGlmIChyLnN0YXR1cyA9PT0gJ2Z1bGZpbGxlZCcgJiYgci52YWx1ZSA9PT0gZmFsc2UpIGZhaWxlZCArPSAxO1xuICAgICAgICBlbHNlIGRvbmUgKz0gMTtcbiAgICAgIH1cbiAgICAgIC8vIENvbnRpbnVhdGlvbnMgbWF5IGhhdmUgY2FwdHVyZWQgbW9yZSBldmVudHMg4oCUIGZsdXNoIGFuZCByZS1jaGVjay5cbiAgICAgIHRoaXMuZmx1c2hOb3coKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgZG9uZSwgZmFpbGVkLCBwZW5kaW5nOiB0aGlzLnF1ZXVlLmRlcHRoIH07XG4gIH1cblxuICAvKiogQTQg4oCUIHRoZSBzdGF0cyBvYmplY3QgQmxvY2sgOSBjb25zdW1lcy4gUHVyZSBnZXR0ZXIsIGZyZXNoIHNuYXBzaG90LiAqL1xuICBnZXRTdGF0cygpOiBEaXNwYXRjaGVyU3RhdHMge1xuICAgIGNvbnN0IGNvdW50ZXJzID0gdGhpcy5xdWV1ZS5nZXRDb3VudGVycygpO1xuICAgIGNvbnN0IGRyaXZlclN0YXRzID0gdGhpcy5kcml2ZXIuZ2V0U3RhdHMoKTtcbiAgICBjb25zdCBwZXJMaXN0ZW5lcjogUmVjb3JkPHN0cmluZywgTGlzdGVuZXJTdGF0cz4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IFtpZCwgc3RhdHNdIG9mIHRoaXMubGlzdGVuZXJTdGF0cykge1xuICAgICAgcGVyTGlzdGVuZXJbaWRdID0geyBldmVudHM6IHN0YXRzLmV2ZW50cywgdG90YWxNczogc3RhdHMudG90YWxNcywgbGFzdEZsdXNoTXM6IHN0YXRzLmxhc3RGbHVzaE1zIH07XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBkZXB0aDogdGhpcy5xdWV1ZS5kZXB0aCxcbiAgICAgIGRyb3BzOiBjb3VudGVycy5kcm9wcyxcbiAgICAgIGZsdXNoZXM6IGRyaXZlclN0YXRzLmZsdXNoZXMsXG4gICAgICBidWRnZXRFeGhhdXN0ZWQ6IGRyaXZlclN0YXRzLmJ1ZGdldEV4aGF1c3RlZCxcbiAgICAgIHA5NUZsdXNoTXM6IGRyaXZlclN0YXRzLnA5NUZsdXNoTXMsXG4gICAgICBpbmxpbmVEZWxpdmVyaWVzOiB0aGlzLmlubGluZURlbGl2ZXJpZXMsXG4gICAgICBpbmZsaWdodDogdGhpcy5pbmZsaWdodC5zaXplLFxuICAgICAgcGVyTGlzdGVuZXIsXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgZGVsaXZlck5leHQoKTogdm9pZCB7XG4gICAgY29uc3QgZW52ZWxvcGUgPSB0aGlzLnF1ZXVlLnNoaWZ0KCk7XG4gICAgaWYgKGVudmVsb3BlID09PSB1bmRlZmluZWQpIHJldHVybjtcbiAgICB0aGlzLmRlbGl2ZXIoZW52ZWxvcGUpO1xuICB9XG5cbiAgLyoqIEludm9rZSBldmVyeSBsaXN0ZW5lciB3aXRoIGZ1bGwgZXJyb3IgaXNvbGF0aW9uICsgdGltZSBhY2NvdW50aW5nLiAqL1xuICBwcml2YXRlIGRlbGl2ZXIoZW52ZWxvcGU6IENhcHR1cmVFbnZlbG9wZSk6IHZvaWQge1xuICAgIGZvciAoY29uc3QgW2lkLCBsaXN0ZW5lcl0gb2YgdGhpcy5saXN0ZW5lcnMpIHtcbiAgICAgIGNvbnN0IHN0YXRzID0gdGhpcy5saXN0ZW5lclN0YXRzLmdldChpZCkgYXMgTXV0YWJsZUxpc3RlbmVyU3RhdHM7XG4gICAgICBjb25zdCBzdGFydCA9IHRoaXMubm93KCk7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBsaXN0ZW5lcihlbnZlbG9wZSk7XG4gICAgICAgIGlmIChpc1RoZW5hYmxlKHJlc3VsdCkpIHRoaXMudHJhY2socmVzdWx0LCBpZCwgZW52ZWxvcGUpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhpcy5zYWZlT25FcnJvcihlcnJvciwgeyBsaXN0ZW5lcklkOiBpZCwgZW52ZWxvcGUsIHBoYXNlOiAnc3luYycgfSk7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBjb25zdCBlbGFwc2VkID0gdGhpcy5ub3coKSAtIHN0YXJ0O1xuICAgICAgICBzdGF0cy5ldmVudHMgKz0gMTtcbiAgICAgICAgc3RhdHMudG90YWxNcyArPSBlbGFwc2VkO1xuICAgICAgICBzdGF0cy5sYXN0Rmx1c2hNcyArPSBlbGFwc2VkO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKiBUcmFjayBhbiBhc3luYyBjb250aW51YXRpb247IHJvdXRlIGl0cyByZWplY3Rpb247IG5ldmVyIHJlamVjdC4gKi9cbiAgcHJpdmF0ZSB0cmFjayhwcm9taXNlOiBQcm9taXNlPHZvaWQ+LCBsaXN0ZW5lcklkOiBzdHJpbmcsIGVudmVsb3BlOiBDYXB0dXJlRW52ZWxvcGUpOiB2b2lkIHtcbiAgICBjb25zdCB0cmFja2VkOiBQcm9taXNlPGJvb2xlYW4+ID0gcHJvbWlzZS50aGVuKFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICAgIChlcnJvcikgPT4ge1xuICAgICAgICB0aGlzLnNhZmVPbkVycm9yKGVycm9yLCB7IGxpc3RlbmVySWQsIGVudmVsb3BlLCBwaGFzZTogJ2FzeW5jJyB9KTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfSxcbiAgICApO1xuICAgIHRoaXMuaW5mbGlnaHQuYWRkKHRyYWNrZWQpO1xuICAgIC8vIFNlbGYtY2xlYW51cCDigJQgYHRyYWNrZWRgIG5ldmVyIHJlamVjdHMsIHNvIHRoaXMgY2hhaW4gY2Fubm90IGZsb2F0IGFuXG4gICAgLy8gdW5oYW5kbGVkIHJlamVjdGlvbi5cbiAgICB0cmFja2VkLnRoZW4oKCkgPT4gdGhpcy5pbmZsaWdodC5kZWxldGUodHJhY2tlZCkpO1xuICB9XG5cbiAgLyoqIFRoZSBlcnJvciBzaW5rIG11c3QgbmV2ZXIgYmVjb21lIGFuIGVycm9yIHNvdXJjZS4gKi9cbiAgcHJpdmF0ZSBzYWZlT25FcnJvcihlcnJvcjogdW5rbm93biwgY29udGV4dDogRGlzcGF0Y2hFcnJvckNvbnRleHQpOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgdGhpcy5vbkVycm9yPy4oZXJyb3IsIGNvbnRleHQpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gU3dhbGxvdyDigJQgaXNvbGF0aW9uIGlzIGFic29sdXRlLlxuICAgIH1cbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* observer-queue/flushDriver.ts — RFC-001 Block 4: armed-once microtask batcher.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Kernel-style bottom-half. Producers only set a flag ("work
|
|
5
|
+
* pending") and return; the actual work runs at the next
|
|
6
|
+
* scheduling checkpoint (a microtask), drains under a time
|
|
7
|
+
* budget, and re-arms itself if backlog remains. Same shape as
|
|
8
|
+
* the detach module's `microtaskBatchDriver` — accumulate during
|
|
9
|
+
* the current sync slice, drain at the boundary.
|
|
10
|
+
* Role: The scheduler of the deferred-observer pipeline. Owns WHEN
|
|
11
|
+
* delivery happens; knows nothing about envelopes or listeners
|
|
12
|
+
* (the dispatcher, Block 5, injects `depth`/`processNext`).
|
|
13
|
+
* Pure module — zero imports, zero engine knowledge.
|
|
14
|
+
*
|
|
15
|
+
* Scheduling semantics (normative, RFC-001 §5 + amendment A1):
|
|
16
|
+
* - `arm()` is idempotent: at most ONE pending flush exists (armed flag).
|
|
17
|
+
* N captures between checkpoints ⇒ exactly 1 flush.
|
|
18
|
+
* - A flush drains a SNAPSHOT: at most `depth()`-at-flush-start items.
|
|
19
|
+
* Events enqueued BY listeners during the flush exceed the snapshot and
|
|
20
|
+
* land at the NEXT checkpoint — listener-driven cascades cannot starve
|
|
21
|
+
* the event loop.
|
|
22
|
+
* - `flushBudgetMs` (default 2; `Infinity` = full snapshot drain): the
|
|
23
|
+
* flush stops once the budget is exhausted, counts `budgetExhausted`,
|
|
24
|
+
* and re-arms. At least ONE item is processed per flush regardless of
|
|
25
|
+
* budget — guaranteed progress under any clock.
|
|
26
|
+
* - If backlog remains after the flush (budget cut OR listener enqueues),
|
|
27
|
+
* the driver re-arms for the next checkpoint.
|
|
28
|
+
*
|
|
29
|
+
* Why stage boundaries make this safe: the engine `await`s every stage, so
|
|
30
|
+
* the microtask queue runs at EVERY stage boundary — flushes are at most
|
|
31
|
+
* "one beat behind" the producing stage. See
|
|
32
|
+
* `docs/guides/execution-model.md` ("Stage boundaries are scheduling
|
|
33
|
+
* points") and the FAQ in `docs/design/rfc-001-deferred-observers.md`.
|
|
34
|
+
*
|
|
35
|
+
* Testability: `now` (clock) and `schedule` (checkpoint primitive) are
|
|
36
|
+
* injectable — tests pump flushes deterministically with a fake clock and
|
|
37
|
+
* a captured-callback scheduler; production uses `performance.now` and
|
|
38
|
+
* `queueMicrotask`.
|
|
39
|
+
*/
|
|
40
|
+
/** Rolling sample window for the p95 flush-duration stat (A4). */
|
|
41
|
+
export const FLUSH_SAMPLE_WINDOW = 128;
|
|
42
|
+
/** Default cascade cap for {@link FlushDriver.flushSync}. */
|
|
43
|
+
const DEFAULT_MAX_SYNC_ROUNDS = 1_000;
|
|
44
|
+
const defaultNow = () => (typeof performance !== 'undefined' ? performance.now() : Date.now());
|
|
45
|
+
export class FlushDriver {
|
|
46
|
+
depth;
|
|
47
|
+
processNext;
|
|
48
|
+
flushBudgetMs;
|
|
49
|
+
now;
|
|
50
|
+
schedule;
|
|
51
|
+
onFlushStart;
|
|
52
|
+
onFlushEnd;
|
|
53
|
+
armed = false;
|
|
54
|
+
flushes = 0;
|
|
55
|
+
budgetExhaustedCount = 0;
|
|
56
|
+
lastFlushMs = 0;
|
|
57
|
+
samples = [];
|
|
58
|
+
sampleWriteIdx = 0;
|
|
59
|
+
constructor(opts) {
|
|
60
|
+
const budget = opts.flushBudgetMs ?? 2;
|
|
61
|
+
if (Number.isNaN(budget) || budget <= 0) {
|
|
62
|
+
throw new RangeError(`flushBudgetMs must be > 0 (got ${budget}); use Infinity for full drains`);
|
|
63
|
+
}
|
|
64
|
+
this.depth = opts.depth;
|
|
65
|
+
this.processNext = opts.processNext;
|
|
66
|
+
this.flushBudgetMs = budget;
|
|
67
|
+
this.now = opts.now ?? defaultNow;
|
|
68
|
+
this.schedule = opts.schedule ?? ((cb) => queueMicrotask(cb));
|
|
69
|
+
this.onFlushStart = opts.onFlushStart;
|
|
70
|
+
this.onFlushEnd = opts.onFlushEnd;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Request a flush at the next checkpoint. Idempotent — while one flush
|
|
74
|
+
* is pending, further arms are free no-ops (the armed-once invariant).
|
|
75
|
+
*/
|
|
76
|
+
arm() {
|
|
77
|
+
if (this.armed)
|
|
78
|
+
return;
|
|
79
|
+
this.armed = true;
|
|
80
|
+
this.schedule(() => this.flush());
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Synchronous full drain — the terminal-flush primitive (end of run /
|
|
84
|
+
* shutdown). Repeats snapshot rounds until the queue is empty so
|
|
85
|
+
* listener-enqueued cascades drain too, capped at `maxRounds` so a
|
|
86
|
+
* listener that enqueues forever cannot hang the process (`remaining`
|
|
87
|
+
* reports what the cap left behind).
|
|
88
|
+
*/
|
|
89
|
+
flushSync(opts) {
|
|
90
|
+
const maxRounds = opts?.maxRounds ?? DEFAULT_MAX_SYNC_ROUNDS;
|
|
91
|
+
if (this.depth() === 0)
|
|
92
|
+
return { drained: 0, remaining: 0 };
|
|
93
|
+
this.onFlushStart?.();
|
|
94
|
+
const start = this.now();
|
|
95
|
+
let drained = 0;
|
|
96
|
+
for (let round = 0; round < maxRounds && this.depth() > 0; round++) {
|
|
97
|
+
const snapshot = this.depth();
|
|
98
|
+
for (let i = 0; i < snapshot && this.depth() > 0; i++) {
|
|
99
|
+
this.processNext();
|
|
100
|
+
drained += 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
this.recordFlush(this.now() - start, false);
|
|
104
|
+
const remaining = this.depth();
|
|
105
|
+
this.onFlushEnd?.({ processed: drained, budgetExhausted: false, rearmed: false });
|
|
106
|
+
return { drained, remaining };
|
|
107
|
+
}
|
|
108
|
+
getStats() {
|
|
109
|
+
return {
|
|
110
|
+
flushes: this.flushes,
|
|
111
|
+
budgetExhausted: this.budgetExhaustedCount,
|
|
112
|
+
lastFlushMs: this.lastFlushMs,
|
|
113
|
+
p95FlushMs: this.p95FlushMs(),
|
|
114
|
+
armed: this.armed,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** The microtask body — see the module-header semantics. */
|
|
118
|
+
flush() {
|
|
119
|
+
this.armed = false;
|
|
120
|
+
const snapshot = this.depth();
|
|
121
|
+
if (snapshot === 0)
|
|
122
|
+
return; // raced with flushSync — zero-work wakeup
|
|
123
|
+
this.onFlushStart?.();
|
|
124
|
+
const start = this.now();
|
|
125
|
+
let processed = 0;
|
|
126
|
+
let exhausted = false;
|
|
127
|
+
while (processed < snapshot && this.depth() > 0) {
|
|
128
|
+
// Budget check AFTER the first item — guaranteed progress per flush.
|
|
129
|
+
if (processed > 0 && this.now() - start >= this.flushBudgetMs) {
|
|
130
|
+
exhausted = true;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
this.processNext();
|
|
134
|
+
processed += 1;
|
|
135
|
+
}
|
|
136
|
+
this.recordFlush(this.now() - start, exhausted);
|
|
137
|
+
// Backlog left (budget cut, or listeners enqueued past the snapshot):
|
|
138
|
+
// hand it to the NEXT checkpoint — never starve, never spin.
|
|
139
|
+
const rearmed = this.depth() > 0;
|
|
140
|
+
if (rearmed)
|
|
141
|
+
this.arm();
|
|
142
|
+
this.onFlushEnd?.({ processed, budgetExhausted: exhausted, rearmed });
|
|
143
|
+
}
|
|
144
|
+
recordFlush(elapsedMs, exhausted) {
|
|
145
|
+
this.flushes += 1;
|
|
146
|
+
this.lastFlushMs = elapsedMs;
|
|
147
|
+
if (exhausted)
|
|
148
|
+
this.budgetExhaustedCount += 1;
|
|
149
|
+
if (this.samples.length < FLUSH_SAMPLE_WINDOW)
|
|
150
|
+
this.samples.push(elapsedMs);
|
|
151
|
+
else {
|
|
152
|
+
this.samples[this.sampleWriteIdx] = elapsedMs;
|
|
153
|
+
this.sampleWriteIdx = (this.sampleWriteIdx + 1) % FLUSH_SAMPLE_WINDOW;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
p95FlushMs() {
|
|
157
|
+
if (this.samples.length === 0)
|
|
158
|
+
return 0;
|
|
159
|
+
const sorted = [...this.samples].sort((a, b) => a - b);
|
|
160
|
+
return sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * 0.95))];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmx1c2hEcml2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL29ic2VydmVyLXF1ZXVlL2ZsdXNoRHJpdmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNDRztBQXFESCxrRUFBa0U7QUFDbEUsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUcsR0FBRyxDQUFDO0FBRXZDLDZEQUE2RDtBQUM3RCxNQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQztBQUV0QyxNQUFNLFVBQVUsR0FBRyxHQUFXLEVBQUUsQ0FBQyxDQUFDLE9BQU8sV0FBVyxLQUFLLFdBQVcsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztBQUV2RyxNQUFNLE9BQU8sV0FBVztJQUNMLEtBQUssQ0FBZTtJQUNwQixXQUFXLENBQWE7SUFDeEIsYUFBYSxDQUFTO0lBQ3RCLEdBQUcsQ0FBZTtJQUNsQixRQUFRLENBQTJCO0lBQ25DLFlBQVksQ0FBYztJQUMxQixVQUFVLENBQW1DO0lBRXRELEtBQUssR0FBRyxLQUFLLENBQUM7SUFDZCxPQUFPLEdBQUcsQ0FBQyxDQUFDO0lBQ1osb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLFdBQVcsR0FBRyxDQUFDLENBQUM7SUFDUCxPQUFPLEdBQWEsRUFBRSxDQUFDO0lBQ2hDLGNBQWMsR0FBRyxDQUFDLENBQUM7SUFFM0IsWUFBWSxJQUF3QjtRQUNsQyxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsYUFBYSxJQUFJLENBQUMsQ0FBQztRQUN2QyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ3hDLE1BQU0sSUFBSSxVQUFVLENBQUMsa0NBQWtDLE1BQU0saUNBQWlDLENBQUMsQ0FBQztRQUNsRyxDQUFDO1FBQ0QsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztRQUNwQyxJQUFJLENBQUMsYUFBYSxHQUFHLE1BQU0sQ0FBQztRQUM1QixJQUFJLENBQUMsR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLElBQUksVUFBVSxDQUFDO1FBQ2xDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsSUFBSSxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUM5RCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUM7UUFDdEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDO0lBQ3BDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxHQUFHO1FBQ0QsSUFBSSxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU87UUFDdkIsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUM7UUFDbEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsU0FBUyxDQUFDLElBQTZCO1FBQ3JDLE1BQU0sU0FBUyxHQUFHLElBQUksRUFBRSxTQUFTLElBQUksdUJBQXVCLENBQUM7UUFDN0QsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQztZQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUU1RCxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUUsQ0FBQztRQUN0QixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDekIsSUFBSSxPQUFPLEdBQUcsQ0FBQyxDQUFDO1FBQ2hCLEtBQUssSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEtBQUssR0FBRyxTQUFTLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDO1lBQ25FLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUM5QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsUUFBUSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDdEQsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUNuQixPQUFPLElBQUksQ0FBQyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFDRCxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDNUMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQy9CLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUNsRixPQUFPLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFRCxRQUFRO1FBQ04sT0FBTztZQUNMLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztZQUNyQixlQUFlLEVBQUUsSUFBSSxDQUFDLG9CQUFvQjtZQUMxQyxXQUFXLEVBQUUsSUFBSSxDQUFDLFdBQVc7WUFDN0IsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUU7WUFDN0IsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO1NBQ2xCLENBQUM7SUFDSixDQUFDO0lBRUQsNERBQTREO0lBQ3BELEtBQUs7UUFDWCxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNuQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDOUIsSUFBSSxRQUFRLEtBQUssQ0FBQztZQUFFLE9BQU8sQ0FBQywwQ0FBMEM7UUFFdEUsSUFBSSxDQUFDLFlBQVksRUFBRSxFQUFFLENBQUM7UUFDdEIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3pCLElBQUksU0FBUyxHQUFHLENBQUMsQ0FBQztRQUNsQixJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUM7UUFDdEIsT0FBTyxTQUFTLEdBQUcsUUFBUSxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNoRCxxRUFBcUU7WUFDckUsSUFBSSxTQUFTLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLElBQUksSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO2dCQUM5RCxTQUFTLEdBQUcsSUFBSSxDQUFDO2dCQUNqQixNQUFNO1lBQ1IsQ0FBQztZQUNELElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNuQixTQUFTLElBQUksQ0FBQyxDQUFDO1FBQ2pCLENBQUM7UUFDRCxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFFaEQsc0VBQXNFO1FBQ3RFLDZEQUE2RDtRQUM3RCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ2pDLElBQUksT0FBTztZQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN4QixJQUFJLENBQUMsVUFBVSxFQUFFLENBQUMsRUFBRSxTQUFTLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQ3hFLENBQUM7SUFFTyxXQUFXLENBQUMsU0FBaUIsRUFBRSxTQUFrQjtRQUN2RCxJQUFJLENBQUMsT0FBTyxJQUFJLENBQUMsQ0FBQztRQUNsQixJQUFJLENBQUMsV0FBVyxHQUFHLFNBQVMsQ0FBQztRQUM3QixJQUFJLFNBQVM7WUFBRSxJQUFJLENBQUMsb0JBQW9CLElBQUksQ0FBQyxDQUFDO1FBQzlDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsbUJBQW1CO1lBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7YUFDdkUsQ0FBQztZQUNKLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxHQUFHLFNBQVMsQ0FBQztZQUM5QyxJQUFJLENBQUMsY0FBYyxHQUFHLENBQUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxDQUFDLENBQUMsR0FBRyxtQkFBbUIsQ0FBQztRQUN4RSxDQUFDO0lBQ0gsQ0FBQztJQUVPLFVBQVU7UUFDaEIsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsT0FBTyxDQUFDLENBQUM7UUFDeEMsTUFBTSxNQUFNLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDdkQsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQy9FLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogb2JzZXJ2ZXItcXVldWUvZmx1c2hEcml2ZXIudHMg4oCUIFJGQy0wMDEgQmxvY2sgNDogYXJtZWQtb25jZSBtaWNyb3Rhc2sgYmF0Y2hlci5cbiAqXG4gKiBQYXR0ZXJuOiAgS2VybmVsLXN0eWxlIGJvdHRvbS1oYWxmLiBQcm9kdWNlcnMgb25seSBzZXQgYSBmbGFnIChcIndvcmtcbiAqICAgICAgICAgICBwZW5kaW5nXCIpIGFuZCByZXR1cm47IHRoZSBhY3R1YWwgd29yayBydW5zIGF0IHRoZSBuZXh0XG4gKiAgICAgICAgICAgc2NoZWR1bGluZyBjaGVja3BvaW50IChhIG1pY3JvdGFzayksIGRyYWlucyB1bmRlciBhIHRpbWVcbiAqICAgICAgICAgICBidWRnZXQsIGFuZCByZS1hcm1zIGl0c2VsZiBpZiBiYWNrbG9nIHJlbWFpbnMuIFNhbWUgc2hhcGUgYXNcbiAqICAgICAgICAgICB0aGUgZGV0YWNoIG1vZHVsZSdzIGBtaWNyb3Rhc2tCYXRjaERyaXZlcmAg4oCUIGFjY3VtdWxhdGUgZHVyaW5nXG4gKiAgICAgICAgICAgdGhlIGN1cnJlbnQgc3luYyBzbGljZSwgZHJhaW4gYXQgdGhlIGJvdW5kYXJ5LlxuICogUm9sZTogICAgIFRoZSBzY2hlZHVsZXIgb2YgdGhlIGRlZmVycmVkLW9ic2VydmVyIHBpcGVsaW5lLiBPd25zIFdIRU5cbiAqICAgICAgICAgICBkZWxpdmVyeSBoYXBwZW5zOyBrbm93cyBub3RoaW5nIGFib3V0IGVudmVsb3BlcyBvciBsaXN0ZW5lcnNcbiAqICAgICAgICAgICAodGhlIGRpc3BhdGNoZXIsIEJsb2NrIDUsIGluamVjdHMgYGRlcHRoYC9gcHJvY2Vzc05leHRgKS5cbiAqICAgICAgICAgICBQdXJlIG1vZHVsZSDigJQgemVybyBpbXBvcnRzLCB6ZXJvIGVuZ2luZSBrbm93bGVkZ2UuXG4gKlxuICogU2NoZWR1bGluZyBzZW1hbnRpY3MgKG5vcm1hdGl2ZSwgUkZDLTAwMSDCpzUgKyBhbWVuZG1lbnQgQTEpOlxuICogICAtIGBhcm0oKWAgaXMgaWRlbXBvdGVudDogYXQgbW9zdCBPTkUgcGVuZGluZyBmbHVzaCBleGlzdHMgKGFybWVkIGZsYWcpLlxuICogICAgIE4gY2FwdHVyZXMgYmV0d2VlbiBjaGVja3BvaW50cyDih5IgZXhhY3RseSAxIGZsdXNoLlxuICogICAtIEEgZmx1c2ggZHJhaW5zIGEgU05BUFNIT1Q6IGF0IG1vc3QgYGRlcHRoKClgLWF0LWZsdXNoLXN0YXJ0IGl0ZW1zLlxuICogICAgIEV2ZW50cyBlbnF1ZXVlZCBCWSBsaXN0ZW5lcnMgZHVyaW5nIHRoZSBmbHVzaCBleGNlZWQgdGhlIHNuYXBzaG90IGFuZFxuICogICAgIGxhbmQgYXQgdGhlIE5FWFQgY2hlY2twb2ludCDigJQgbGlzdGVuZXItZHJpdmVuIGNhc2NhZGVzIGNhbm5vdCBzdGFydmVcbiAqICAgICB0aGUgZXZlbnQgbG9vcC5cbiAqICAgLSBgZmx1c2hCdWRnZXRNc2AgKGRlZmF1bHQgMjsgYEluZmluaXR5YCA9IGZ1bGwgc25hcHNob3QgZHJhaW4pOiB0aGVcbiAqICAgICBmbHVzaCBzdG9wcyBvbmNlIHRoZSBidWRnZXQgaXMgZXhoYXVzdGVkLCBjb3VudHMgYGJ1ZGdldEV4aGF1c3RlZGAsXG4gKiAgICAgYW5kIHJlLWFybXMuIEF0IGxlYXN0IE9ORSBpdGVtIGlzIHByb2Nlc3NlZCBwZXIgZmx1c2ggcmVnYXJkbGVzcyBvZlxuICogICAgIGJ1ZGdldCDigJQgZ3VhcmFudGVlZCBwcm9ncmVzcyB1bmRlciBhbnkgY2xvY2suXG4gKiAgIC0gSWYgYmFja2xvZyByZW1haW5zIGFmdGVyIHRoZSBmbHVzaCAoYnVkZ2V0IGN1dCBPUiBsaXN0ZW5lciBlbnF1ZXVlcyksXG4gKiAgICAgdGhlIGRyaXZlciByZS1hcm1zIGZvciB0aGUgbmV4dCBjaGVja3BvaW50LlxuICpcbiAqIFdoeSBzdGFnZSBib3VuZGFyaWVzIG1ha2UgdGhpcyBzYWZlOiB0aGUgZW5naW5lIGBhd2FpdGBzIGV2ZXJ5IHN0YWdlLCBzb1xuICogdGhlIG1pY3JvdGFzayBxdWV1ZSBydW5zIGF0IEVWRVJZIHN0YWdlIGJvdW5kYXJ5IOKAlCBmbHVzaGVzIGFyZSBhdCBtb3N0XG4gKiBcIm9uZSBiZWF0IGJlaGluZFwiIHRoZSBwcm9kdWNpbmcgc3RhZ2UuIFNlZVxuICogYGRvY3MvZ3VpZGVzL2V4ZWN1dGlvbi1tb2RlbC5tZGAgKFwiU3RhZ2UgYm91bmRhcmllcyBhcmUgc2NoZWR1bGluZ1xuICogcG9pbnRzXCIpIGFuZCB0aGUgRkFRIGluIGBkb2NzL2Rlc2lnbi9yZmMtMDAxLWRlZmVycmVkLW9ic2VydmVycy5tZGAuXG4gKlxuICogVGVzdGFiaWxpdHk6IGBub3dgIChjbG9jaykgYW5kIGBzY2hlZHVsZWAgKGNoZWNrcG9pbnQgcHJpbWl0aXZlKSBhcmVcbiAqIGluamVjdGFibGUg4oCUIHRlc3RzIHB1bXAgZmx1c2hlcyBkZXRlcm1pbmlzdGljYWxseSB3aXRoIGEgZmFrZSBjbG9jayBhbmRcbiAqIGEgY2FwdHVyZWQtY2FsbGJhY2sgc2NoZWR1bGVyOyBwcm9kdWN0aW9uIHVzZXMgYHBlcmZvcm1hbmNlLm5vd2AgYW5kXG4gKiBgcXVldWVNaWNyb3Rhc2tgLlxuICovXG5cbi8qKiBSZXN1bHQgb2Ygb25lIGZsdXNoIChhbHNvIGRlbGl2ZXJlZCB0byBgb25GbHVzaEVuZGApLiAqL1xuZXhwb3J0IGludGVyZmFjZSBGbHVzaE91dGNvbWUge1xuICAvKiogSXRlbXMgcHJvY2Vzc2VkIGluIHRoaXMgZmx1c2guICovXG4gIHJlYWRvbmx5IHByb2Nlc3NlZDogbnVtYmVyO1xuICAvKiogVHJ1ZSB3aGVuIHRoZSB0aW1lIGJ1ZGdldCBjdXQgdGhlIGZsdXNoIGJlZm9yZSB0aGUgc25hcHNob3QgZHJhaW5lZC4gKi9cbiAgcmVhZG9ubHkgYnVkZ2V0RXhoYXVzdGVkOiBib29sZWFuO1xuICAvKiogVHJ1ZSB3aGVuIGJhY2tsb2cgcmVtYWluZWQgYW5kIHRoZSBkcml2ZXIgcmUtYXJtZWQgaXRzZWxmLiAqL1xuICByZWFkb25seSByZWFybWVkOiBib29sZWFuO1xufVxuXG4vKiogUmVzdWx0IG9mIGEgc3luY2hyb25vdXMge0BsaW5rIEZsdXNoRHJpdmVyLmZsdXNoU3luY30gZHJhaW4uICovXG5leHBvcnQgaW50ZXJmYWNlIEZsdXNoU3luY1Jlc3VsdCB7XG4gIC8qKiBJdGVtcyBwcm9jZXNzZWQgYWNyb3NzIGFsbCByb3VuZHMuICovXG4gIHJlYWRvbmx5IGRyYWluZWQ6IG51bWJlcjtcbiAgLyoqIEl0ZW1zIHN0aWxsIHF1ZXVlZCB3aGVuIGBtYXhSb3VuZHNgIHN0b3BwZWQgYSBydW5hd2F5IGNhc2NhZGUuICovXG4gIHJlYWRvbmx5IHJlbWFpbmluZzogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEZsdXNoRHJpdmVyT3B0aW9ucyB7XG4gIC8qKiBDdXJyZW50IGJhY2tsb2cgb2YgdGhlIHF1ZXVlIHRoaXMgZHJpdmVyIGRyYWlucy4gKi9cbiAgcmVhZG9ubHkgZGVwdGg6ICgpID0+IG51bWJlcjtcbiAgLyoqIFByb2Nlc3MgZXhhY3RseSBPTkUgcXVldWVkIGl0ZW0uIFByZWNvbmRpdGlvbjogYGRlcHRoKCkgPiAwYC4gKi9cbiAgcmVhZG9ubHkgcHJvY2Vzc05leHQ6ICgpID0+IHZvaWQ7XG4gIC8qKlxuICAgKiBQZXItZmx1c2ggdGltZSBidWRnZXQgaW4gbXMuIERlZmF1bHQgMi4gYEluZmluaXR5YCBkcmFpbnMgdGhlIGZ1bGxcbiAgICogc25hcHNob3QgZXZlcnkgY2hlY2twb2ludC4gTXVzdCBiZSA+IDAuXG4gICAqL1xuICByZWFkb25seSBmbHVzaEJ1ZGdldE1zPzogbnVtYmVyO1xuICAvKiogQ2xvY2sg4oCUIGRlZmF1bHQgYHBlcmZvcm1hbmNlLm5vd2AgKGZhbGxzIGJhY2sgdG8gYERhdGUubm93YCkuICovXG4gIHJlYWRvbmx5IG5vdz86ICgpID0+IG51bWJlcjtcbiAgLyoqIENoZWNrcG9pbnQgcHJpbWl0aXZlIOKAlCBkZWZhdWx0IGBxdWV1ZU1pY3JvdGFza2AuICovXG4gIHJlYWRvbmx5IHNjaGVkdWxlPzogKGNiOiAoKSA9PiB2b2lkKSA9PiB2b2lkO1xuICAvKiogRmlyZXMgYmVmb3JlIHRoZSBmaXJzdCBpdGVtIG9mIGV2ZXJ5IGZsdXNoIChpbmNsLiBgZmx1c2hTeW5jYCkuICovXG4gIHJlYWRvbmx5IG9uRmx1c2hTdGFydD86ICgpID0+IHZvaWQ7XG4gIC8qKiBGaXJlcyBhZnRlciBldmVyeSBmbHVzaCB3aXRoIGl0cyBvdXRjb21lIChpbmNsLiBgZmx1c2hTeW5jYCkuICovXG4gIHJlYWRvbmx5IG9uRmx1c2hFbmQ/OiAob3V0Y29tZTogRmx1c2hPdXRjb21lKSA9PiB2b2lkO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEZsdXNoRHJpdmVyU3RhdHMge1xuICAvKiogQ29tcGxldGVkIGZsdXNoZXMgKHplcm8td29yayB3YWtldXBzIGFyZSBub3QgY291bnRlZCkuICovXG4gIHJlYWRvbmx5IGZsdXNoZXM6IG51bWJlcjtcbiAgLyoqIEZsdXNoZXMgY3V0IHNob3J0IGJ5IGBmbHVzaEJ1ZGdldE1zYCAoQTEg4oCUIGJhY2tsb2cgdmlzaWJpbGl0eSkuICovXG4gIHJlYWRvbmx5IGJ1ZGdldEV4aGF1c3RlZDogbnVtYmVyO1xuICAvKiogRHVyYXRpb24gb2YgdGhlIG1vc3QgcmVjZW50IGZsdXNoLCBtcy4gKi9cbiAgcmVhZG9ubHkgbGFzdEZsdXNoTXM6IG51bWJlcjtcbiAgLyoqIHA5NSBvdmVyIHRoZSBsYXN0IHtAbGluayBGTFVTSF9TQU1QTEVfV0lORE9XfSBmbHVzaCBkdXJhdGlvbnMsIG1zLiAqL1xuICByZWFkb25seSBwOTVGbHVzaE1zOiBudW1iZXI7XG4gIC8qKiBUcnVlIHdoaWxlIGEgZmx1c2ggaXMgc2NoZWR1bGVkIGJ1dCBub3QgeWV0IHJ1bi4gKi9cbiAgcmVhZG9ubHkgYXJtZWQ6IGJvb2xlYW47XG59XG5cbi8qKiBSb2xsaW5nIHNhbXBsZSB3aW5kb3cgZm9yIHRoZSBwOTUgZmx1c2gtZHVyYXRpb24gc3RhdCAoQTQpLiAqL1xuZXhwb3J0IGNvbnN0IEZMVVNIX1NBTVBMRV9XSU5ET1cgPSAxMjg7XG5cbi8qKiBEZWZhdWx0IGNhc2NhZGUgY2FwIGZvciB7QGxpbmsgRmx1c2hEcml2ZXIuZmx1c2hTeW5jfS4gKi9cbmNvbnN0IERFRkFVTFRfTUFYX1NZTkNfUk9VTkRTID0gMV8wMDA7XG5cbmNvbnN0IGRlZmF1bHROb3cgPSAoKTogbnVtYmVyID0+ICh0eXBlb2YgcGVyZm9ybWFuY2UgIT09ICd1bmRlZmluZWQnID8gcGVyZm9ybWFuY2Uubm93KCkgOiBEYXRlLm5vdygpKTtcblxuZXhwb3J0IGNsYXNzIEZsdXNoRHJpdmVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBkZXB0aDogKCkgPT4gbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IHByb2Nlc3NOZXh0OiAoKSA9PiB2b2lkO1xuICBwcml2YXRlIHJlYWRvbmx5IGZsdXNoQnVkZ2V0TXM6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBub3c6ICgpID0+IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSBzY2hlZHVsZTogKGNiOiAoKSA9PiB2b2lkKSA9PiB2b2lkO1xuICBwcml2YXRlIHJlYWRvbmx5IG9uRmx1c2hTdGFydD86ICgpID0+IHZvaWQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgb25GbHVzaEVuZD86IChvdXRjb21lOiBGbHVzaE91dGNvbWUpID0+IHZvaWQ7XG5cbiAgcHJpdmF0ZSBhcm1lZCA9IGZhbHNlO1xuICBwcml2YXRlIGZsdXNoZXMgPSAwO1xuICBwcml2YXRlIGJ1ZGdldEV4aGF1c3RlZENvdW50ID0gMDtcbiAgcHJpdmF0ZSBsYXN0Rmx1c2hNcyA9IDA7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2FtcGxlczogbnVtYmVyW10gPSBbXTtcbiAgcHJpdmF0ZSBzYW1wbGVXcml0ZUlkeCA9IDA7XG5cbiAgY29uc3RydWN0b3Iob3B0czogRmx1c2hEcml2ZXJPcHRpb25zKSB7XG4gICAgY29uc3QgYnVkZ2V0ID0gb3B0cy5mbHVzaEJ1ZGdldE1zID8/IDI7XG4gICAgaWYgKE51bWJlci5pc05hTihidWRnZXQpIHx8IGJ1ZGdldCA8PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcihgZmx1c2hCdWRnZXRNcyBtdXN0IGJlID4gMCAoZ290ICR7YnVkZ2V0fSk7IHVzZSBJbmZpbml0eSBmb3IgZnVsbCBkcmFpbnNgKTtcbiAgICB9XG4gICAgdGhpcy5kZXB0aCA9IG9wdHMuZGVwdGg7XG4gICAgdGhpcy5wcm9jZXNzTmV4dCA9IG9wdHMucHJvY2Vzc05leHQ7XG4gICAgdGhpcy5mbHVzaEJ1ZGdldE1zID0gYnVkZ2V0O1xuICAgIHRoaXMubm93ID0gb3B0cy5ub3cgPz8gZGVmYXVsdE5vdztcbiAgICB0aGlzLnNjaGVkdWxlID0gb3B0cy5zY2hlZHVsZSA/PyAoKGNiKSA9PiBxdWV1ZU1pY3JvdGFzayhjYikpO1xuICAgIHRoaXMub25GbHVzaFN0YXJ0ID0gb3B0cy5vbkZsdXNoU3RhcnQ7XG4gICAgdGhpcy5vbkZsdXNoRW5kID0gb3B0cy5vbkZsdXNoRW5kO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlcXVlc3QgYSBmbHVzaCBhdCB0aGUgbmV4dCBjaGVja3BvaW50LiBJZGVtcG90ZW50IOKAlCB3aGlsZSBvbmUgZmx1c2hcbiAgICogaXMgcGVuZGluZywgZnVydGhlciBhcm1zIGFyZSBmcmVlIG5vLW9wcyAodGhlIGFybWVkLW9uY2UgaW52YXJpYW50KS5cbiAgICovXG4gIGFybSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5hcm1lZCkgcmV0dXJuO1xuICAgIHRoaXMuYXJtZWQgPSB0cnVlO1xuICAgIHRoaXMuc2NoZWR1bGUoKCkgPT4gdGhpcy5mbHVzaCgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTeW5jaHJvbm91cyBmdWxsIGRyYWluIOKAlCB0aGUgdGVybWluYWwtZmx1c2ggcHJpbWl0aXZlIChlbmQgb2YgcnVuIC9cbiAgICogc2h1dGRvd24pLiBSZXBlYXRzIHNuYXBzaG90IHJvdW5kcyB1bnRpbCB0aGUgcXVldWUgaXMgZW1wdHkgc29cbiAgICogbGlzdGVuZXItZW5xdWV1ZWQgY2FzY2FkZXMgZHJhaW4gdG9vLCBjYXBwZWQgYXQgYG1heFJvdW5kc2Agc28gYVxuICAgKiBsaXN0ZW5lciB0aGF0IGVucXVldWVzIGZvcmV2ZXIgY2Fubm90IGhhbmcgdGhlIHByb2Nlc3MgKGByZW1haW5pbmdgXG4gICAqIHJlcG9ydHMgd2hhdCB0aGUgY2FwIGxlZnQgYmVoaW5kKS5cbiAgICovXG4gIGZsdXNoU3luYyhvcHRzPzogeyBtYXhSb3VuZHM/OiBudW1iZXIgfSk6IEZsdXNoU3luY1Jlc3VsdCB7XG4gICAgY29uc3QgbWF4Um91bmRzID0gb3B0cz8ubWF4Um91bmRzID8/IERFRkFVTFRfTUFYX1NZTkNfUk9VTkRTO1xuICAgIGlmICh0aGlzLmRlcHRoKCkgPT09IDApIHJldHVybiB7IGRyYWluZWQ6IDAsIHJlbWFpbmluZzogMCB9O1xuXG4gICAgdGhpcy5vbkZsdXNoU3RhcnQ/LigpO1xuICAgIGNvbnN0IHN0YXJ0ID0gdGhpcy5ub3coKTtcbiAgICBsZXQgZHJhaW5lZCA9IDA7XG4gICAgZm9yIChsZXQgcm91bmQgPSAwOyByb3VuZCA8IG1heFJvdW5kcyAmJiB0aGlzLmRlcHRoKCkgPiAwOyByb3VuZCsrKSB7XG4gICAgICBjb25zdCBzbmFwc2hvdCA9IHRoaXMuZGVwdGgoKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc25hcHNob3QgJiYgdGhpcy5kZXB0aCgpID4gMDsgaSsrKSB7XG4gICAgICAgIHRoaXMucHJvY2Vzc05leHQoKTtcbiAgICAgICAgZHJhaW5lZCArPSAxO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnJlY29yZEZsdXNoKHRoaXMubm93KCkgLSBzdGFydCwgZmFsc2UpO1xuICAgIGNvbnN0IHJlbWFpbmluZyA9IHRoaXMuZGVwdGgoKTtcbiAgICB0aGlzLm9uRmx1c2hFbmQ/Lih7IHByb2Nlc3NlZDogZHJhaW5lZCwgYnVkZ2V0RXhoYXVzdGVkOiBmYWxzZSwgcmVhcm1lZDogZmFsc2UgfSk7XG4gICAgcmV0dXJuIHsgZHJhaW5lZCwgcmVtYWluaW5nIH07XG4gIH1cblxuICBnZXRTdGF0cygpOiBGbHVzaERyaXZlclN0YXRzIHtcbiAgICByZXR1cm4ge1xuICAgICAgZmx1c2hlczogdGhpcy5mbHVzaGVzLFxuICAgICAgYnVkZ2V0RXhoYXVzdGVkOiB0aGlzLmJ1ZGdldEV4aGF1c3RlZENvdW50LFxuICAgICAgbGFzdEZsdXNoTXM6IHRoaXMubGFzdEZsdXNoTXMsXG4gICAgICBwOTVGbHVzaE1zOiB0aGlzLnA5NUZsdXNoTXMoKSxcbiAgICAgIGFybWVkOiB0aGlzLmFybWVkLFxuICAgIH07XG4gIH1cblxuICAvKiogVGhlIG1pY3JvdGFzayBib2R5IOKAlCBzZWUgdGhlIG1vZHVsZS1oZWFkZXIgc2VtYW50aWNzLiAqL1xuICBwcml2YXRlIGZsdXNoKCk6IHZvaWQge1xuICAgIHRoaXMuYXJtZWQgPSBmYWxzZTtcbiAgICBjb25zdCBzbmFwc2hvdCA9IHRoaXMuZGVwdGgoKTtcbiAgICBpZiAoc25hcHNob3QgPT09IDApIHJldHVybjsgLy8gcmFjZWQgd2l0aCBmbHVzaFN5bmMg4oCUIHplcm8td29yayB3YWtldXBcblxuICAgIHRoaXMub25GbHVzaFN0YXJ0Py4oKTtcbiAgICBjb25zdCBzdGFydCA9IHRoaXMubm93KCk7XG4gICAgbGV0IHByb2Nlc3NlZCA9IDA7XG4gICAgbGV0IGV4aGF1c3RlZCA9IGZhbHNlO1xuICAgIHdoaWxlIChwcm9jZXNzZWQgPCBzbmFwc2hvdCAmJiB0aGlzLmRlcHRoKCkgPiAwKSB7XG4gICAgICAvLyBCdWRnZXQgY2hlY2sgQUZURVIgdGhlIGZpcnN0IGl0ZW0g4oCUIGd1YXJhbnRlZWQgcHJvZ3Jlc3MgcGVyIGZsdXNoLlxuICAgICAgaWYgKHByb2Nlc3NlZCA+IDAgJiYgdGhpcy5ub3coKSAtIHN0YXJ0ID49IHRoaXMuZmx1c2hCdWRnZXRNcykge1xuICAgICAgICBleGhhdXN0ZWQgPSB0cnVlO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIHRoaXMucHJvY2Vzc05leHQoKTtcbiAgICAgIHByb2Nlc3NlZCArPSAxO1xuICAgIH1cbiAgICB0aGlzLnJlY29yZEZsdXNoKHRoaXMubm93KCkgLSBzdGFydCwgZXhoYXVzdGVkKTtcblxuICAgIC8vIEJhY2tsb2cgbGVmdCAoYnVkZ2V0IGN1dCwgb3IgbGlzdGVuZXJzIGVucXVldWVkIHBhc3QgdGhlIHNuYXBzaG90KTpcbiAgICAvLyBoYW5kIGl0IHRvIHRoZSBORVhUIGNoZWNrcG9pbnQg4oCUIG5ldmVyIHN0YXJ2ZSwgbmV2ZXIgc3Bpbi5cbiAgICBjb25zdCByZWFybWVkID0gdGhpcy5kZXB0aCgpID4gMDtcbiAgICBpZiAocmVhcm1lZCkgdGhpcy5hcm0oKTtcbiAgICB0aGlzLm9uRmx1c2hFbmQ/Lih7IHByb2Nlc3NlZCwgYnVkZ2V0RXhoYXVzdGVkOiBleGhhdXN0ZWQsIHJlYXJtZWQgfSk7XG4gIH1cblxuICBwcml2YXRlIHJlY29yZEZsdXNoKGVsYXBzZWRNczogbnVtYmVyLCBleGhhdXN0ZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmZsdXNoZXMgKz0gMTtcbiAgICB0aGlzLmxhc3RGbHVzaE1zID0gZWxhcHNlZE1zO1xuICAgIGlmIChleGhhdXN0ZWQpIHRoaXMuYnVkZ2V0RXhoYXVzdGVkQ291bnQgKz0gMTtcbiAgICBpZiAodGhpcy5zYW1wbGVzLmxlbmd0aCA8IEZMVVNIX1NBTVBMRV9XSU5ET1cpIHRoaXMuc2FtcGxlcy5wdXNoKGVsYXBzZWRNcyk7XG4gICAgZWxzZSB7XG4gICAgICB0aGlzLnNhbXBsZXNbdGhpcy5zYW1wbGVXcml0ZUlkeF0gPSBlbGFwc2VkTXM7XG4gICAgICB0aGlzLnNhbXBsZVdyaXRlSWR4ID0gKHRoaXMuc2FtcGxlV3JpdGVJZHggKyAxKSAlIEZMVVNIX1NBTVBMRV9XSU5ET1c7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBwOTVGbHVzaE1zKCk6IG51bWJlciB7XG4gICAgaWYgKHRoaXMuc2FtcGxlcy5sZW5ndGggPT09IDApIHJldHVybiAwO1xuICAgIGNvbnN0IHNvcnRlZCA9IFsuLi50aGlzLnNhbXBsZXNdLnNvcnQoKGEsIGIpID0+IGEgLSBiKTtcbiAgICByZXR1cm4gc29ydGVkW01hdGgubWluKHNvcnRlZC5sZW5ndGggLSAxLCBNYXRoLmZsb29yKHNvcnRlZC5sZW5ndGggKiAwLjk1KSldO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* istanbul ignore file */
|
|
2
|
+
/**
|
|
3
|
+
* observer-queue/ — RFC-001 deferred observer delivery, Blocks 2–5.
|
|
4
|
+
*
|
|
5
|
+
* The pure "one beat behind" pipeline:
|
|
6
|
+
*
|
|
7
|
+
* producer ─► capture (Block 1, `capture/envelope`) ─► MergedQueue
|
|
8
|
+
* (Block 3, seq-stamped over a BoundedRing, Block 2) ─► FlushDriver
|
|
9
|
+
* (Block 4, armed-once microtask checkpoints, flushBudgetMs) ─►
|
|
10
|
+
* DeferredDispatcher (Block 5, isolated listeners + inflight + stats)
|
|
11
|
+
*
|
|
12
|
+
* INTERNAL MODULE — deliberately NOT exported from the public footprintjs
|
|
13
|
+
* barrels yet. The engine wiring + public surface land with Blocks 6–10
|
|
14
|
+
* (see docs/design/rfc-001-deferred-observers.md). Zero engine imports:
|
|
15
|
+
* this directory may import only `../capture/` and its own files.
|
|
16
|
+
*/
|
|
17
|
+
export { capture, PAYLOAD_SUMMARY_MAX_DEPTH, PAYLOAD_SUMMARY_MAX_ENTRIES, PAYLOAD_SUMMARY_MAX_NODES, summarizePayload, } from '../capture/envelope.js';
|
|
18
|
+
export { DeferredDispatcher } from './deferredDispatcher.js';
|
|
19
|
+
export { FLUSH_SAMPLE_WINDOW, FlushDriver } from './flushDriver.js';
|
|
20
|
+
export { DEFAULT_MAX_QUEUE, MergedQueue } from './mergedQueue.js';
|
|
21
|
+
export { BoundedRing } from './ring.js';
|
|
22
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL29ic2VydmVyLXF1ZXVlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDBCQUEwQjtBQUMxQjs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQVlILE9BQU8sRUFDTCxPQUFPLEVBQ1AseUJBQXlCLEVBQ3pCLDJCQUEyQixFQUMzQix5QkFBeUIsRUFDekIsZ0JBQWdCLEdBQ2pCLE1BQU0sd0JBQXdCLENBQUM7QUFVaEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFN0QsT0FBTyxFQUFFLG1CQUFtQixFQUFFLFdBQVcsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXBFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVsRSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sV0FBVyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogaXN0YW5idWwgaWdub3JlIGZpbGUgKi9cbi8qKlxuICogb2JzZXJ2ZXItcXVldWUvIOKAlCBSRkMtMDAxIGRlZmVycmVkIG9ic2VydmVyIGRlbGl2ZXJ5LCBCbG9ja3MgMuKAkzUuXG4gKlxuICogVGhlIHB1cmUgXCJvbmUgYmVhdCBiZWhpbmRcIiBwaXBlbGluZTpcbiAqXG4gKiAgIHByb2R1Y2VyIOKUgOKWuiBjYXB0dXJlIChCbG9jayAxLCBgY2FwdHVyZS9lbnZlbG9wZWApIOKUgOKWuiBNZXJnZWRRdWV1ZVxuICogICAoQmxvY2sgMywgc2VxLXN0YW1wZWQgb3ZlciBhIEJvdW5kZWRSaW5nLCBCbG9jayAyKSDilIDilrogRmx1c2hEcml2ZXJcbiAqICAgKEJsb2NrIDQsIGFybWVkLW9uY2UgbWljcm90YXNrIGNoZWNrcG9pbnRzLCBmbHVzaEJ1ZGdldE1zKSDilIDilrpcbiAqICAgRGVmZXJyZWREaXNwYXRjaGVyIChCbG9jayA1LCBpc29sYXRlZCBsaXN0ZW5lcnMgKyBpbmZsaWdodCArIHN0YXRzKVxuICpcbiAqIElOVEVSTkFMIE1PRFVMRSDigJQgZGVsaWJlcmF0ZWx5IE5PVCBleHBvcnRlZCBmcm9tIHRoZSBwdWJsaWMgZm9vdHByaW50anNcbiAqIGJhcnJlbHMgeWV0LiBUaGUgZW5naW5lIHdpcmluZyArIHB1YmxpYyBzdXJmYWNlIGxhbmQgd2l0aCBCbG9ja3MgNuKAkzEwXG4gKiAoc2VlIGRvY3MvZGVzaWduL3JmYy0wMDEtZGVmZXJyZWQtb2JzZXJ2ZXJzLm1kKS4gWmVybyBlbmdpbmUgaW1wb3J0czpcbiAqIHRoaXMgZGlyZWN0b3J5IG1heSBpbXBvcnQgb25seSBgLi4vY2FwdHVyZS9gIGFuZCBpdHMgb3duIGZpbGVzLlxuICovXG5cbmV4cG9ydCB0eXBlIHtcbiAgQ2FwdHVyZUNoYW5uZWwsXG4gIENhcHR1cmVFbnZlbG9wZSxcbiAgQ2FwdHVyZUhvb2tzLFxuICBDYXB0dXJlUG9saWN5LFxuICBDYXB0dXJlUmVxdWVzdCxcbiAgUGF5bG9hZFN1bW1hcnksXG4gIFBheWxvYWRTdW1tYXJ5Tm9kZSxcbiAgUGF5bG9hZFN1bW1hcnlUeXBlLFxufSBmcm9tICcuLi9jYXB0dXJlL2VudmVsb3BlLmpzJztcbmV4cG9ydCB7XG4gIGNhcHR1cmUsXG4gIFBBWUxPQURfU1VNTUFSWV9NQVhfREVQVEgsXG4gIFBBWUxPQURfU1VNTUFSWV9NQVhfRU5UUklFUyxcbiAgUEFZTE9BRF9TVU1NQVJZX01BWF9OT0RFUyxcbiAgc3VtbWFyaXplUGF5bG9hZCxcbn0gZnJvbSAnLi4vY2FwdHVyZS9lbnZlbG9wZS5qcyc7XG5leHBvcnQgdHlwZSB7XG4gIERlZmVycmVkRGlzcGF0Y2hlck9wdGlvbnMsXG4gIERlZmVycmVkTGlzdGVuZXIsXG4gIERpc3BhdGNoRXJyb3JDb250ZXh0LFxuICBEaXNwYXRjaEVycm9ySGFuZGxlcixcbiAgRGlzcGF0Y2hlclN0YXRzLFxuICBEcmFpblJlc3VsdCxcbiAgTGlzdGVuZXJTdGF0cyxcbn0gZnJvbSAnLi9kZWZlcnJlZERpc3BhdGNoZXIuanMnO1xuZXhwb3J0IHsgRGVmZXJyZWREaXNwYXRjaGVyIH0gZnJvbSAnLi9kZWZlcnJlZERpc3BhdGNoZXIuanMnO1xuZXhwb3J0IHR5cGUgeyBGbHVzaERyaXZlck9wdGlvbnMsIEZsdXNoRHJpdmVyU3RhdHMsIEZsdXNoT3V0Y29tZSwgRmx1c2hTeW5jUmVzdWx0IH0gZnJvbSAnLi9mbHVzaERyaXZlci5qcyc7XG5leHBvcnQgeyBGTFVTSF9TQU1QTEVfV0lORE9XLCBGbHVzaERyaXZlciB9IGZyb20gJy4vZmx1c2hEcml2ZXIuanMnO1xuZXhwb3J0IHR5cGUgeyBFbnF1ZXVlSW5wdXQsIEVucXVldWVPdXRjb21lLCBFbnF1ZXVlUmVzdWx0LCBNZXJnZWRRdWV1ZU9wdGlvbnMgfSBmcm9tICcuL21lcmdlZFF1ZXVlLmpzJztcbmV4cG9ydCB7IERFRkFVTFRfTUFYX1FVRVVFLCBNZXJnZWRRdWV1ZSB9IGZyb20gJy4vbWVyZ2VkUXVldWUuanMnO1xuZXhwb3J0IHR5cGUgeyBPdmVyZmxvd1BvbGljeSwgUmluZ0NvdW50ZXJzLCBSaW5nT3B0aW9ucywgUmluZ1B1c2hSZXN1bHQgfSBmcm9tICcuL3JpbmcuanMnO1xuZXhwb3J0IHsgQm91bmRlZFJpbmcgfSBmcm9tICcuL3JpbmcuanMnO1xuIl19
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* observer-queue/mergedQueue.ts — RFC-001 Block 3: seq stamping + multi-channel merge.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: Single totally-ordered staging queue. All three observer
|
|
5
|
+
* channels (`scope` / `flow` / `emit`) funnel through ONE queue;
|
|
6
|
+
* the `seq` counter is assigned at capture under the single JS
|
|
7
|
+
* thread, so drain order == arrival order ACROSS channels with no
|
|
8
|
+
* cross-queue merge logic ever needed.
|
|
9
|
+
* Role: Glue between the capture tier (Block 1) and the flush driver
|
|
10
|
+
* (Block 4). Pure module — imports only `capture/envelope` and
|
|
11
|
+
* the ring (Block 2); zero engine knowledge.
|
|
12
|
+
*
|
|
13
|
+
* Seq semantics (normative, RFC-001 §5):
|
|
14
|
+
* - Stamped BEFORE admission — an event that is then dropped (overflow)
|
|
15
|
+
* or refused (`'block'`) still consumed its seq. Drops therefore leave
|
|
16
|
+
* VISIBLE gaps in the delivered stream (honest loss accounting), and
|
|
17
|
+
* `'block'`-refused events delivered inline keep their true arrival
|
|
18
|
+
* stamp even though they overtake the queued backlog.
|
|
19
|
+
* - Monotonic, starts at 0, never reused for the lifetime of the queue.
|
|
20
|
+
*
|
|
21
|
+
* Enqueue outcomes:
|
|
22
|
+
* - `'queued'` — staged for the next flush (drop-oldest may have evicted
|
|
23
|
+
* an older event to make room; that loss is counted, never silent).
|
|
24
|
+
* - `'dropped'` — the event was sampled out at saturation. Lost; counted.
|
|
25
|
+
* - `'inline'` — `'block'` policy refused the enqueue. NOT lost: the
|
|
26
|
+
* caller (the dispatcher, Block 5) must deliver the returned envelope
|
|
27
|
+
* synchronously inline — blocking delivery by explicit consumer choice.
|
|
28
|
+
*/
|
|
29
|
+
import { capture, } from '../capture/envelope.js';
|
|
30
|
+
import { BoundedRing } from './ring.js';
|
|
31
|
+
/** RFC-001 §5 default queue bound. */
|
|
32
|
+
export const DEFAULT_MAX_QUEUE = 10_000;
|
|
33
|
+
export class MergedQueue {
|
|
34
|
+
ring;
|
|
35
|
+
overflow;
|
|
36
|
+
defaultPolicy;
|
|
37
|
+
hooks;
|
|
38
|
+
/** Arrival stamp — monotonic across ALL channels (see module header). */
|
|
39
|
+
seq = 0;
|
|
40
|
+
constructor(opts) {
|
|
41
|
+
this.overflow = opts?.overflow ?? 'drop-oldest';
|
|
42
|
+
this.ring = new BoundedRing({
|
|
43
|
+
capacity: opts?.maxQueue ?? DEFAULT_MAX_QUEUE,
|
|
44
|
+
policy: this.overflow,
|
|
45
|
+
sampleEvery: opts?.sampleEvery,
|
|
46
|
+
});
|
|
47
|
+
this.defaultPolicy = opts?.capturePolicy ?? 'summary';
|
|
48
|
+
this.hooks = opts?.hooks;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Capture one event (seq-stamped at arrival) and stage it for deferred
|
|
52
|
+
* delivery. `policy` overrides the queue default per call — e.g. `'ref'`
|
|
53
|
+
* for payloads the caller proved immutable. Never throws.
|
|
54
|
+
*/
|
|
55
|
+
enqueue(input, policy) {
|
|
56
|
+
const envelope = capture({
|
|
57
|
+
seq: this.seq,
|
|
58
|
+
channel: input.channel,
|
|
59
|
+
method: input.method,
|
|
60
|
+
runtimeStageId: input.runtimeStageId,
|
|
61
|
+
runId: input.runId,
|
|
62
|
+
payload: input.payload,
|
|
63
|
+
}, policy ?? this.defaultPolicy, this.hooks);
|
|
64
|
+
this.seq += 1;
|
|
65
|
+
const pushed = this.ring.push(envelope);
|
|
66
|
+
if (pushed.accepted)
|
|
67
|
+
return { envelope, outcome: 'queued' };
|
|
68
|
+
return { envelope, outcome: this.overflow === 'block' ? 'inline' : 'dropped' };
|
|
69
|
+
}
|
|
70
|
+
/** Pop the oldest staged envelope (total arrival order across channels). */
|
|
71
|
+
shift() {
|
|
72
|
+
return this.ring.shift();
|
|
73
|
+
}
|
|
74
|
+
/** Current backlog. */
|
|
75
|
+
get depth() {
|
|
76
|
+
return this.ring.size;
|
|
77
|
+
}
|
|
78
|
+
/** Ring capacity (the `maxQueue` bound). */
|
|
79
|
+
get capacity() {
|
|
80
|
+
return this.ring.capacity;
|
|
81
|
+
}
|
|
82
|
+
/** The next seq to be assigned == total events captured so far. */
|
|
83
|
+
get nextSeq() {
|
|
84
|
+
return this.seq;
|
|
85
|
+
}
|
|
86
|
+
/** Lifetime loss/delivery accounting — delegated to the ring. */
|
|
87
|
+
getCounters() {
|
|
88
|
+
return this.ring.getCounters();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVyZ2VkUXVldWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL29ic2VydmVyLXF1ZXVlL21lcmdlZFF1ZXVlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQkc7QUFFSCxPQUFPLEVBS0wsT0FBTyxHQUNSLE1BQU0sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxFQUEwQyxXQUFXLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFaEYsc0NBQXNDO0FBQ3RDLE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQztBQWtDeEMsTUFBTSxPQUFPLFdBQVc7SUFDTCxJQUFJLENBQStCO0lBQ25DLFFBQVEsQ0FBaUI7SUFDekIsYUFBYSxDQUFnQjtJQUM3QixLQUFLLENBQWdCO0lBQ3RDLHlFQUF5RTtJQUNqRSxHQUFHLEdBQUcsQ0FBQyxDQUFDO0lBRWhCLFlBQVksSUFBeUI7UUFDbkMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLEVBQUUsUUFBUSxJQUFJLGFBQWEsQ0FBQztRQUNoRCxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksV0FBVyxDQUFrQjtZQUMzQyxRQUFRLEVBQUUsSUFBSSxFQUFFLFFBQVEsSUFBSSxpQkFBaUI7WUFDN0MsTUFBTSxFQUFFLElBQUksQ0FBQyxRQUFRO1lBQ3JCLFdBQVcsRUFBRSxJQUFJLEVBQUUsV0FBVztTQUMvQixDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksRUFBRSxhQUFhLElBQUksU0FBUyxDQUFDO1FBQ3RELElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxFQUFFLEtBQUssQ0FBQztJQUMzQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILE9BQU8sQ0FBQyxLQUFtQixFQUFFLE1BQXNCO1FBQ2pELE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FDdEI7WUFDRSxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7WUFDYixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87WUFDdEIsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNO1lBQ3BCLGNBQWMsRUFBRSxLQUFLLENBQUMsY0FBYztZQUNwQyxLQUFLLEVBQUUsS0FBSyxDQUFDLEtBQUs7WUFDbEIsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO1NBQ3ZCLEVBQ0QsTUFBTSxJQUFJLElBQUksQ0FBQyxhQUFhLEVBQzVCLElBQUksQ0FBQyxLQUFLLENBQ1gsQ0FBQztRQUNGLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBRWQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDeEMsSUFBSSxNQUFNLENBQUMsUUFBUTtZQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDO1FBQzVELE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQ2pGLENBQUM7SUFFRCw0RUFBNEU7SUFDNUUsS0FBSztRQUNILE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMzQixDQUFDO0lBRUQsdUJBQXVCO0lBQ3ZCLElBQUksS0FBSztRQUNQLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDeEIsQ0FBQztJQUVELDRDQUE0QztJQUM1QyxJQUFJLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQzVCLENBQUM7SUFFRCxtRUFBbUU7SUFDbkUsSUFBSSxPQUFPO1FBQ1QsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBQ2xCLENBQUM7SUFFRCxpRUFBaUU7SUFDakUsV0FBVztRQUNULE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNqQyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIG9ic2VydmVyLXF1ZXVlL21lcmdlZFF1ZXVlLnRzIOKAlCBSRkMtMDAxIEJsb2NrIDM6IHNlcSBzdGFtcGluZyArIG11bHRpLWNoYW5uZWwgbWVyZ2UuXG4gKlxuICogUGF0dGVybjogIFNpbmdsZSB0b3RhbGx5LW9yZGVyZWQgc3RhZ2luZyBxdWV1ZS4gQWxsIHRocmVlIG9ic2VydmVyXG4gKiAgICAgICAgICAgY2hhbm5lbHMgKGBzY29wZWAgLyBgZmxvd2AgLyBgZW1pdGApIGZ1bm5lbCB0aHJvdWdoIE9ORSBxdWV1ZTtcbiAqICAgICAgICAgICB0aGUgYHNlcWAgY291bnRlciBpcyBhc3NpZ25lZCBhdCBjYXB0dXJlIHVuZGVyIHRoZSBzaW5nbGUgSlNcbiAqICAgICAgICAgICB0aHJlYWQsIHNvIGRyYWluIG9yZGVyID09IGFycml2YWwgb3JkZXIgQUNST1NTIGNoYW5uZWxzIHdpdGggbm9cbiAqICAgICAgICAgICBjcm9zcy1xdWV1ZSBtZXJnZSBsb2dpYyBldmVyIG5lZWRlZC5cbiAqIFJvbGU6ICAgICBHbHVlIGJldHdlZW4gdGhlIGNhcHR1cmUgdGllciAoQmxvY2sgMSkgYW5kIHRoZSBmbHVzaCBkcml2ZXJcbiAqICAgICAgICAgICAoQmxvY2sgNCkuIFB1cmUgbW9kdWxlIOKAlCBpbXBvcnRzIG9ubHkgYGNhcHR1cmUvZW52ZWxvcGVgIGFuZFxuICogICAgICAgICAgIHRoZSByaW5nIChCbG9jayAyKTsgemVybyBlbmdpbmUga25vd2xlZGdlLlxuICpcbiAqIFNlcSBzZW1hbnRpY3MgKG5vcm1hdGl2ZSwgUkZDLTAwMSDCpzUpOlxuICogICAtIFN0YW1wZWQgQkVGT1JFIGFkbWlzc2lvbiDigJQgYW4gZXZlbnQgdGhhdCBpcyB0aGVuIGRyb3BwZWQgKG92ZXJmbG93KVxuICogICAgIG9yIHJlZnVzZWQgKGAnYmxvY2snYCkgc3RpbGwgY29uc3VtZWQgaXRzIHNlcS4gRHJvcHMgdGhlcmVmb3JlIGxlYXZlXG4gKiAgICAgVklTSUJMRSBnYXBzIGluIHRoZSBkZWxpdmVyZWQgc3RyZWFtIChob25lc3QgbG9zcyBhY2NvdW50aW5nKSwgYW5kXG4gKiAgICAgYCdibG9jaydgLXJlZnVzZWQgZXZlbnRzIGRlbGl2ZXJlZCBpbmxpbmUga2VlcCB0aGVpciB0cnVlIGFycml2YWxcbiAqICAgICBzdGFtcCBldmVuIHRob3VnaCB0aGV5IG92ZXJ0YWtlIHRoZSBxdWV1ZWQgYmFja2xvZy5cbiAqICAgLSBNb25vdG9uaWMsIHN0YXJ0cyBhdCAwLCBuZXZlciByZXVzZWQgZm9yIHRoZSBsaWZldGltZSBvZiB0aGUgcXVldWUuXG4gKlxuICogRW5xdWV1ZSBvdXRjb21lczpcbiAqICAgLSBgJ3F1ZXVlZCdgICDigJQgc3RhZ2VkIGZvciB0aGUgbmV4dCBmbHVzaCAoZHJvcC1vbGRlc3QgbWF5IGhhdmUgZXZpY3RlZFxuICogICAgIGFuIG9sZGVyIGV2ZW50IHRvIG1ha2Ugcm9vbTsgdGhhdCBsb3NzIGlzIGNvdW50ZWQsIG5ldmVyIHNpbGVudCkuXG4gKiAgIC0gYCdkcm9wcGVkJ2Ag4oCUIHRoZSBldmVudCB3YXMgc2FtcGxlZCBvdXQgYXQgc2F0dXJhdGlvbi4gTG9zdDsgY291bnRlZC5cbiAqICAgLSBgJ2lubGluZSdgICDigJQgYCdibG9jaydgIHBvbGljeSByZWZ1c2VkIHRoZSBlbnF1ZXVlLiBOT1QgbG9zdDogdGhlXG4gKiAgICAgY2FsbGVyICh0aGUgZGlzcGF0Y2hlciwgQmxvY2sgNSkgbXVzdCBkZWxpdmVyIHRoZSByZXR1cm5lZCBlbnZlbG9wZVxuICogICAgIHN5bmNocm9ub3VzbHkgaW5saW5lIOKAlCBibG9ja2luZyBkZWxpdmVyeSBieSBleHBsaWNpdCBjb25zdW1lciBjaG9pY2UuXG4gKi9cblxuaW1wb3J0IHtcbiAgdHlwZSBDYXB0dXJlQ2hhbm5lbCxcbiAgdHlwZSBDYXB0dXJlRW52ZWxvcGUsXG4gIHR5cGUgQ2FwdHVyZUhvb2tzLFxuICB0eXBlIENhcHR1cmVQb2xpY3ksXG4gIGNhcHR1cmUsXG59IGZyb20gJy4uL2NhcHR1cmUvZW52ZWxvcGUuanMnO1xuaW1wb3J0IHsgdHlwZSBPdmVyZmxvd1BvbGljeSwgdHlwZSBSaW5nQ291bnRlcnMsIEJvdW5kZWRSaW5nIH0gZnJvbSAnLi9yaW5nLmpzJztcblxuLyoqIFJGQy0wMDEgwqc1IGRlZmF1bHQgcXVldWUgYm91bmQuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9NQVhfUVVFVUUgPSAxMF8wMDA7XG5cbi8qKiBPbmUgb2JzZXJ2ZXIgZXZlbnQgdG8gbWVyZ2Ug4oCUIHtAbGluayBjYXB0dXJlfSdzIHJlcXVlc3QgbWludXMgYHNlcWAuICovXG5leHBvcnQgaW50ZXJmYWNlIEVucXVldWVJbnB1dCB7XG4gIHJlYWRvbmx5IGNoYW5uZWw6IENhcHR1cmVDaGFubmVsO1xuICByZWFkb25seSBtZXRob2Q6IHN0cmluZztcbiAgcmVhZG9ubHkgcnVudGltZVN0YWdlSWQ6IHN0cmluZztcbiAgcmVhZG9ubHkgcnVuSWQ6IHN0cmluZztcbiAgLyoqIExJVkUgcGF5bG9hZCDigJQgbWF0ZXJpYWxpemVkIHBlciBjYXB0dXJlIHBvbGljeSBhdCBlbnF1ZXVlIHRpbWUuICovXG4gIHJlYWRvbmx5IHBheWxvYWQ6IHVua25vd247XG59XG5cbi8qKiBGYXRlIG9mIG9uZSBlbnF1ZXVlZCBldmVudCDigJQgc2VlIHRoZSBtb2R1bGUgaGVhZGVyLiAqL1xuZXhwb3J0IHR5cGUgRW5xdWV1ZU91dGNvbWUgPSAncXVldWVkJyB8ICdkcm9wcGVkJyB8ICdpbmxpbmUnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEVucXVldWVSZXN1bHQge1xuICAvKiogVGhlIGNhcHR1cmVkLCBzZXEtc3RhbXBlZCBlbnZlbG9wZSAoYnVpbHQgZXZlbiB3aGVuIG5vdCBxdWV1ZWQpLiAqL1xuICByZWFkb25seSBlbnZlbG9wZTogQ2FwdHVyZUVudmVsb3BlO1xuICByZWFkb25seSBvdXRjb21lOiBFbnF1ZXVlT3V0Y29tZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBNZXJnZWRRdWV1ZU9wdGlvbnMge1xuICAvKiogUmluZyBjYXBhY2l0eS4gRGVmYXVsdCB7QGxpbmsgREVGQVVMVF9NQVhfUVVFVUV9ICgxMCAwMDApLiAqL1xuICByZWFkb25seSBtYXhRdWV1ZT86IG51bWJlcjtcbiAgLyoqIE92ZXJmbG93IHBvbGljeSBhdCBjYXBhY2l0eS4gRGVmYXVsdCBgJ2Ryb3Atb2xkZXN0J2AuICovXG4gIHJlYWRvbmx5IG92ZXJmbG93PzogT3ZlcmZsb3dQb2xpY3k7XG4gIC8qKiBgJ3NhbXBsZSdgIG9ubHkg4oCUIGFkbWl0IDEgaW4gdGhpcyBtYW55IHNhdHVyYXRlZCBhcnJpdmFscy4gKi9cbiAgcmVhZG9ubHkgc2FtcGxlRXZlcnk/OiBudW1iZXI7XG4gIC8qKiBEZWZhdWx0IGNhcHR1cmUgcG9saWN5IHdoZW4gYGVucXVldWVgIGdldHMgbm9uZS4gRGVmYXVsdCBgJ3N1bW1hcnknYC4gKi9cbiAgcmVhZG9ubHkgY2FwdHVyZVBvbGljeT86IENhcHR1cmVQb2xpY3k7XG4gIC8qKiBFbmdpbmUtZnJlZSBzZWFtcyAoZGV2LXdhcm4sIGNsb2NrKSBwYXNzZWQgdGhyb3VnaCB0byB7QGxpbmsgY2FwdHVyZX0uICovXG4gIHJlYWRvbmx5IGhvb2tzPzogQ2FwdHVyZUhvb2tzO1xufVxuXG5leHBvcnQgY2xhc3MgTWVyZ2VkUXVldWUge1xuICBwcml2YXRlIHJlYWRvbmx5IHJpbmc6IEJvdW5kZWRSaW5nPENhcHR1cmVFbnZlbG9wZT47XG4gIHByaXZhdGUgcmVhZG9ubHkgb3ZlcmZsb3c6IE92ZXJmbG93UG9saWN5O1xuICBwcml2YXRlIHJlYWRvbmx5IGRlZmF1bHRQb2xpY3k6IENhcHR1cmVQb2xpY3k7XG4gIHByaXZhdGUgcmVhZG9ubHkgaG9va3M/OiBDYXB0dXJlSG9va3M7XG4gIC8qKiBBcnJpdmFsIHN0YW1wIOKAlCBtb25vdG9uaWMgYWNyb3NzIEFMTCBjaGFubmVscyAoc2VlIG1vZHVsZSBoZWFkZXIpLiAqL1xuICBwcml2YXRlIHNlcSA9IDA7XG5cbiAgY29uc3RydWN0b3Iob3B0cz86IE1lcmdlZFF1ZXVlT3B0aW9ucykge1xuICAgIHRoaXMub3ZlcmZsb3cgPSBvcHRzPy5vdmVyZmxvdyA/PyAnZHJvcC1vbGRlc3QnO1xuICAgIHRoaXMucmluZyA9IG5ldyBCb3VuZGVkUmluZzxDYXB0dXJlRW52ZWxvcGU+KHtcbiAgICAgIGNhcGFjaXR5OiBvcHRzPy5tYXhRdWV1ZSA/PyBERUZBVUxUX01BWF9RVUVVRSxcbiAgICAgIHBvbGljeTogdGhpcy5vdmVyZmxvdyxcbiAgICAgIHNhbXBsZUV2ZXJ5OiBvcHRzPy5zYW1wbGVFdmVyeSxcbiAgICB9KTtcbiAgICB0aGlzLmRlZmF1bHRQb2xpY3kgPSBvcHRzPy5jYXB0dXJlUG9saWN5ID8/ICdzdW1tYXJ5JztcbiAgICB0aGlzLmhvb2tzID0gb3B0cz8uaG9va3M7XG4gIH1cblxuICAvKipcbiAgICogQ2FwdHVyZSBvbmUgZXZlbnQgKHNlcS1zdGFtcGVkIGF0IGFycml2YWwpIGFuZCBzdGFnZSBpdCBmb3IgZGVmZXJyZWRcbiAgICogZGVsaXZlcnkuIGBwb2xpY3lgIG92ZXJyaWRlcyB0aGUgcXVldWUgZGVmYXVsdCBwZXIgY2FsbCDigJQgZS5nLiBgJ3JlZidgXG4gICAqIGZvciBwYXlsb2FkcyB0aGUgY2FsbGVyIHByb3ZlZCBpbW11dGFibGUuIE5ldmVyIHRocm93cy5cbiAgICovXG4gIGVucXVldWUoaW5wdXQ6IEVucXVldWVJbnB1dCwgcG9saWN5PzogQ2FwdHVyZVBvbGljeSk6IEVucXVldWVSZXN1bHQge1xuICAgIGNvbnN0IGVudmVsb3BlID0gY2FwdHVyZShcbiAgICAgIHtcbiAgICAgICAgc2VxOiB0aGlzLnNlcSxcbiAgICAgICAgY2hhbm5lbDogaW5wdXQuY2hhbm5lbCxcbiAgICAgICAgbWV0aG9kOiBpbnB1dC5tZXRob2QsXG4gICAgICAgIHJ1bnRpbWVTdGFnZUlkOiBpbnB1dC5ydW50aW1lU3RhZ2VJZCxcbiAgICAgICAgcnVuSWQ6IGlucHV0LnJ1bklkLFxuICAgICAgICBwYXlsb2FkOiBpbnB1dC5wYXlsb2FkLFxuICAgICAgfSxcbiAgICAgIHBvbGljeSA/PyB0aGlzLmRlZmF1bHRQb2xpY3ksXG4gICAgICB0aGlzLmhvb2tzLFxuICAgICk7XG4gICAgdGhpcy5zZXEgKz0gMTtcblxuICAgIGNvbnN0IHB1c2hlZCA9IHRoaXMucmluZy5wdXNoKGVudmVsb3BlKTtcbiAgICBpZiAocHVzaGVkLmFjY2VwdGVkKSByZXR1cm4geyBlbnZlbG9wZSwgb3V0Y29tZTogJ3F1ZXVlZCcgfTtcbiAgICByZXR1cm4geyBlbnZlbG9wZSwgb3V0Y29tZTogdGhpcy5vdmVyZmxvdyA9PT0gJ2Jsb2NrJyA/ICdpbmxpbmUnIDogJ2Ryb3BwZWQnIH07XG4gIH1cblxuICAvKiogUG9wIHRoZSBvbGRlc3Qgc3RhZ2VkIGVudmVsb3BlICh0b3RhbCBhcnJpdmFsIG9yZGVyIGFjcm9zcyBjaGFubmVscykuICovXG4gIHNoaWZ0KCk6IENhcHR1cmVFbnZlbG9wZSB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMucmluZy5zaGlmdCgpO1xuICB9XG5cbiAgLyoqIEN1cnJlbnQgYmFja2xvZy4gKi9cbiAgZ2V0IGRlcHRoKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMucmluZy5zaXplO1xuICB9XG5cbiAgLyoqIFJpbmcgY2FwYWNpdHkgKHRoZSBgbWF4UXVldWVgIGJvdW5kKS4gKi9cbiAgZ2V0IGNhcGFjaXR5KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMucmluZy5jYXBhY2l0eTtcbiAgfVxuXG4gIC8qKiBUaGUgbmV4dCBzZXEgdG8gYmUgYXNzaWduZWQgPT0gdG90YWwgZXZlbnRzIGNhcHR1cmVkIHNvIGZhci4gKi9cbiAgZ2V0IG5leHRTZXEoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zZXE7XG4gIH1cblxuICAvKiogTGlmZXRpbWUgbG9zcy9kZWxpdmVyeSBhY2NvdW50aW5nIOKAlCBkZWxlZ2F0ZWQgdG8gdGhlIHJpbmcuICovXG4gIGdldENvdW50ZXJzKCk6IFJpbmdDb3VudGVycyB7XG4gICAgcmV0dXJuIHRoaXMucmluZy5nZXRDb3VudGVycygpO1xuICB9XG59XG4iXX0=
|