@dbx-tools/postgres 0.6.62
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 +225 -0
- package/index.ts +10 -0
- package/lib/exports.d.ts +1 -0
- package/lib/exports.js +6 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +8 -0
- package/lib/src/advisory-lock.d.ts +51 -0
- package/lib/src/advisory-lock.js +111 -0
- package/lib/src/topic-bus.d.ts +272 -0
- package/lib/src/topic-bus.js +506 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +74 -0
- package/src/advisory-lock.ts +148 -0
- package/src/topic-bus.ts +655 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic fan-out over PostgreSQL `LISTEN`/`NOTIFY`, for telling every running
|
|
3
|
+
* instance of an app that something happened.
|
|
4
|
+
*
|
|
5
|
+
* The sibling `advisory-lock` module is about making sure only ONE connection
|
|
6
|
+
* does a thing; this one is the opposite - EVERY listening session gets every
|
|
7
|
+
* notification. That makes it the right primitive for live UI updates
|
|
8
|
+
* (an SSE stream per browser tab, a cache invalidation, a presence ping) and the
|
|
9
|
+
* wrong one for work distribution: there are no competing consumers, no acks, and
|
|
10
|
+
* no replay.
|
|
11
|
+
*
|
|
12
|
+
* Delivery is best-effort and live. `NOTIFY` reaches sessions that are listening
|
|
13
|
+
* at the moment it commits, so a listener that connects a second later never sees
|
|
14
|
+
* it, and a listener whose connection drops misses everything until the bus
|
|
15
|
+
* reconnects. Use a table or a queue when a subscriber needs durability.
|
|
16
|
+
*
|
|
17
|
+
* ONE CHANNEL, MANY TOPICS. Every bus instance listens on a single Postgres
|
|
18
|
+
* channel (`channel`, default `dbx_tools_topic_bus`) and filters by the
|
|
19
|
+
* envelope's `topic` in-process, so adding a topic costs no connection and no
|
|
20
|
+
* `LISTEN`. The tradeoff is that every listening session decodes every message on
|
|
21
|
+
* the channel; give a genuinely high-volume, unrelated stream its own `channel`
|
|
22
|
+
* rather than a topic.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { hostname } from "node:os";
|
|
27
|
+
import { async as asyncUtil, error, hash, json, object, string } from "@dbx-tools/shared-core";
|
|
28
|
+
/** Channel parts used when a caller names no channel of its own. */
|
|
29
|
+
const DEFAULT_CHANNEL = "dbx_tools_topic_bus";
|
|
30
|
+
/**
|
|
31
|
+
* `NAMEDATALEN - 1`. Postgres TRUNCATES a longer channel name rather than
|
|
32
|
+
* rejecting it, which would split publishers and listeners onto different
|
|
33
|
+
* channels while every call looked like it succeeded.
|
|
34
|
+
*/
|
|
35
|
+
const MAX_CHANNEL_LENGTH = 63;
|
|
36
|
+
/** Base-32 chars of the channel's identity suffix. Max 7 - the digest is 32 bits. */
|
|
37
|
+
const CHANNEL_HASH_LENGTH = 6;
|
|
38
|
+
/** Body used when the parts tokenize to nothing, or lead with a digit. */
|
|
39
|
+
const CHANNEL_FALLBACK = "bus";
|
|
40
|
+
/**
|
|
41
|
+
* Encoded-envelope ceiling. PostgreSQL caps a `NOTIFY` payload at 8000 bytes and
|
|
42
|
+
* fails the statement past that, so the bus rejects the message first with a size
|
|
43
|
+
* it can name. The margin below 8000 covers the server's own accounting.
|
|
44
|
+
*/
|
|
45
|
+
const MAX_NOTIFY_BYTES = 7_900;
|
|
46
|
+
const MIN_RECONNECT_DELAY_MS = 250;
|
|
47
|
+
const MAX_RECONNECT_DELAY_MS = 5_000;
|
|
48
|
+
/**
|
|
49
|
+
* Cached result of the one optional-peer import, resolved to `undefined` when
|
|
50
|
+
* AppKit is not installed. Memoized so a bus in a plain Postgres process pays for
|
|
51
|
+
* the failed resolution once instead of per broadcast.
|
|
52
|
+
*/
|
|
53
|
+
let appKitModule;
|
|
54
|
+
/**
|
|
55
|
+
* Drop keys whose value is `undefined`, so an unset environment variable leaves
|
|
56
|
+
* the key ABSENT rather than present-and-null. Absence is what lets caller
|
|
57
|
+
* metadata and later merge layers supply the key instead.
|
|
58
|
+
*/
|
|
59
|
+
function definedMetadata(values) {
|
|
60
|
+
return Object.fromEntries(Object.entries(values).filter((entry) => {
|
|
61
|
+
return entry[1] !== undefined;
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Context every message gets for free: which project, host, process, and
|
|
66
|
+
* deployment published it.
|
|
67
|
+
*
|
|
68
|
+
* Read fresh per broadcast rather than cached, because a long-lived process can
|
|
69
|
+
* be re-parented (a deployment id appearing after boot). Keys that resolve to
|
|
70
|
+
* nothing are omitted. Public IP is only read from the environment here - the bus
|
|
71
|
+
* never makes a network call to discover it; a process that wants a discovered IP
|
|
72
|
+
* passes a {@link TopicMetadataProvider}.
|
|
73
|
+
*
|
|
74
|
+
* Deliberately NOT included: CPU architecture, runtime name, and runtime version.
|
|
75
|
+
* They are constant per deployment and cost payload bytes against the `NOTIFY`
|
|
76
|
+
* limit on every single message.
|
|
77
|
+
*/
|
|
78
|
+
function machineMetadata() {
|
|
79
|
+
return definedMetadata({
|
|
80
|
+
project: string.firstNonEmpty([
|
|
81
|
+
process.env.DATABRICKS_APP_NAME,
|
|
82
|
+
process.env.DATABRICKS_BUNDLE_NAME,
|
|
83
|
+
process.env.PROJECT_NAME,
|
|
84
|
+
process.env.npm_package_name,
|
|
85
|
+
]) ?? undefined,
|
|
86
|
+
publicIp: string.firstNonEmpty([
|
|
87
|
+
process.env.PUBLIC_IP,
|
|
88
|
+
process.env.DATABRICKS_PUBLIC_IP,
|
|
89
|
+
process.env.HOST_IP,
|
|
90
|
+
]) ?? undefined,
|
|
91
|
+
hostname: hostname(),
|
|
92
|
+
cwd: process.cwd(),
|
|
93
|
+
platform: process.platform,
|
|
94
|
+
pid: process.pid,
|
|
95
|
+
environment: string.trimToNull(process.env.NODE_ENV) ?? undefined,
|
|
96
|
+
appName: string.trimToNull(process.env.DATABRICKS_APP_NAME) ?? undefined,
|
|
97
|
+
deploymentId: string.trimToNull(process.env.DATABRICKS_APP_DEPLOYMENT_ID) ?? undefined,
|
|
98
|
+
databricksHost: string.trimToNull(process.env.DATABRICKS_HOST) ?? undefined,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Identity of the signed-in user on whose behalf this broadcast happens, when the
|
|
103
|
+
* process can tell.
|
|
104
|
+
*
|
|
105
|
+
* `@databricks/appkit` is an OPTIONAL peer, so the import is lazy and a missing
|
|
106
|
+
* module is not an error - this is a plain Postgres package that adds sender
|
|
107
|
+
* identity when it happens to run inside an AppKit app. Outside an active
|
|
108
|
+
* per-request execution context AppKit throws, which means "no user here", so the
|
|
109
|
+
* result is empty metadata rather than a failed broadcast.
|
|
110
|
+
*/
|
|
111
|
+
async function senderMetadata() {
|
|
112
|
+
appKitModule ??= import("@databricks/appkit")
|
|
113
|
+
.then((module) => module)
|
|
114
|
+
.catch(() => undefined);
|
|
115
|
+
const appkit = await appKitModule;
|
|
116
|
+
if (!appkit)
|
|
117
|
+
return {};
|
|
118
|
+
try {
|
|
119
|
+
const context = appkit.getExecutionContext();
|
|
120
|
+
return definedMetadata({
|
|
121
|
+
senderId: string.trimToNull(context.userId) ?? undefined,
|
|
122
|
+
senderName: string.trimToNull(context.userName) ?? undefined,
|
|
123
|
+
senderEmail: string.trimToNull(context.userEmail) ?? undefined,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Derive a legal Postgres channel name from whatever a caller used to identify
|
|
132
|
+
* the channel.
|
|
133
|
+
*
|
|
134
|
+
* Callers think in terms of what the channel IS - an app name, a tenant id, a
|
|
135
|
+
* `[env, feature]` pair, a config object - and none of those are identifiers.
|
|
136
|
+
* Rejecting them pushed the sanitizing onto every call site, which is how two
|
|
137
|
+
* services end up disagreeing about whether the channel is `my-app`, `my_app`,
|
|
138
|
+
* or `myApp` and silently never hear each other.
|
|
139
|
+
*
|
|
140
|
+
* The parts are tokenized and joined into an identifier, then a short hash of the
|
|
141
|
+
* ORIGINAL parts is appended. The hash is what makes the mapping trustworthy:
|
|
142
|
+
* tokenizing alone is lossy, so `my-app` and `my_app` and `myApp` would collapse
|
|
143
|
+
* onto one channel, and a name long enough to hit `NAMEDATALEN` would collide
|
|
144
|
+
* with anything sharing its leading tokens. With the suffix, the readable part
|
|
145
|
+
* stays readable and distinct inputs stay distinct.
|
|
146
|
+
*
|
|
147
|
+
* Deterministic across processes and runs, which is the whole point - two
|
|
148
|
+
* services given the same parts must land on the same channel without
|
|
149
|
+
* coordinating. Hash inputs are structure-aware, so `["a", "b"]` and `["a_b"]`
|
|
150
|
+
* differ, and object key order does not.
|
|
151
|
+
*/
|
|
152
|
+
function channelName(parts) {
|
|
153
|
+
// Hash the CANONICAL form, the same rule `advisoryLockId` uses, so structure and
|
|
154
|
+
// types decide identity: `["a","b"]` differs from `["ab"]`, `1` from `"1"`, and
|
|
155
|
+
// object key order does not matter. Hashing the raw parts would instead lean on
|
|
156
|
+
// the hash module's canonicalizer, which folds every `Date` onto one token.
|
|
157
|
+
const suffix = hash.fnvHashWithOptions({ length: CHANNEL_HASH_LENGTH }, parts.map((part) => object.toStableKey(part)).join("\u0000"));
|
|
158
|
+
// Only parts that stringify to something a reader recognizes contribute to the
|
|
159
|
+
// readable half. An object would tokenize from `String(value)` as
|
|
160
|
+
// `object_object`, which is noise - it still shapes the hash, so identity is
|
|
161
|
+
// unaffected.
|
|
162
|
+
const labelled = parts.filter((part) => {
|
|
163
|
+
const type = typeof part;
|
|
164
|
+
return type === "string" || type === "number" || type === "boolean" || type === "bigint";
|
|
165
|
+
});
|
|
166
|
+
// Truncate rather than cap through the tokenizer: `trim` would DROP a single
|
|
167
|
+
// token longer than the budget, turning one long name into the bare fallback and
|
|
168
|
+
// making every long name look alike. The hash still separates them, but the
|
|
169
|
+
// channel is unreadable in a log.
|
|
170
|
+
const body = string
|
|
171
|
+
.toIdentifierWithOptions({ delimiter: "_" }, ...labelled)
|
|
172
|
+
.slice(0, MAX_CHANNEL_LENGTH - suffix.length - 1)
|
|
173
|
+
.replace(/_+$/, "");
|
|
174
|
+
// An identifier cannot start with a digit and the hash alphabet is
|
|
175
|
+
// digit-leading, so a numeric or empty body needs a letter in front.
|
|
176
|
+
const prefix = /^[A-Za-z_]/.test(body) ? body : `${CHANNEL_FALLBACK}_${body}`;
|
|
177
|
+
return `${prefix}_${suffix}`.replace(/_+/g, "_");
|
|
178
|
+
}
|
|
179
|
+
/** Quote a validated channel for `LISTEN`/`UNLISTEN`, which take no parameters. */
|
|
180
|
+
function quoteIdentifier(value) {
|
|
181
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Parse a `NOTIFY` payload into an envelope, returning `undefined` for anything
|
|
185
|
+
* that is not one.
|
|
186
|
+
*
|
|
187
|
+
* The channel is shared and any Postgres session can `pg_notify` on it, so an
|
|
188
|
+
* unrecognized payload is expected input, not an exception. Publishers run this
|
|
189
|
+
* over their own encoded message too, which is how a body that stringifies but
|
|
190
|
+
* does not round-trip is caught before it ships.
|
|
191
|
+
*/
|
|
192
|
+
function decode(value) {
|
|
193
|
+
if (!value)
|
|
194
|
+
return undefined;
|
|
195
|
+
const record = json.parseRecord(value);
|
|
196
|
+
if (!record ||
|
|
197
|
+
typeof record.id !== "string" ||
|
|
198
|
+
typeof record.topic !== "string" ||
|
|
199
|
+
typeof record.type !== "string" ||
|
|
200
|
+
typeof record.publishedAt !== "string" ||
|
|
201
|
+
!record.metadata ||
|
|
202
|
+
typeof record.metadata !== "object" ||
|
|
203
|
+
Array.isArray(record.metadata) ||
|
|
204
|
+
!("body" in record) ||
|
|
205
|
+
!object.isSerializableValue(record.metadata) ||
|
|
206
|
+
!object.isSerializableValue(record.body)) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
id: record.id,
|
|
211
|
+
topic: record.topic,
|
|
212
|
+
type: record.type,
|
|
213
|
+
metadata: record.metadata,
|
|
214
|
+
body: record.body,
|
|
215
|
+
publishedAt: record.publishedAt,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Broadcasts structured messages by topic and delivers them to every process
|
|
220
|
+
* listening on the same channel.
|
|
221
|
+
*
|
|
222
|
+
* CONNECTION SHAPE. Publishing borrows a pooled connection per call, like any
|
|
223
|
+
* other query. Listening cannot: `LISTEN` is session state, so the bus holds ONE
|
|
224
|
+
* dedicated client out of the pool for as long as it has subscribers, no matter
|
|
225
|
+
* how many topics or listeners are registered. Size the pool with that one
|
|
226
|
+
* long-lived checkout in mind.
|
|
227
|
+
*
|
|
228
|
+
* LIFECYCLE. Construction is inert - nothing connects until the first
|
|
229
|
+
* {@link listen} (or an explicit {@link start}). {@link close} is required to give
|
|
230
|
+
* the connection back; a closed bus stays closed and throws on further use rather
|
|
231
|
+
* than silently reconnecting. Register it with the host's shutdown hook.
|
|
232
|
+
*
|
|
233
|
+
* FAILURE HANDLING. A lost notification connection reconnects on its own with
|
|
234
|
+
* bounded backoff while subscribers remain, reporting each failed attempt through
|
|
235
|
+
* `onError`. Messages published during the gap are lost - `NOTIFY` has no replay.
|
|
236
|
+
* A throwing or rejecting listener never affects the publisher or the other
|
|
237
|
+
* listeners; its failure goes to `onError`.
|
|
238
|
+
*
|
|
239
|
+
* Not safe to share one instance across unrelated channels - construct one bus
|
|
240
|
+
* per channel. The channel itself is DERIVED from the `channel` option rather
|
|
241
|
+
* than taken literally; see {@link PostgresTopicBusOptions.channel} and
|
|
242
|
+
* {@link PostgresTopicBus.channelName}.
|
|
243
|
+
*/
|
|
244
|
+
export class PostgresTopicBus {
|
|
245
|
+
pool;
|
|
246
|
+
/**
|
|
247
|
+
* The resolved Postgres channel this bus listens and publishes on, derived from
|
|
248
|
+
* the `channel` option. Read it to confirm two processes agree, or to log what a
|
|
249
|
+
* set of parts actually resolved to.
|
|
250
|
+
*/
|
|
251
|
+
channelName;
|
|
252
|
+
metadata;
|
|
253
|
+
onError;
|
|
254
|
+
listeners = new Map();
|
|
255
|
+
client;
|
|
256
|
+
starting;
|
|
257
|
+
reconnecting;
|
|
258
|
+
reconnectAbort = new AbortController();
|
|
259
|
+
closed = false;
|
|
260
|
+
constructor(pool, options = {}) {
|
|
261
|
+
this.pool = pool;
|
|
262
|
+
this.channelName = channelName(object.toOneOrMany(options.channel === undefined ? DEFAULT_CHANNEL : options.channel));
|
|
263
|
+
this.metadata = options.metadata;
|
|
264
|
+
this.onError = options.onError ?? (() => undefined);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Open the dedicated notification connection and `LISTEN`.
|
|
268
|
+
*
|
|
269
|
+
* Idempotent, and safe to call concurrently - overlapping calls await the same
|
|
270
|
+
* in-flight connect. {@link listen} calls this, so it is only needed to surface
|
|
271
|
+
* a connection problem at startup rather than on first subscribe. Throws if the
|
|
272
|
+
* bus is closed, or if the pool cannot hand out a connection.
|
|
273
|
+
*/
|
|
274
|
+
async start() {
|
|
275
|
+
if (this.client)
|
|
276
|
+
return;
|
|
277
|
+
if (this.closed)
|
|
278
|
+
throw new Error("Postgres topic bus is closed");
|
|
279
|
+
this.starting ??= this.connect().finally(() => {
|
|
280
|
+
this.starting = undefined;
|
|
281
|
+
});
|
|
282
|
+
await this.starting;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Publish a message to `topic`, returning the envelope that was sent.
|
|
286
|
+
*
|
|
287
|
+
* Resolves once Postgres has accepted the `NOTIFY`, which says nothing about
|
|
288
|
+
* anyone receiving it: sessions not listening at that moment miss it. Publishing
|
|
289
|
+
* needs no {@link start} and no subscribers.
|
|
290
|
+
*
|
|
291
|
+
* Validation is deliberately front-loaded, since a message that fails on the
|
|
292
|
+
* wire is far harder to diagnose than one rejected at the call: `TypeError` for a
|
|
293
|
+
* blank topic or type, or a body/metadata that would not round-trip through JSON
|
|
294
|
+
* unchanged (`object.isSerializableValue`); `RangeError` when the encoded
|
|
295
|
+
* envelope exceeds the `NOTIFY` payload limit, which the automatic metadata
|
|
296
|
+
* counts against - send a reference and let the receiver fetch the payload.
|
|
297
|
+
* Throws if the bus is closed.
|
|
298
|
+
*/
|
|
299
|
+
async broadcast(topic, input) {
|
|
300
|
+
if (!topic.trim())
|
|
301
|
+
throw new TypeError("Topic must not be empty");
|
|
302
|
+
if (!input.type.trim())
|
|
303
|
+
throw new TypeError("Message type must not be empty");
|
|
304
|
+
if (!object.isSerializableValue(input.metadata ?? {}) ||
|
|
305
|
+
!object.isSerializableValue(input.body)) {
|
|
306
|
+
throw new TypeError("Message metadata and body must be JSON serializable without coercion");
|
|
307
|
+
}
|
|
308
|
+
if (this.closed)
|
|
309
|
+
throw new Error("Postgres topic bus is closed");
|
|
310
|
+
const automatic = await this.resolveMetadata();
|
|
311
|
+
const message = {
|
|
312
|
+
id: hash.id(),
|
|
313
|
+
topic,
|
|
314
|
+
type: input.type,
|
|
315
|
+
metadata: { ...automatic, ...input.metadata },
|
|
316
|
+
body: input.body,
|
|
317
|
+
publishedAt: new Date().toISOString(),
|
|
318
|
+
};
|
|
319
|
+
let encoded;
|
|
320
|
+
try {
|
|
321
|
+
encoded = JSON.stringify(message);
|
|
322
|
+
}
|
|
323
|
+
catch (cause) {
|
|
324
|
+
throw new TypeError(`Message must be JSON serializable: ${error.errorMessage(cause)}`);
|
|
325
|
+
}
|
|
326
|
+
if (!decode(encoded)) {
|
|
327
|
+
throw new TypeError("Message type, metadata, and body must be JSON serializable");
|
|
328
|
+
}
|
|
329
|
+
if (Buffer.byteLength(encoded, "utf8") > MAX_NOTIFY_BYTES) {
|
|
330
|
+
throw new RangeError(`Postgres notification exceeds ${MAX_NOTIFY_BYTES} bytes`);
|
|
331
|
+
}
|
|
332
|
+
await this.pool.query("SELECT pg_notify($1, $2)", [this.channelName, encoded]);
|
|
333
|
+
return message;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Subscribe to `topic`, returning the function that unsubscribes.
|
|
337
|
+
*
|
|
338
|
+
* Connects on first use. Several listeners may share a topic; each is called
|
|
339
|
+
* once per message. Only messages published AFTER this resolves arrive, so
|
|
340
|
+
* subscribe before triggering whatever you expect to observe.
|
|
341
|
+
*
|
|
342
|
+
* The returned function removes just this listener and is safe to call twice.
|
|
343
|
+
* The connection stays open once the last listener leaves - {@link close}
|
|
344
|
+
* releases it - so a bus that subscribes and unsubscribes per request does not
|
|
345
|
+
* churn connections.
|
|
346
|
+
*/
|
|
347
|
+
async listen(topic, listener) {
|
|
348
|
+
if (!topic.trim())
|
|
349
|
+
throw new TypeError("Topic must not be empty");
|
|
350
|
+
await this.start();
|
|
351
|
+
const listeners = this.listeners.get(topic) ?? new Set();
|
|
352
|
+
listeners.add(listener);
|
|
353
|
+
this.listeners.set(topic, listeners);
|
|
354
|
+
return async () => {
|
|
355
|
+
listeners.delete(listener);
|
|
356
|
+
if (listeners.size === 0)
|
|
357
|
+
this.listeners.delete(topic);
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Build the automatic half of a message's metadata.
|
|
362
|
+
*
|
|
363
|
+
* Precedence, weakest first: machine/process context, then AppKit sender
|
|
364
|
+
* identity, then this bus's configured metadata. The caller's per-message
|
|
365
|
+
* metadata is layered over the result in {@link broadcast}, so the most specific
|
|
366
|
+
* source always wins and nothing here can overwrite an explicit key.
|
|
367
|
+
*/
|
|
368
|
+
async resolveMetadata() {
|
|
369
|
+
const sender = await senderMetadata();
|
|
370
|
+
const configured = typeof this.metadata === "function" ? await this.metadata() : (this.metadata ?? {});
|
|
371
|
+
return { ...machineMetadata(), ...sender, ...configured };
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Release the notification connection and stop delivering messages. Idempotent.
|
|
375
|
+
*
|
|
376
|
+
* Cancels any pending reconnect, drops all listeners, then `UNLISTEN`s and
|
|
377
|
+
* returns the client to the pool. A failed `UNLISTEN` is reported to the pool as
|
|
378
|
+
* a release error so the connection is DISCARDED rather than handed to the next
|
|
379
|
+
* caller still subscribed to the channel. Does not throw; the bus stays closed.
|
|
380
|
+
*/
|
|
381
|
+
async close() {
|
|
382
|
+
if (this.closed)
|
|
383
|
+
return;
|
|
384
|
+
this.closed = true;
|
|
385
|
+
this.reconnectAbort.abort(new Error("Postgres topic bus is closed"));
|
|
386
|
+
await this.starting?.catch(() => undefined);
|
|
387
|
+
await this.reconnecting?.catch(() => undefined);
|
|
388
|
+
const client = this.client;
|
|
389
|
+
this.client = undefined;
|
|
390
|
+
this.listeners.clear();
|
|
391
|
+
if (!client)
|
|
392
|
+
return;
|
|
393
|
+
client.removeListener("notification", this.handleNotification);
|
|
394
|
+
client.removeListener("error", this.handleClientError);
|
|
395
|
+
let releaseError;
|
|
396
|
+
try {
|
|
397
|
+
await client.query(`UNLISTEN ${quoteIdentifier(this.channelName)}`);
|
|
398
|
+
}
|
|
399
|
+
catch (cause) {
|
|
400
|
+
releaseError = error.toError(cause);
|
|
401
|
+
}
|
|
402
|
+
client.release(releaseError);
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Check out one client, attach the handlers, and `LISTEN`.
|
|
406
|
+
*
|
|
407
|
+
* The handlers are attached BEFORE the `LISTEN` round trip so a notification or
|
|
408
|
+
* error arriving mid-setup is not missed. Any failure - including the bus being
|
|
409
|
+
* closed while the connect was in flight - detaches the handlers and releases
|
|
410
|
+
* the client as errored, so a half-configured connection never returns to the
|
|
411
|
+
* pool.
|
|
412
|
+
*/
|
|
413
|
+
async connect() {
|
|
414
|
+
const client = await this.pool.connect();
|
|
415
|
+
try {
|
|
416
|
+
client.on("notification", this.handleNotification);
|
|
417
|
+
client.on("error", this.handleClientError);
|
|
418
|
+
await client.query(`LISTEN ${quoteIdentifier(this.channelName)}`);
|
|
419
|
+
if (this.closed)
|
|
420
|
+
throw new Error("Postgres topic bus is closed");
|
|
421
|
+
this.client = client;
|
|
422
|
+
}
|
|
423
|
+
catch (cause) {
|
|
424
|
+
client.removeListener("notification", this.handleNotification);
|
|
425
|
+
client.removeListener("error", this.handleClientError);
|
|
426
|
+
client.release(error.toError(cause));
|
|
427
|
+
throw cause;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Route one inbound notification to the topic's listeners.
|
|
432
|
+
*
|
|
433
|
+
* Ignores other channels and undecodable payloads - the channel is shared, so
|
|
434
|
+
* both are ordinary traffic. Listeners are invoked concurrently and their
|
|
435
|
+
* rejections go to `onError`, keeping one slow or broken subscriber from
|
|
436
|
+
* stalling the notification connection.
|
|
437
|
+
*/
|
|
438
|
+
handleNotification = (notification) => {
|
|
439
|
+
if (notification.channel !== this.channelName)
|
|
440
|
+
return;
|
|
441
|
+
const message = decode(notification.payload);
|
|
442
|
+
if (!message)
|
|
443
|
+
return;
|
|
444
|
+
for (const listener of this.listeners.get(message.topic) ?? []) {
|
|
445
|
+
Promise.resolve(listener(message)).catch(this.onError);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
/**
|
|
449
|
+
* Handle the notification connection dying, which `pg` reports as an `error`
|
|
450
|
+
* event rather than a rejected query.
|
|
451
|
+
*
|
|
452
|
+
* The client is unusable at this point, so it is detached and released as
|
|
453
|
+
* errored (which discards it) before anything else. Reconnection only starts
|
|
454
|
+
* when there is still someone to deliver to, so an idle or closing bus does not
|
|
455
|
+
* hold a connection open chasing a channel nobody reads.
|
|
456
|
+
*/
|
|
457
|
+
handleClientError = (cause) => {
|
|
458
|
+
this.onError(cause);
|
|
459
|
+
const client = this.client;
|
|
460
|
+
if (!client)
|
|
461
|
+
return;
|
|
462
|
+
this.client = undefined;
|
|
463
|
+
client.removeListener("notification", this.handleNotification);
|
|
464
|
+
client.removeListener("error", this.handleClientError);
|
|
465
|
+
client.release(cause);
|
|
466
|
+
if (this.closed || this.listeners.size === 0)
|
|
467
|
+
return;
|
|
468
|
+
this.reconnecting ??= this.reconnect().finally(() => {
|
|
469
|
+
this.reconnecting = undefined;
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
/**
|
|
473
|
+
* Re-establish the notification connection with bounded exponential backoff.
|
|
474
|
+
*
|
|
475
|
+
* The first attempt is immediate, since the common case is a single dropped
|
|
476
|
+
* connection that reconnects at once; subsequent delays double from 250ms up to
|
|
477
|
+
* 5s and stay there. Retries indefinitely rather than giving up, because a Postgres restart
|
|
478
|
+
* or a rotated Lakebase credential is a recoverable outage and a silently dead
|
|
479
|
+
* listener is worse than a noisy one. Stops when the bus closes or the last
|
|
480
|
+
* listener leaves, and reports every failed attempt through `onError`.
|
|
481
|
+
*/
|
|
482
|
+
async reconnect() {
|
|
483
|
+
let delay = 0;
|
|
484
|
+
while (!this.closed && this.listeners.size > 0) {
|
|
485
|
+
if (delay > 0) {
|
|
486
|
+
try {
|
|
487
|
+
await asyncUtil.sleep(delay, this.reconnectAbort.signal);
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
await this.start();
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
catch (cause) {
|
|
498
|
+
if (this.closed)
|
|
499
|
+
return;
|
|
500
|
+
this.onError(cause);
|
|
501
|
+
delay = delay === 0 ? MIN_RECONNECT_DELAY_MS : Math.min(delay * 2, MAX_RECONNECT_DELAY_MS);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9waWMtYnVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RvcGljLWJ1cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHO0FBRUgsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNuQyxPQUFPLEVBQUUsS0FBSyxJQUFJLFNBQVMsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFXL0Ysb0VBQW9FO0FBQ3BFLE1BQU0sZUFBZSxHQUFHLHFCQUFxQixDQUFDO0FBQzlDOzs7O0dBSUc7QUFDSCxNQUFNLGtCQUFrQixHQUFHLEVBQUUsQ0FBQztBQUM5QixxRkFBcUY7QUFDckYsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLENBQUM7QUFDOUIsMEVBQTBFO0FBQzFFLE1BQU0sZ0JBQWdCLEdBQUcsS0FBSyxDQUFDO0FBQy9COzs7O0dBSUc7QUFDSCxNQUFNLGdCQUFnQixHQUFHLEtBQUssQ0FBQztBQUMvQixNQUFNLHNCQUFzQixHQUFHLEdBQUcsQ0FBQztBQUNuQyxNQUFNLHNCQUFzQixHQUFHLEtBQUssQ0FBQztBQWtCckM7Ozs7R0FJRztBQUNILElBQUksWUFBMkQsQ0FBQztBQTZHaEU7Ozs7R0FJRztBQUNILFNBQVMsZUFBZSxDQUFDLE1BQXFEO0lBQzVFLE9BQU8sTUFBTSxDQUFDLFdBQVcsQ0FDdkIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQXdDLEVBQUU7UUFDNUUsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssU0FBUyxDQUFDO0lBQ2hDLENBQUMsQ0FBQyxDQUNILENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILFNBQVMsZUFBZTtJQUN0QixPQUFPLGVBQWUsQ0FBQztR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Q2xELElBQUksQ0FBQyxZQUFZLEdBQUcsU0FBUyxDQUFDO1FBQ2hDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDO0lBRUY7Ozs7Ozs7OztPQVNHO0lBQ0ssS0FBSyxDQUFDLFNBQVM7UUFDckIsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO1FBQ2QsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDL0MsSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ2QsSUFBSSxDQUFDO29CQUNILE1BQU0sU0FBUyxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDM0QsQ0FBQztnQkFBQyxNQUFNLENBQUM7b0JBQ1AsT0FBTztnQkFDVCxDQUFDO1lBQ0gsQ0FBQztZQUNELElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDbkIsT0FBTztZQUNULENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLElBQUksSUFBSSxDQUFDLE1BQU07b0JBQUUsT0FBTztnQkFDeEIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDcEIsS0FBSyxHQUFHLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEtBQUssR0FBRyxDQUFDLEVBQUUsc0JBQXNCLENBQUMsQ0FBQztZQUM3RixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVG9waWMgZmFuLW91dCBvdmVyIFBvc3RncmVTUUwgYExJU1RFTmAvYE5PVElGWWAsIGZvciB0ZWxsaW5nIGV2ZXJ5IHJ1bm5pbmdcbiAqIGluc3RhbmNlIG9mIGFuIGFwcCB0aGF0IHNvbWV0aGluZyBoYXBwZW5lZC5cbiAqXG4gKiBUaGUgc2libGluZyBgYWR2aXNvcnktbG9ja2AgbW9kdWxlIGlzIGFib3V0IG1ha2luZyBzdXJlIG9ubHkgT05FIGNvbm5lY3Rpb25cbiAqIGRvZXMgYSB0aGluZzsgdGhpcyBvbmUgaXMgdGhlIG9wcG9zaXRlIC0gRVZFUlkgbGlzdGVuaW5nIHNlc3Npb24gZ2V0cyBldmVyeVxuICogbm90aWZpY2F0aW9uLiBUaGF0IG1ha2VzIGl0IHRoZSByaWdodCBwcmltaXRpdmUgZm9yIGxpdmUgVUkgdXBkYXRlc1xuICogKGFuIFNTRSBzdHJlYW0gcGVyIGJyb3dzZXIgdGFiLCBhIGNhY2hlIGludmFsaWRhdGlvbiwgYSBwcmVzZW5jZSBwaW5nKSBhbmQgdGhlXG4gKiB3cm9uZyBvbmUgZm9yIHdvcmsgZGlzdHJpYnV0aW9uOiB0aGVyZSBhcmUgbm8gY29tcGV0aW5nIGNvbnN1bWVycywgbm8gYWNrcywgYW5kXG4gKiBubyByZXBsYXkuXG4gKlxuICogRGVsaXZlcnkgaXMgYmVzdC1lZmZvcnQgYW5kIGxpdmUuIGBOT1RJRllgIHJlYWNoZXMgc2Vzc2lvbnMgdGhhdCBhcmUgbGlzdGVuaW5nXG4gKiBhdCB0aGUgbW9tZW50IGl0IGNvbW1pdHMsIHNvIGEgbGlzdGVuZXIgdGhhdCBjb25uZWN0cyBhIHNlY29uZCBsYXRlciBuZXZlciBzZWVzXG4gKiBpdCwgYW5kIGEgbGlzdGVuZXIgd2hvc2UgY29ubmVjdGlvbiBkcm9wcyBtaXNzZXMgZXZlcnl0aGluZyB1bnRpbCB0aGUgYnVzXG4gKiByZWNvbm5lY3RzLiBVc2UgYSB0YWJsZSBvciBhIHF1ZXVlIHdoZW4gYSBzdWJzY3JpYmVyIG5lZWRzIGR1cmFiaWxpdHkuXG4gKlxuICogT05FIENIQU5ORUwsIE1BTlkgVE9QSUNTLiBFdmVyeSBidXMgaW5zdGFuY2UgbGlzdGVucyBvbiBhIHNpbmdsZSBQb3N0Z3Jlc1xuICogY2hhbm5lbCAoYGNoYW5uZWxgLCBkZWZhdWx0IGBkYnhfdG9vbHNfdG9waWNfYnVzYCkgYW5kIGZpbHRlcnMgYnkgdGhlXG4gKiBlbnZlbG9wZSdzIGB0b3BpY2AgaW4tcHJvY2Vzcywgc28gYWRkaW5nIGEgdG9waWMgY29zdHMgbm8gY29ubmVjdGlvbiBhbmQgbm9cbiAqIGBMSVNURU5gLiBUaGUgdHJhZGVvZmYgaXMgdGhhdCBldmVyeSBsaXN0ZW5pbmcgc2Vzc2lvbiBkZWNvZGVzIGV2ZXJ5IG1lc3NhZ2Ugb25cbiAqIHRoZSBjaGFubmVsOyBnaXZlIGEgZ2VudWluZWx5IGhpZ2gtdm9sdW1lLCB1bnJlbGF0ZWQgc3RyZWFtIGl0cyBvd24gYGNoYW5uZWxgXG4gKiByYXRoZXIgdGhhbiBhIHRvcGljLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBob3N0bmFtZSB9IGZyb20gXCJub2RlOm9zXCI7XG5pbXBvcnQgeyBhc3luYyBhcyBhc3luY1V0aWwsIGVycm9yLCBoYXNoLCBqc29uLCBvYmplY3QsIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgdHlwZSB7IE5vdGlmaWNhdGlvbiwgUG9vbENsaWVudCB9IGZyb20gXCJwZ1wiO1xuXG5pbXBvcnQgdHlwZSB7IFBnUG9vbExpa2UsIFBnUXVlcnlhYmxlIH0gZnJvbSBcIi4vYWR2aXNvcnktbG9jay50c1wiO1xuXG4vKipcbiAqIGBAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlYCBvd25zIHRoZSBKU09OLXJvdW5kLXRyaXAgcnVsZTsgdGhpcyBhbGlhcyBqdXN0IGtlZXBzIHRoZVxuICogZ2VuZXJpYyBzaWduYXR1cmVzIGJlbG93IHJlYWRhYmxlLiBDb25zdW1lcnMgaW1wb3J0IHRoZSB0eXBlIGZyb20gc2hhcmVkLWNvcmUuXG4gKi9cbnR5cGUgU2VyaWFsaXphYmxlVmFsdWUgPSBvYmplY3QuU2VyaWFsaXphYmxlVmFsdWU7XG5cbi8qKiBDaGFubmVsIHBhcnRzIHVzZWQgd2hlbiBhIGNhbGxlciBuYW1lcyBubyBjaGFubmVsIG9mIGl0cyBvd24uICovXG5jb25zdCBERUZBVUxUX0NIQU5ORUwgPSBcImRieF90b29sc190b3BpY19idXNcIjtcbi8qKlxuICogYE5BTUVEQVRBTEVOIC0gMWAuIFBvc3RncmVzIFRSVU5DQVRFUyBhIGxvbmdlciBjaGFubmVsIG5hbWUgcmF0aGVyIHRoYW5cbiAqIHJlamVjdGluZyBpdCwgd2hpY2ggd291bGQgc3BsaXQgcHVibGlzaGVycyBhbmQgbGlzdGVuZXJzIG9udG8gZGlmZmVyZW50XG4gKiBjaGFubmVscyB3aGlsZSBldmVyeSBjYWxsIGxvb2tlZCBsaWtlIGl0IHN1Y2NlZWRlZC5cbiAqL1xuY29uc3QgTUFYX0NIQU5ORUxfTEVOR1RIID0gNjM7XG4vKiogQmFzZS0zMiBjaGFycyBvZiB0aGUgY2hhbm5lbCdzIGlkZW50aXR5IHN1ZmZpeC4gTWF4IDcgLSB0aGUgZGlnZXN0IGlzIDMyIGJpdHMuICovXG5jb25zdCBDSEFOTkVMX0hBU0hfTEVOR1RIID0gNjtcbi8qKiBCb2R5IHVzZWQgd2hlbiB0aGUgcGFydHMgdG9rZW5pemUgdG8gbm90aGluZywgb3IgbGVhZCB3aXRoIGEgZGlnaXQuICovXG5jb25zdCBDSEFOTkVMX0ZBTExCQUNLID0gXCJidXNcIjtcbi8qKlxuICogRW5jb2RlZC1lbnZlbG9wZSBjZWlsaW5nLiBQb3N0Z3JlU1FMIGNhcHMgYSBgTk9USUZZYCBwYXlsb2FkIGF0IDgwMDAgYnl0ZXMgYW5kXG4gKiBmYWlscyB0aGUgc3RhdGVtZW50IHBhc3QgdGhhdCwgc28gdGhlIGJ1cyByZWplY3RzIHRoZSBtZXNzYWdlIGZpcnN0IHdpdGggYSBzaXplXG4gKiBpdCBjYW4gbmFtZS4gVGhlIG1hcmdpbiBiZWxvdyA4MDAwIGNvdmVycyB0aGUgc2VydmVyJ3Mgb3duIGFjY291bnRpbmcuXG4gKi9cbmNvbnN0IE1BWF9OT1RJRllfQllURVMgPSA3XzkwMDtcbmNvbnN0IE1JTl9SRUNPTk5FQ1RfREVMQVlfTVMgPSAyNTA7XG5jb25zdCBNQVhfUkVDT05ORUNUX0RFTEFZX01TID0gNV8wMDA7XG5cbi8qKlxuICogVGhlIHN1YnNldCBvZiBBcHBLaXQncyBwZXItcmVxdWVzdCBleGVjdXRpb24gY29udGV4dCB0aGUgYnVzIHJlYWRzIGZvciBzZW5kZXJcbiAqIGlkZW50aXR5LiBGaWVsZHMgYXJlIGB1bmtub3duYCBiZWNhdXNlIHRoaXMgaXMgYSBzdHJ1Y3R1cmFsIHZpZXcgb2YgYW4gT1BUSU9OQUxcbiAqIHBlZXI6IHRoZSB2YWx1ZXMgYXJlIHRyaW1tZWQgYW5kIHZhbGlkYXRlZCByYXRoZXIgdGhhbiB0cnVzdGVkLlxuICovXG50eXBlIEFwcEtpdEV4ZWN1dGlvbkNvbnRleHQgPSB7XG4gIHVzZXJFbWFpbD86IHVua25vd247XG4gIHVzZXJJZD86IHVua25vd247XG4gIHVzZXJOYW1lPzogdW5rbm93bjtcbn07XG5cbi8qKiBTdHJ1Y3R1cmFsIHZpZXcgb2YgYEBkYXRhYnJpY2tzL2FwcGtpdGAsIGltcG9ydGVkIGxhemlseSBhbmQgbmV2ZXIgcmVxdWlyZWQuICovXG50eXBlIEFwcEtpdE1vZHVsZSA9IHtcbiAgZ2V0RXhlY3V0aW9uQ29udGV4dCgpOiBBcHBLaXRFeGVjdXRpb25Db250ZXh0O1xufTtcblxuLyoqXG4gKiBDYWNoZWQgcmVzdWx0IG9mIHRoZSBvbmUgb3B0aW9uYWwtcGVlciBpbXBvcnQsIHJlc29sdmVkIHRvIGB1bmRlZmluZWRgIHdoZW5cbiAqIEFwcEtpdCBpcyBub3QgaW5zdGFsbGVkLiBNZW1vaXplZCBzbyBhIGJ1cyBpbiBhIHBsYWluIFBvc3RncmVzIHByb2Nlc3MgcGF5cyBmb3JcbiAqIHRoZSBmYWlsZWQgcmVzb2x1dGlvbiBvbmNlIGluc3RlYWQgb2YgcGVyIGJyb2FkY2FzdC5cbiAqL1xubGV0IGFwcEtpdE1vZHVsZTogUHJvbWlzZTxBcHBLaXRNb2R1bGUgfCB1bmRlZmluZWQ+IHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIEZsYXQta2V5ZWQgY29udGV4dCB0cmF2ZWxsaW5nIGFsb25nc2lkZSBhIG1lc3NhZ2UgYm9keTogd2hvIHNlbnQgaXQsIGZyb21cbiAqIHdoZXJlLCBpbiB3aGljaCBkZXBsb3ltZW50LiBWYWx1ZXMgbWF5IG5lc3QsIGJ1dCB0aGUga2V5cyBhcmUgdGhlIGFkZHJlc3NhYmxlXG4gKiBwYXJ0IC0gYSBsaXN0ZW5lciBmaWx0ZXJpbmcgb3IgbGFiZWxsaW5nIG1lc3NhZ2VzIHJlYWRzIGBtZXRhZGF0YS51c2VyYCwgbm90IGFcbiAqIHBhdGggaW50byB0aGUgYm9keS5cbiAqL1xuZXhwb3J0IHR5cGUgVG9waWNNZXRhZGF0YSA9IFJlY29yZDxzdHJpbmcsIFNlcmlhbGl6YWJsZVZhbHVlPjtcblxuLyoqXG4gKiBUaGUgd2lyZSBlbnZlbG9wZSBldmVyeSBzdWJzY3JpYmVyIHJlY2VpdmVzLCBhbmQgd2hhdCB7QGxpbmtcbiAqIFBvc3RncmVzVG9waWNCdXMuYnJvYWRjYXN0fSByZXR1cm5zIHRvIHRoZSBwdWJsaXNoZXIuXG4gKlxuICogYGlkYCwgYHRvcGljYCwgYW5kIGBwdWJsaXNoZWRBdGAgYXJlIGFzc2lnbmVkIGJ5IHRoZSBidXM7IGB0eXBlYCwgYG1ldGFkYXRhYCxcbiAqIGFuZCBgYm9keWAgY29tZSBmcm9tIHRoZSBjYWxsZXIgKHdpdGggYXV0b21hdGljIGNvbnRleHQgbWVyZ2VkIHVuZGVyXG4gKiBgbWV0YWRhdGFgKS4gVGhlIHNoYXBlIGlzIHN0YWJsZSBlbm91Z2ggdG8gaGFuZCBzdHJhaWdodCB0byBhbiBTU0UgYGRhdGE6YFxuICogZnJhbWUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVG9waWNNZXNzYWdlPFRCb2R5IGV4dGVuZHMgU2VyaWFsaXphYmxlVmFsdWUgPSBTZXJpYWxpemFibGVWYWx1ZT4ge1xuICAvKipcbiAgICogUGVyLW1lc3NhZ2UgaWRlbnRpdHkgZ2VuZXJhdGVkIGJ5IHRoZSBwdWJsaXNoZXIsIHVuaXF1ZSBhY3Jvc3MgaW5zdGFuY2VzLlxuICAgKiBTdWl0YWJsZSBmb3IgZGVkdXBlIHdoZW4gYSBjbGllbnQgcmVjb25uZWN0cyBhbmQgZm9yIGFuIFNTRSBgaWQ6YCBmaWVsZC4gTm90XG4gICAqIG9yZGVyZWQsIGFuZCBub3QgYSBkYXRhYmFzZSBrZXkuXG4gICAqL1xuICBpZDogc3RyaW5nO1xuICAvKiogVGhlIHRvcGljIHRoaXMgd2FzIGJyb2FkY2FzdCBvbjsgbGlzdGVuZXJzIG9uIG90aGVyIHRvcGljcyBuZXZlciBzZWUgaXQuICovXG4gIHRvcGljOiBzdHJpbmc7XG4gIC8qKiBDYWxsZXItY2hvc2VuIGV2ZW50IG5hbWUsIGUuZy4gYG9yZGVyLnVwZGF0ZWRgLiBOZXZlciBlbXB0eS4gKi9cbiAgdHlwZTogc3RyaW5nO1xuICAvKiogQXV0b21hdGljIGNvbnRleHQgbWVyZ2VkIHdpdGggY2FsbGVyIG1ldGFkYXRhLiBTZWUge0BsaW5rIFRvcGljTWV0YWRhdGF9LiAqL1xuICBtZXRhZGF0YTogVG9waWNNZXRhZGF0YTtcbiAgLyoqIFRoZSBjYWxsZXIncyBwYXlsb2FkLCB1bmNoYW5nZWQuICovXG4gIGJvZHk6IFRCb2R5O1xuICAvKipcbiAgICogSVNPLTg2MDEgcHVibGlzaCB0aW1lIGZyb20gdGhlIFBVQkxJU0hJTkcgcHJvY2VzcydzIGNsb2NrLCBub3QgdGhlIGRhdGFiYXNlJ3MuXG4gICAqIEZpbmUgZm9yIGRpc3BsYXk7IGRvIG5vdCBvcmRlciBtZXNzYWdlcyBmcm9tIGRpZmZlcmVudCBpbnN0YW5jZXMgYnkgaXQuXG4gICAqL1xuICBwdWJsaXNoZWRBdDogc3RyaW5nO1xufVxuXG4vKiogV2hhdCBhIGNhbGxlciBzdXBwbGllcyB0byB7QGxpbmsgUG9zdGdyZXNUb3BpY0J1cy5icm9hZGNhc3R9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBUb3BpY1B1Ymxpc2hJbnB1dDxUQm9keSBleHRlbmRzIFNlcmlhbGl6YWJsZVZhbHVlID0gU2VyaWFsaXphYmxlVmFsdWU+IHtcbiAgLyoqIEV2ZW50IG5hbWUsIGUuZy4gYGNoYXQubWVzc2FnZWAuIE11c3QgYmUgbm9uLWJsYW5rLiAqL1xuICB0eXBlOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBDb250ZXh0IHRvIGF0dGFjaC4gV2lucyBvdmVyIGFueSBhdXRvbWF0aWMga2V5IG9mIHRoZSBzYW1lIG5hbWUsIHNvIGEgY2FsbGVyXG4gICAqIGNhbiBvdmVycmlkZSBhIG1hY2hpbmUgZGVmYXVsdCAoYHByb2plY3RgKSBvciBhZGQgaXRzIG93biAoYHRyYWNlSWRgKS5cbiAgICovXG4gIG1ldGFkYXRhPzogVG9waWNNZXRhZGF0YTtcbiAgLyoqIFRoZSBwYXlsb2FkLiBNdXN0IHNhdGlzZnkgYG9iamVjdC5pc1NlcmlhbGl6YWJsZVZhbHVlYC4gKi9cbiAgYm9keTogVEJvZHk7XG59XG5cbi8qKlxuICogU3Vic2NyaWJlciBjYWxsYmFjay4gSW52b2tlZCBvbmNlIHBlciBtYXRjaGluZyBtZXNzYWdlIG9uIHRoZSBub3RpZmljYXRpb25cbiAqIGNvbm5lY3Rpb24ncyBjYWxsYmFjaywgc28gaXQgc2hvdWxkIG5vdCBibG9jazogYSByZXR1cm5lZCBwcm9taXNlIGlzIGF3YWl0ZWRcbiAqIG9ubHkgdG8gcm91dGUgYSByZWplY3Rpb24gdG8gYG9uRXJyb3JgLCBhbmQgbGlzdGVuZXJzIGZvciBvbmUgbWVzc2FnZSBydW5cbiAqIGNvbmN1cnJlbnRseSByYXRoZXIgdGhhbiBpbiByZWdpc3RyYXRpb24gb3JkZXIuXG4gKlxuICogYFRCb2R5YCBpcyB1bmNoZWNrZWQgYXQgcnVudGltZS4gVGhlIGJ1cyBndWFyYW50ZWVzIHRoZSBib2R5IGlzIHNlcmlhbGl6YWJsZSxcbiAqIG5vdCB0aGF0IGl0IG1hdGNoZXMgdGhlIHR5cGUgcGFyYW1ldGVyLCBzbyB2YWxpZGF0ZSBhbnl0aGluZyBhIGxpc3RlbmVyXG4gKiBicmFuY2hlcyBvbi5cbiAqL1xuZXhwb3J0IHR5cGUgVG9waWNMaXN0ZW5lcjxUQm9keSBleHRlbmRzIFNlcmlhbGl6YWJsZVZhbHVlID0gU2VyaWFsaXphYmxlVmFsdWU+ID0gKFxuICBtZXNzYWdlOiBUb3BpY01lc3NhZ2U8VEJvZHk+LFxuKSA9PiB2b2lkIHwgUHJvbWlzZUxpa2U8dm9pZD47XG5cbi8qKlxuICogUmVzb2x2ZXMgbWV0YWRhdGEgYXQgcHVibGlzaCB0aW1lIGluc3RlYWQgb2YgY29uc3RydWN0aW9uIHRpbWUsIGZvciBjb250ZXh0IGFcbiAqIHByb2Nlc3MgbGVhcm5zIGxhdGUgb3IgYXN5bmNocm9ub3VzbHkgLSBhIGRpc2NvdmVyZWQgcHVibGljIElQLCBhbiBpbnN0YW5jZSBpZFxuICogZnJvbSBhIGNvbnRyb2wgcGxhbmUuIENhbGxlZCBvbiBldmVyeSBicm9hZGNhc3QsIHNvIG1lbW9pemUgYW55dGhpbmcgZXhwZW5zaXZlO1xuICogYSByZWplY3Rpb24gZmFpbHMgdGhlIGJyb2FkY2FzdC5cbiAqL1xuZXhwb3J0IHR5cGUgVG9waWNNZXRhZGF0YVByb3ZpZGVyID0gKCkgPT4gVG9waWNNZXRhZGF0YSB8IFByb21pc2VMaWtlPFRvcGljTWV0YWRhdGE+O1xuXG4vKiogQ29uc3RydWN0aW9uIG9wdGlvbnMgZm9yIHtAbGluayBQb3N0Z3Jlc1RvcGljQnVzfS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUG9zdGdyZXNUb3BpY0J1c09wdGlvbnMge1xuICAvKipcbiAgICogV2hhdCBpZGVudGlmaWVzIHRoaXMgY2hhbm5lbCAtIGFueXRoaW5nLCBub3QganVzdCBhbiBpZGVudGlmaWVyOiBhIG5hbWUsIGFuXG4gICAqIGlkLCBhIGBbZW52LCBmZWF0dXJlXWAgcGFpciwgYSBjb25maWcgb2JqZWN0LiBUaGUgcGFydHMgYXJlIHRva2VuaXplZCBpbnRvIGFcbiAgICogbGVnYWwgUG9zdGdyZXMgY2hhbm5lbCBuYW1lIHdpdGggYSBzaG9ydCBoYXNoIG9mIHRoZSBvcmlnaW5hbHMgYXBwZW5kZWQsIHNvXG4gICAqIHRoZSBkZXJpdmF0aW9uIGlzIGRldGVybWluaXN0aWMgYW5kIG5vIGNhbGwgc2l0ZSBoYXMgdG8gc2FuaXRpemUuIFNlZVxuICAgKiB7QGxpbmsgUG9zdGdyZXNUb3BpY0J1cy5jaGFubmVsTmFtZX0gZm9yIHRoZSByZXNvbHZlZCBuYW1lLlxuICAgKlxuICAgKiBPbmUgdmFsdWUgb3IgbWFueTogYW4gYXJyYXkgaXMgcmVhZCBhcyBtdWx0aXBsZSBwYXJ0c1xuICAgKiAoe0BsaW5rIG9iamVjdC5PbmVPck1hbnl9KSwgYW55dGhpbmcgZWxzZSBhcyBhIHNpbmdsZSBwYXJ0LiBTb1xuICAgKiBgW1wiYmlsbGluZ1wiLCBcInByb2RcIl1gIGFuZCBgXCJiaWxsaW5nX3Byb2RcImAgYXJlIGRpZmZlcmVudCBjaGFubmVscywgc2luY2UgdGhlXG4gICAqIGhhc2ggc2VlcyBkaWZmZXJlbnQgc3RydWN0dXJlLlxuICAgKlxuICAgKiBFdmVyeSBwYXJ0aWNpcGF0aW5nIHByb2Nlc3MgbXVzdCBwYXNzIEVRVUlWQUxFTlQgcGFydHMsIHNpbmNlIGEgZGlmZmVyZW50XG4gICAqIHNwZWxsaW5nIGhhc2hlcyB0byBhIGRpZmZlcmVudCBjaGFubmVsLiBEZWZhdWx0cyB0byBhIHNoYXJlZFxuICAgKiBgZGJ4X3Rvb2xzX3RvcGljX2J1c2AgY2hhbm5lbC5cbiAgICovXG4gIGNoYW5uZWw/OiB1bmtub3duO1xuICAvKipcbiAgICogRXh0cmEgY29udGV4dCBhZGRlZCB0byBldmVyeSBtZXNzYWdlIHRoaXMgYnVzIHB1Ymxpc2hlcywgZWl0aGVyIGEgZml4ZWRcbiAgICogcmVjb3JkIG9yIGEge0BsaW5rIFRvcGljTWV0YWRhdGFQcm92aWRlcn0gY2FsbGVkIHBlciBicm9hZGNhc3QuIE92ZXJyaWRlc1xuICAgKiBhdXRvbWF0aWMgbWFjaGluZSBrZXlzOyBwZXItY2FsbCBgbWV0YWRhdGFgIG92ZXJyaWRlcyB0aGlzLlxuICAgKi9cbiAgbWV0YWRhdGE/OiBUb3BpY01ldGFkYXRhIHwgVG9waWNNZXRhZGF0YVByb3ZpZGVyO1xuICAvKipcbiAgICogU2luayBmb3IgZmFpbHVyZXMgdGhhdCBoYXZlIG5vIGNhbGxlciB0byB0aHJvdyB0bzogYSBsaXN0ZW5lciB0aGF0IHJlamVjdGVkLFxuICAgKiBhIGRyb3BwZWQgbm90aWZpY2F0aW9uIGNvbm5lY3Rpb24sIGEgZmFpbGVkIHJlY29ubmVjdCBhdHRlbXB0LiBEZWZhdWx0cyB0b1xuICAgKiBzd2FsbG93aW5nIHRoZW0sIHNvIHdpcmUgdGhpcyB0byBhIGxvZ2dlciBpbiBhbnl0aGluZyBsb25nLXJ1bm5pbmcuXG4gICAqL1xuICBvbkVycm9yPzogKGNhdXNlOiB1bmtub3duKSA9PiB2b2lkO1xufVxuXG4vKipcbiAqIERyb3Aga2V5cyB3aG9zZSB2YWx1ZSBpcyBgdW5kZWZpbmVkYCwgc28gYW4gdW5zZXQgZW52aXJvbm1lbnQgdmFyaWFibGUgbGVhdmVzXG4gKiB0aGUga2V5IEFCU0VOVCByYXRoZXIgdGhhbiBwcmVzZW50LWFuZC1udWxsLiBBYnNlbmNlIGlzIHdoYXQgbGV0cyBjYWxsZXJcbiAqIG1ldGFkYXRhIGFuZCBsYXRlciBtZXJnZSBsYXllcnMgc3VwcGx5IHRoZSBrZXkgaW5zdGVhZC5cbiAqL1xuZnVuY3Rpb24gZGVmaW5lZE1ldGFkYXRhKHZhbHVlczogUmVjb3JkPHN0cmluZywgU2VyaWFsaXphYmxlVmFsdWUgfCB1bmRlZmluZWQ+KTogVG9waWNNZXRhZGF0YSB7XG4gIHJldHVybiBPYmplY3QuZnJvbUVudHJpZXMoXG4gICAgT2JqZWN0LmVudHJpZXModmFsdWVzKS5maWx0ZXIoKGVudHJ5KTogZW50cnkgaXMgW3N0cmluZywgU2VyaWFsaXphYmxlVmFsdWVdID0+IHtcbiAgICAgIHJldHVybiBlbnRyeVsxXSAhPT0gdW5kZWZpbmVkO1xuICAgIH0pLFxuICApO1xufVxuXG4vKipcbiAqIENvbnRleHQgZXZlcnkgbWVzc2FnZSBnZXRzIGZvciBmcmVlOiB3aGljaCBwcm9qZWN0LCBob3N0LCBwcm9jZXNzLCBhbmRcbiAqIGRlcGxveW1lbnQgcHVibGlzaGVkIGl0LlxuICpcbiAqIFJlYWQgZnJlc2ggcGVyIGJyb2FkY2FzdCByYXRoZXIgdGhhbiBjYWNoZWQsIGJlY2F1c2UgYSBsb25nLWxpdmVkIHByb2Nlc3MgY2FuXG4gKiBiZSByZS1wYXJlbnRlZCAoYSBkZXBsb3ltZW50IGlkIGFwcGVhcmluZyBhZnRlciBib290KS4gS2V5cyB0aGF0IHJlc29sdmUgdG9cbiAqIG5vdGhpbmcgYXJlIG9taXR0ZWQuIFB1YmxpYyBJUCBpcyBvbmx5IHJlYWQgZnJvbSB0aGUgZW52aXJvbm1lbnQgaGVyZSAtIHRoZSBidXNcbiAqIG5ldmVyIG1ha2VzIGEgbmV0d29yayBjYWxsIHRvIGRpc2NvdmVyIGl0OyBhIHByb2Nlc3MgdGhhdCB3YW50cyBhIGRpc2NvdmVyZWQgSVBcbiAqIHBhc3NlcyBhIHtAbGluayBUb3BpY01ldGFkYXRhUHJvdmlkZXJ9LlxuICpcbiAqIERlbGliZXJhdGVseSBOT1QgaW5jbHVkZWQ6IENQVSBhcmNoaXRlY3R1cmUsIHJ1bnRpbWUgbmFtZSwgYW5kIHJ1bnRpbWUgdmVyc2lvbi5cbiAqIFRoZXkgYXJlIGNvbnN0YW50IHBlciBkZXBsb3ltZW50IGFuZCBjb3N0IHBheWxvYWQgYnl0ZXMgYWdhaW5zdCB0aGUgYE5PVElGWWBcbiAqIGxpbWl0IG9uIGV2ZXJ5IHNpbmdsZSBtZXNzYWdlLlxuICovXG5mdW5jdGlvbiBtYWNoaW5lTWV0YWRhdGEoKTogVG9waWNNZXRhZGF0YSB7XG4gIHJldHVybiBkZWZpbmVkTWV0YWRhdGEoe1xuICAgIHByb2plY3Q6XG4gICAgICBzdHJpbmcuZmlyc3ROb25FbXB0eShbXG4gICAgICAgIHByb2Nlc3MuZW52LkRBVEFCUklDS1NfQVBQX05BTUUsXG4gICAgICAgIHByb2Nlc3MuZW52LkRBVEFCUklDS1NfQlVORExFX05BTUUsXG4gICAgICAgIHByb2Nlc3MuZW52LlBST0pFQ1RfTkFNRSxcbiAgICAgICAgcHJvY2Vzcy5lbnYubnBtX3BhY2thZ2VfbmFtZSxcbiAgICAgIF0pID8/IHVuZGVmaW5lZCxcbiAgICBwdWJsaWNJcDpcbiAgICAgIHN0cmluZy5maXJzdE5vbkVtcHR5KFtcbiAgICAgICAgcHJvY2Vzcy5lbnYuUFVCTElDX0lQLFxuICAgICAgICBwcm9jZXNzLmVudi5EQVRBQlJJQ0tTX1BVQkxJQ19JUCxcbiAgICAgICAgcHJvY2Vzcy5lbnYuSE9TVF9JUCxcbiAgICAgIF0pID8/IHVuZGVmaW5lZCxcbiAgICBob3N0bmFtZTogaG9zdG5hbWUoKSxcbiAgICBjd2Q6IHByb2Nlc3MuY3dkKCksXG4gICAgcGxhdGZvcm06IHByb2Nlc3MucGxhdGZvcm0sXG4gICAgcGlkOiBwcm9jZXNzLnBpZCxcbiAgICBlbnZpcm9ubWVudDogc3RyaW5nLnRyaW1Ub051bGwocHJvY2Vzcy5lbnYuTk9ERV9FTlYpID8/IHVuZGVmaW5lZCxcbiAgICBhcHBOYW1lOiBzdHJpbmcudHJpbVRvTnVsbChwcm9jZXNzLmVudi5EQVRBQlJJQ0tTX0FQUF9OQU1FKSA/PyB1bmRlZmluZWQsXG4gICAgZGVwbG95bWVudElkOiBzdHJpbmcudHJpbVRvTnVsbChwcm9jZXNzLmVudi5EQVRBQlJJQ0tTX0FQUF9ERVBMT1lNRU5UX0lEKSA/PyB1bmRlZmluZWQsXG4gICAgZGF0YWJyaWNrc0hvc3Q6IHN0cmluZy50cmltVG9OdWxsKHByb2Nlc3MuZW52LkRBVEFCUklDS1NfSE9TVCkgPz8gdW5kZWZpbmVkLFxuICB9KTtcbn1cblxuLyoqXG4gKiBJZGVudGl0eSBvZiB0aGUgc2lnbmVkLWluIHVzZXIgb24gd2hvc2UgYmVoYWxmIHRoaXMgYnJvYWRjYXN0IGhhcHBlbnMsIHdoZW4gdGhlXG4gKiBwcm9jZXNzIGNhbiB0ZWxsLlxuICpcbiAqIGBAZGF0YWJyaWNrcy9hcHBraXRgIGlzIGFuIE9QVElPTkFMIHBlZXIsIHNvIHRoZSBpbXBvcnQgaXMgbGF6eSBhbmQgYSBtaXNzaW5nXG4gKiBtb2R1bGUgaXMgbm90IGFuIGVycm9yIC0gdGhpcyBpcyBhIHBsYWluIFBvc3RncmVzIHBhY2thZ2UgdGhhdCBhZGRzIHNlbmRlclxuICogaWRlbnRpdHkgd2hlbiBpdCBoYXBwZW5zIHRvIHJ1biBpbnNpZGUgYW4gQXBwS2l0IGFwcC4gT3V0c2lkZSBhbiBhY3RpdmVcbiAqIHBlci1yZXF1ZXN0IGV4ZWN1dGlvbiBjb250ZXh0IEFwcEtpdCB0aHJvd3MsIHdoaWNoIG1lYW5zIFwibm8gdXNlciBoZXJlXCIsIHNvIHRoZVxuICogcmVzdWx0IGlzIGVtcHR5IG1ldGFkYXRhIHJhdGhlciB0aGFuIGEgZmFpbGVkIGJyb2FkY2FzdC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc2VuZGVyTWV0YWRhdGEoKTogUHJvbWlzZTxUb3BpY01ldGFkYXRhPiB7XG4gIGFwcEtpdE1vZHVsZSA/Pz0gaW1wb3J0KFwiQGRhdGFicmlja3MvYXBwa2l0XCIpXG4gICAgLnRoZW4oKG1vZHVsZSkgPT4gbW9kdWxlIGFzIEFwcEtpdE1vZHVsZSlcbiAgICAuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgY29uc3QgYXBwa2l0ID0gYXdhaXQgYXBwS2l0TW9kdWxlO1xuICBpZiAoIWFwcGtpdCkgcmV0dXJuIHt9O1xuICB0cnkge1xuICAgIGNvbnN0IGNvbnRleHQgPSBhcHBraXQuZ2V0RXhlY3V0aW9uQ29udGV4dCgpO1xuICAgIHJldHVybiBkZWZpbmVkTWV0YWRhdGEoe1xuICAgICAgc2VuZGVySWQ6IHN0cmluZy50cmltVG9OdWxsKGNvbnRleHQudXNlcklkKSA/PyB1bmRlZmluZWQsXG4gICAgICBzZW5kZXJOYW1lOiBzdHJpbmcudHJpbVRvTnVsbChjb250ZXh0LnVzZXJOYW1lKSA/PyB1bmRlZmluZWQsXG4gICAgICBzZW5kZXJFbWFpbDogc3RyaW5nLnRyaW1Ub051bGwoY29udGV4dC51c2VyRW1haWwpID8/IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG59XG5cbi8qKlxuICogRGVyaXZlIGEgbGVnYWwgUG9zdGdyZXMgY2hhbm5lbCBuYW1lIGZyb20gd2hhdGV2ZXIgYSBjYWxsZXIgdXNlZCB0byBpZGVudGlmeVxuICogdGhlIGNoYW5uZWwuXG4gKlxuICogQ2FsbGVycyB0aGluayBpbiB0ZXJtcyBvZiB3aGF0IHRoZSBjaGFubmVsIElTIC0gYW4gYXBwIG5hbWUsIGEgdGVuYW50IGlkLCBhXG4gKiBgW2VudiwgZmVhdHVyZV1gIHBhaXIsIGEgY29uZmlnIG9iamVjdCAtIGFuZCBub25lIG9mIHRob3NlIGFyZSBpZGVudGlmaWVycy5cbiAqIFJlamVjdGluZyB0aGVtIHB1c2hlZCB0aGUgc2FuaXRpemluZyBvbnRvIGV2ZXJ5IGNhbGwgc2l0ZSwgd2hpY2ggaXMgaG93IHR3b1xuICogc2VydmljZXMgZW5kIHVwIGRpc2FncmVlaW5nIGFib3V0IHdoZXRoZXIgdGhlIGNoYW5uZWwgaXMgYG15LWFwcGAsIGBteV9hcHBgLFxuICogb3IgYG15QXBwYCBhbmQgc2lsZW50bHkgbmV2ZXIgaGVhciBlYWNoIG90aGVyLlxuICpcbiAqIFRoZSBwYXJ0cyBhcmUgdG9rZW5pemVkIGFuZCBqb2luZWQgaW50byBhbiBpZGVudGlmaWVyLCB0aGVuIGEgc2hvcnQgaGFzaCBvZiB0aGVcbiAqIE9SSUdJTkFMIHBhcnRzIGlzIGFwcGVuZGVkLiBUaGUgaGFzaCBpcyB3aGF0IG1ha2VzIHRoZSBtYXBwaW5nIHRydXN0d29ydGh5OlxuICogdG9rZW5pemluZyBhbG9uZSBpcyBsb3NzeSwgc28gYG15LWFwcGAgYW5kIGBteV9hcHBgIGFuZCBgbXlBcHBgIHdvdWxkIGNvbGxhcHNlXG4gKiBvbnRvIG9uZSBjaGFubmVsLCBhbmQgYSBuYW1lIGxvbmcgZW5vdWdoIHRvIGhpdCBgTkFNRURBVEFMRU5gIHdvdWxkIGNvbGxpZGVcbiAqIHdpdGggYW55dGhpbmcgc2hhcmluZyBpdHMgbGVhZGluZyB0b2tlbnMuIFdpdGggdGhlIHN1ZmZpeCwgdGhlIHJlYWRhYmxlIHBhcnRcbiAqIHN0YXlzIHJlYWRhYmxlIGFuZCBkaXN0aW5jdCBpbnB1dHMgc3RheSBkaXN0aW5jdC5cbiAqXG4gKiBEZXRlcm1pbmlzdGljIGFjcm9zcyBwcm9jZXNzZXMgYW5kIHJ1bnMsIHdoaWNoIGlzIHRoZSB3aG9sZSBwb2ludCAtIHR3b1xuICogc2VydmljZXMgZ2l2ZW4gdGhlIHNhbWUgcGFydHMgbXVzdCBsYW5kIG9uIHRoZSBzYW1lIGNoYW5uZWwgd2l0aG91dFxuICogY29vcmRpbmF0aW5nLiBIYXNoIGlucHV0cyBhcmUgc3RydWN0dXJlLWF3YXJlLCBzbyBgW1wiYVwiLCBcImJcIl1gIGFuZCBgW1wiYV9iXCJdYFxuICogZGlmZmVyLCBhbmQgb2JqZWN0IGtleSBvcmRlciBkb2VzIG5vdC5cbiAqL1xuZnVuY3Rpb24gY2hhbm5lbE5hbWUocGFydHM6IG9iamVjdC5PbmVPck1hbnk8dW5rbm93bj4pOiBzdHJpbmcge1xuICAvLyBIYXNoIHRoZSBDQU5PTklDQUwgZm9ybSwgdGhlIHNhbWUgcnVsZSBgYWR2aXNvcnlMb2NrSWRgIHVzZXMsIHNvIHN0cnVjdHVyZSBhbmRcbiAgLy8gdHlwZXMgZGVjaWRlIGlkZW50aXR5OiBgW1wiYVwiLFwiYlwiXWAgZGlmZmVycyBmcm9tIGBbXCJhYlwiXWAsIGAxYCBmcm9tIGBcIjFcImAsIGFuZFxuICAvLyBvYmplY3Qga2V5IG9yZGVyIGRvZXMgbm90IG1hdHRlci4gSGFzaGluZyB0aGUgcmF3IHBhcnRzIHdvdWxkIGluc3RlYWQgbGVhbiBvblxuICAvLyB0aGUgaGFzaCBtb2R1bGUncyBjYW5vbmljYWxpemVyLCB3aGljaCBmb2xkcyBldmVyeSBgRGF0ZWAgb250byBvbmUgdG9rZW4uXG4gIGNvbnN0IHN1ZmZpeCA9IGhhc2guZm52SGFzaFdpdGhPcHRpb25zKFxuICAgIHsgbGVuZ3RoOiBDSEFOTkVMX0hBU0hfTEVOR1RIIH0sXG4gICAgcGFydHMubWFwKChwYXJ0KSA9PiBvYmplY3QudG9TdGFibGVLZXkocGFydCkpLmpvaW4oXCJcXHUwMDAwXCIpLFxuICApO1xuICAvLyBPbmx5IHBhcnRzIHRoYXQgc3RyaW5naWZ5IHRvIHNvbWV0aGluZyBhIHJlYWRlciByZWNvZ25pemVzIGNvbnRyaWJ1dGUgdG8gdGhlXG4gIC8vIHJlYWRhYmxlIGhhbGYuIEFuIG9iamVjdCB3b3VsZCB0b2tlbml6ZSBmcm9tIGBTdHJpbmcodmFsdWUpYCBhc1xuICAvLyBgb2JqZWN0X29iamVjdGAsIHdoaWNoIGlzIG5vaXNlIC0gaXQgc3RpbGwgc2hhcGVzIHRoZSBoYXNoLCBzbyBpZGVudGl0eSBpc1xuICAvLyB1bmFmZmVjdGVkLlxuICBjb25zdCBsYWJlbGxlZCA9IHBhcnRzLmZpbHRlcigocGFydCkgPT4ge1xuICAgIGNvbnN0IHR5cGUgPSB0eXBlb2YgcGFydDtcbiAgICByZXR1cm4gdHlwZSA9PT0gXCJzdHJpbmdcIiB8fCB0eXBlID09PSBcIm51bWJlclwiIHx8IHR5cGUgPT09IFwiYm9vbGVhblwiIHx8IHR5cGUgPT09IFwiYmlnaW50XCI7XG4gIH0pO1xuICAvLyBUcnVuY2F0ZSByYXRoZXIgdGhhbiBjYXAgdGhyb3VnaCB0aGUgdG9rZW5pemVyOiBgdHJpbWAgd291bGQgRFJPUCBhIHNpbmdsZVxuICAvLyB0b2tlbiBsb25nZXIgdGhhbiB0aGUgYnVkZ2V0LCB0dXJuaW5nIG9uZSBsb25nIG5hbWUgaW50byB0aGUgYmFyZSBmYWxsYmFjayBhbmRcbiAgLy8gbWFraW5nIGV2ZXJ5IGxvbmcgbmFtZSBsb29rIGFsaWtlLiBUaGUgaGFzaCBzdGlsbCBzZXBhcmF0ZXMgdGhlbSwgYnV0IHRoZVxuICAvLyBjaGFubmVsIGlzIHVucmVhZGFibGUgaW4gYSBsb2cuXG4gIGNvbnN0IGJvZHkgPSBzdHJpbmdcbiAgICAudG9JZGVudGlmaWVyV2l0aE9wdGlvbnMoeyBkZWxpbWl0ZXI6IFwiX1wiIH0sIC4uLmxhYmVsbGVkKVxuICAgIC5zbGljZSgwLCBNQVhfQ0hBTk5FTF9MRU5HVEggLSBzdWZmaXgubGVuZ3RoIC0gMSlcbiAgICAucmVwbGFjZSgvXyskLywgXCJcIik7XG4gIC8vIEFuIGlkZW50aWZpZXIgY2Fubm90IHN0YXJ0IHdpdGggYSBkaWdpdCBhbmQgdGhlIGhhc2ggYWxwaGFiZXQgaXNcbiAgLy8gZGlnaXQtbGVhZGluZywgc28gYSBudW1lcmljIG9yIGVtcHR5IGJvZHkgbmVlZHMgYSBsZXR0ZXIgaW4gZnJvbnQuXG4gIGNvbnN0IHByZWZpeCA9IC9eW0EtWmEtel9dLy50ZXN0KGJvZHkpID8gYm9keSA6IGAke0NIQU5ORUxfRkFMTEJBQ0t9XyR7Ym9keX1gO1xuICByZXR1cm4gYCR7cHJlZml4fV8ke3N1ZmZpeH1gLnJlcGxhY2UoL18rL2csIFwiX1wiKTtcbn1cblxuLyoqIFF1b3RlIGEgdmFsaWRhdGVkIGNoYW5uZWwgZm9yIGBMSVNURU5gL2BVTkxJU1RFTmAsIHdoaWNoIHRha2Ugbm8gcGFyYW1ldGVycy4gKi9cbmZ1bmN0aW9uIHF1b3RlSWRlbnRpZmllcih2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIGBcIiR7dmFsdWUucmVwbGFjZUFsbCgnXCInLCAnXCJcIicpfVwiYDtcbn1cblxuLyoqXG4gKiBQYXJzZSBhIGBOT1RJRllgIHBheWxvYWQgaW50byBhbiBlbnZlbG9wZSwgcmV0dXJuaW5nIGB1bmRlZmluZWRgIGZvciBhbnl0aGluZ1xuICogdGhhdCBpcyBub3Qgb25lLlxuICpcbiAqIFRoZSBjaGFubmVsIGlzIHNoYXJlZCBhbmQgYW55IFBvc3RncmVzIHNlc3Npb24gY2FuIGBwZ19ub3RpZnlgIG9uIGl0LCBzbyBhblxuICogdW5yZWNvZ25pemVkIHBheWxvYWQgaXMgZXhwZWN0ZWQgaW5wdXQsIG5vdCBhbiBleGNlcHRpb24uIFB1Ymxpc2hlcnMgcnVuIHRoaXNcbiAqIG92ZXIgdGhlaXIgb3duIGVuY29kZWQgbWVzc2FnZSB0b28sIHdoaWNoIGlzIGhvdyBhIGJvZHkgdGhhdCBzdHJpbmdpZmllcyBidXRcbiAqIGRvZXMgbm90IHJvdW5kLXRyaXAgaXMgY2F1Z2h0IGJlZm9yZSBpdCBzaGlwcy5cbiAqL1xuZnVuY3Rpb24gZGVjb2RlKHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBUb3BpY01lc3NhZ2UgfCB1bmRlZmluZWQge1xuICBpZiAoIXZhbHVlKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCByZWNvcmQgPSBqc29uLnBhcnNlUmVjb3JkKHZhbHVlKTtcbiAgaWYgKFxuICAgICFyZWNvcmQgfHxcbiAgICB0eXBlb2YgcmVjb3JkLmlkICE9PSBcInN0cmluZ1wiIHx8XG4gICAgdHlwZW9mIHJlY29yZC50b3BpYyAhPT0gXCJzdHJpbmdcIiB8fFxuICAgIHR5cGVvZiByZWNvcmQudHlwZSAhPT0gXCJzdHJpbmdcIiB8fFxuICAgIHR5cGVvZiByZWNvcmQucHVibGlzaGVkQXQgIT09IFwic3RyaW5nXCIgfHxcbiAgICAhcmVjb3JkLm1ldGFkYXRhIHx8XG4gICAgdHlwZW9mIHJlY29yZC5tZXRhZGF0YSAhPT0gXCJvYmplY3RcIiB8fFxuICAgIEFycmF5LmlzQXJyYXkocmVjb3JkLm1ldGFkYXRhKSB8fFxuICAgICEoXCJib2R5XCIgaW4gcmVjb3JkKSB8fFxuICAgICFvYmplY3QuaXNTZXJpYWxpemFibGVWYWx1ZShyZWNvcmQubWV0YWRhdGEpIHx8XG4gICAgIW9iamVjdC5pc1NlcmlhbGl6YWJsZVZhbHVlKHJlY29yZC5ib2R5KVxuICApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiB7XG4gICAgaWQ6IHJlY29yZC5pZCxcbiAgICB0b3BpYzogcmVjb3JkLnRvcGljLFxuICAgIHR5cGU6IHJlY29yZC50eXBlLFxuICAgIG1ldGFkYXRhOiByZWNvcmQubWV0YWRhdGEgYXMgVG9waWNNZXRhZGF0YSxcbiAgICBib2R5OiByZWNvcmQuYm9keSBhcyBTZXJpYWxpemFibGVWYWx1ZSxcbiAgICBwdWJsaXNoZWRBdDogcmVjb3JkLnB1Ymxpc2hlZEF0LFxuICB9O1xufVxuXG4vKipcbiAqIEJyb2FkY2FzdHMgc3RydWN0dXJlZCBtZXNzYWdlcyBieSB0b3BpYyBhbmQgZGVsaXZlcnMgdGhlbSB0byBldmVyeSBwcm9jZXNzXG4gKiBsaXN0ZW5pbmcgb24gdGhlIHNhbWUgY2hhbm5lbC5cbiAqXG4gKiBDT05ORUNUSU9OIFNIQVBFLiBQdWJsaXNoaW5nIGJvcnJvd3MgYSBwb29sZWQgY29ubmVjdGlvbiBwZXIgY2FsbCwgbGlrZSBhbnlcbiAqIG90aGVyIHF1ZXJ5LiBMaXN0ZW5pbmcgY2Fubm90OiBgTElTVEVOYCBpcyBzZXNzaW9uIHN0YXRlLCBzbyB0aGUgYnVzIGhvbGRzIE9ORVxuICogZGVkaWNhdGVkIGNsaWVudCBvdXQgb2YgdGhlIHBvb2wgZm9yIGFzIGxvbmcgYXMgaXQgaGFzIHN1YnNjcmliZXJzLCBubyBtYXR0ZXJcbiAqIGhvdyBtYW55IHRvcGljcyBvciBsaXN0ZW5lcnMgYXJlIHJlZ2lzdGVyZWQuIFNpemUgdGhlIHBvb2wgd2l0aCB0aGF0IG9uZVxuICogbG9uZy1saXZlZCBjaGVja291dCBpbiBtaW5kLlxuICpcbiAqIExJRkVDWUNMRS4gQ29uc3RydWN0aW9uIGlzIGluZXJ0IC0gbm90aGluZyBjb25uZWN0cyB1bnRpbCB0aGUgZmlyc3RcbiAqIHtAbGluayBsaXN0ZW59IChvciBhbiBleHBsaWNpdCB7QGxpbmsgc3RhcnR9KS4ge0BsaW5rIGNsb3NlfSBpcyByZXF1aXJlZCB0byBnaXZlXG4gKiB0aGUgY29ubmVjdGlvbiBiYWNrOyBhIGNsb3NlZCBidXMgc3RheXMgY2xvc2VkIGFuZCB0aHJvd3Mgb24gZnVydGhlciB1c2UgcmF0aGVyXG4gKiB0aGFuIHNpbGVudGx5IHJlY29ubmVjdGluZy4gUmVnaXN0ZXIgaXQgd2l0aCB0aGUgaG9zdCdzIHNodXRkb3duIGhvb2suXG4gKlxuICogRkFJTFVSRSBIQU5ETElORy4gQSBsb3N0IG5vdGlmaWNhdGlvbiBjb25uZWN0aW9uIHJlY29ubmVjdHMgb24gaXRzIG93biB3aXRoXG4gKiBib3VuZGVkIGJhY2tvZmYgd2hpbGUgc3Vic2NyaWJlcnMgcmVtYWluLCByZXBvcnRpbmcgZWFjaCBmYWlsZWQgYXR0ZW1wdCB0aHJvdWdoXG4gKiBgb25FcnJvcmAuIE1lc3NhZ2VzIHB1Ymxpc2hlZCBkdXJpbmcgdGhlIGdhcCBhcmUgbG9zdCAtIGBOT1RJRllgIGhhcyBubyByZXBsYXkuXG4gKiBBIHRocm93aW5nIG9yIHJlamVjdGluZyBsaXN0ZW5lciBuZXZlciBhZmZlY3RzIHRoZSBwdWJsaXNoZXIgb3IgdGhlIG90aGVyXG4gKiBsaXN0ZW5lcnM7IGl0cyBmYWlsdXJlIGdvZXMgdG8gYG9uRXJyb3JgLlxuICpcbiAqIE5vdCBzYWZlIHRvIHNoYXJlIG9uZSBpbnN0YW5jZSBhY3Jvc3MgdW5yZWxhdGVkIGNoYW5uZWxzIC0gY29uc3RydWN0IG9uZSBidXNcbiAqIHBlciBjaGFubmVsLiBUaGUgY2hhbm5lbCBpdHNlbGYgaXMgREVSSVZFRCBmcm9tIHRoZSBgY2hhbm5lbGAgb3B0aW9uIHJhdGhlclxuICogdGhhbiB0YWtlbiBsaXRlcmFsbHk7IHNlZSB7QGxpbmsgUG9zdGdyZXNUb3BpY0J1c09wdGlvbnMuY2hhbm5lbH0gYW5kXG4gKiB7QGxpbmsgUG9zdGdyZXNUb3BpY0J1cy5jaGFubmVsTmFtZX0uXG4gKi9cbmV4cG9ydCBjbGFzcyBQb3N0Z3Jlc1RvcGljQnVzIHtcbiAgLyoqXG4gICAqIFRoZSByZXNvbHZlZCBQb3N0Z3JlcyBjaGFubmVsIHRoaXMgYnVzIGxpc3RlbnMgYW5kIHB1Ymxpc2hlcyBvbiwgZGVyaXZlZCBmcm9tXG4gICAqIHRoZSBgY2hhbm5lbGAgb3B0aW9uLiBSZWFkIGl0IHRvIGNvbmZpcm0gdHdvIHByb2Nlc3NlcyBhZ3JlZSwgb3IgdG8gbG9nIHdoYXQgYVxuICAgKiBzZXQgb2YgcGFydHMgYWN0dWFsbHkgcmVzb2x2ZWQgdG8uXG4gICAqL1xuICByZWFkb25seSBjaGFubmVsTmFtZTogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IG1ldGFkYXRhOiBUb3BpY01ldGFkYXRhIHwgVG9waWNNZXRhZGF0YVByb3ZpZGVyIHwgdW5kZWZpbmVkO1xuICBwcml2YXRlIHJlYWRvbmx5IG9uRXJyb3I6IChjYXVzZTogdW5rbm93bikgPT4gdm9pZDtcbiAgcHJpdmF0ZSByZWFkb25seSBsaXN0ZW5lcnMgPSBuZXcgTWFwPHN0cmluZywgU2V0PFRvcGljTGlzdGVuZXI+PigpO1xuICBwcml2YXRlIGNsaWVudDogUG9vbENsaWVudCB8IHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSBzdGFydGluZzogUHJvbWlzZTx2b2lkPiB8IHVuZGVmaW5lZDtcbiAgcHJpdmF0ZSByZWNvbm5lY3Rpbmc6IFByb21pc2U8dm9pZD4gfCB1bmRlZmluZWQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgcmVjb25uZWN0QWJvcnQgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gIHByaXZhdGUgY2xvc2VkID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBwb29sOiBQZ1Bvb2xMaWtlICYgUGdRdWVyeWFibGUsXG4gICAgb3B0aW9uczogUG9zdGdyZXNUb3BpY0J1c09wdGlvbnMgPSB7fSxcbiAgKSB7XG4gICAgdGhpcy5jaGFubmVsTmFtZSA9IGNoYW5uZWxOYW1lKFxuICAgICAgb2JqZWN0LnRvT25lT3JNYW55KG9wdGlvbnMuY2hhbm5lbCA9PT0gdW5kZWZpbmVkID8gREVGQVVMVF9DSEFOTkVMIDogb3B0aW9ucy5jaGFubmVsKSxcbiAgICApO1xuICAgIHRoaXMubWV0YWRhdGEgPSBvcHRpb25zLm1ldGFkYXRhO1xuICAgIHRoaXMub25FcnJvciA9IG9wdGlvbnMub25FcnJvciA/PyAoKCkgPT4gdW5kZWZpbmVkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPcGVuIHRoZSBkZWRpY2F0ZWQgbm90aWZpY2F0aW9uIGNvbm5lY3Rpb24gYW5kIGBMSVNURU5gLlxuICAgKlxuICAgKiBJZGVtcG90ZW50LCBhbmQgc2FmZSB0byBjYWxsIGNvbmN1cnJlbnRseSAtIG92ZXJsYXBwaW5nIGNhbGxzIGF3YWl0IHRoZSBzYW1lXG4gICAqIGluLWZsaWdodCBjb25uZWN0LiB7QGxpbmsgbGlzdGVufSBjYWxscyB0aGlzLCBzbyBpdCBpcyBvbmx5IG5lZWRlZCB0byBzdXJmYWNlXG4gICAqIGEgY29ubmVjdGlvbiBwcm9ibGVtIGF0IHN0YXJ0dXAgcmF0aGVyIHRoYW4gb24gZmlyc3Qgc3Vic2NyaWJlLiBUaHJvd3MgaWYgdGhlXG4gICAqIGJ1cyBpcyBjbG9zZWQsIG9yIGlmIHRoZSBwb29sIGNhbm5vdCBoYW5kIG91dCBhIGNvbm5lY3Rpb24uXG4gICAqL1xuICBhc3luYyBzdGFydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jbGllbnQpIHJldHVybjtcbiAgICBpZiAodGhpcy5jbG9zZWQpIHRocm93IG5ldyBFcnJvcihcIlBvc3RncmVzIHRvcGljIGJ1cyBpcyBjbG9zZWRcIik7XG4gICAgdGhpcy5zdGFydGluZyA/Pz0gdGhpcy5jb25uZWN0KCkuZmluYWxseSgoKSA9PiB7XG4gICAgICB0aGlzLnN0YXJ0aW5nID0gdW5kZWZpbmVkO1xuICAgIH0pO1xuICAgIGF3YWl0IHRoaXMuc3RhcnRpbmc7XG4gIH1cblxuICAvKipcbiAgICogUHVibGlzaCBhIG1lc3NhZ2UgdG8gYHRvcGljYCwgcmV0dXJuaW5nIHRoZSBlbnZlbG9wZSB0aGF0IHdhcyBzZW50LlxuICAgKlxuICAgKiBSZXNvbHZlcyBvbmNlIFBvc3RncmVzIGhhcyBhY2NlcHRlZCB0aGUgYE5PVElGWWAsIHdoaWNoIHNheXMgbm90aGluZyBhYm91dFxuICAgKiBhbnlvbmUgcmVjZWl2aW5nIGl0OiBzZXNzaW9ucyBub3QgbGlzdGVuaW5nIGF0IHRoYXQgbW9tZW50IG1pc3MgaXQuIFB1Ymxpc2hpbmdcbiAgICogbmVlZHMgbm8ge0BsaW5rIHN0YXJ0fSBhbmQgbm8gc3Vic2NyaWJlcnMuXG4gICAqXG4gICAqIFZhbGlkYXRpb24gaXMgZGVsaWJlcmF0ZWx5IGZyb250LWxvYWRlZCwgc2luY2UgYSBtZXNzYWdlIHRoYXQgZmFpbHMgb24gdGhlXG4gICAqIHdpcmUgaXMgZmFyIGhhcmRlciB0byBkaWFnbm9zZSB0aGFuIG9uZSByZWplY3RlZCBhdCB0aGUgY2FsbDogYFR5cGVFcnJvcmAgZm9yIGFcbiAgICogYmxhbmsgdG9waWMgb3IgdHlwZSwgb3IgYSBib2R5L21ldGFkYXRhIHRoYXQgd291bGQgbm90IHJvdW5kLXRyaXAgdGhyb3VnaCBKU09OXG4gICAqIHVuY2hhbmdlZCAoYG9iamVjdC5pc1NlcmlhbGl6YWJsZVZhbHVlYCk7IGBSYW5nZUVycm9yYCB3aGVuIHRoZSBlbmNvZGVkXG4gICAqIGVudmVsb3BlIGV4Y2VlZHMgdGhlIGBOT1RJRllgIHBheWxvYWQgbGltaXQsIHdoaWNoIHRoZSBhdXRvbWF0aWMgbWV0YWRhdGFcbiAgICogY291bnRzIGFnYWluc3QgLSBzZW5kIGEgcmVmZXJlbmNlIGFuZCBsZXQgdGhlIHJlY2VpdmVyIGZldGNoIHRoZSBwYXlsb2FkLlxuICAgKiBUaHJvd3MgaWYgdGhlIGJ1cyBpcyBjbG9zZWQuXG4gICAqL1xuICBhc3luYyBicm9hZGNhc3Q8VEJvZHkgZXh0ZW5kcyBTZXJpYWxpemFibGVWYWx1ZT4oXG4gICAgdG9waWM6IHN0cmluZyxcbiAgICBpbnB1dDogVG9waWNQdWJsaXNoSW5wdXQ8VEJvZHk+LFxuICApOiBQcm9taXNlPFRvcGljTWVzc2FnZTxUQm9keT4+IHtcbiAgICBpZiAoIXRvcGljLnRyaW0oKSkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlRvcGljIG11c3Qgbm90IGJlIGVtcHR5XCIpO1xuICAgIGlmICghaW5wdXQudHlwZS50cmltKCkpIHRocm93IG5ldyBUeXBlRXJyb3IoXCJNZXNzYWdlIHR5cGUgbXVzdCBub3QgYmUgZW1wdHlcIik7XG4gICAgaWYgKFxuICAgICAgIW9iamVjdC5pc1NlcmlhbGl6YWJsZVZhbHVlKGlucHV0Lm1ldGFkYXRhID8/IHt9KSB8fFxuICAgICAgIW9iamVjdC5pc1NlcmlhbGl6YWJsZVZhbHVlKGlucHV0LmJvZHkpXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiTWVzc2FnZSBtZXRhZGF0YSBhbmQgYm9keSBtdXN0IGJlIEpTT04gc2VyaWFsaXphYmxlIHdpdGhvdXQgY29lcmNpb25cIik7XG4gICAgfVxuICAgIGlmICh0aGlzLmNsb3NlZCkgdGhyb3cgbmV3IEVycm9yKFwiUG9zdGdyZXMgdG9waWMgYnVzIGlzIGNsb3NlZFwiKTtcbiAgICBjb25zdCBhdXRvbWF0aWMgPSBhd2FpdCB0aGlzLnJlc29sdmVNZXRhZGF0YSgpO1xuICAgIGNvbnN0IG1lc3NhZ2U6IFRvcGljTWVzc2FnZTxUQm9keT4gPSB7XG4gICAgICBpZDogaGFzaC5pZCgpLFxuICAgICAgdG9waWMsXG4gICAgICB0eXBlOiBpbnB1dC50eXBlLFxuICAgICAgbWV0YWRhdGE6IHsgLi4uYXV0b21hdGljLCAuLi5pbnB1dC5tZXRhZGF0YSB9LFxuICAgICAgYm9keTogaW5wdXQuYm9keSxcbiAgICAgIHB1Ymxpc2hlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgfTtcbiAgICBsZXQgZW5jb2RlZDogc3RyaW5nO1xuICAgIHRyeSB7XG4gICAgICBlbmNvZGVkID0gSlNPTi5zdHJpbmdpZnkobWVzc2FnZSk7XG4gICAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYE1lc3NhZ2UgbXVzdCBiZSBKU09OIHNlcmlhbGl6YWJsZTogJHtlcnJvci5lcnJvck1lc3NhZ2UoY2F1c2UpfWApO1xuICAgIH1cbiAgICBpZiAoIWRlY29kZShlbmNvZGVkKSkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIk1lc3NhZ2UgdHlwZSwgbWV0YWRhdGEsIGFuZCBib2R5IG11c3QgYmUgSlNPTiBzZXJpYWxpemFibGVcIik7XG4gICAgfVxuICAgIGlmIChCdWZmZXIuYnl0ZUxlbmd0aChlbmNvZGVkLCBcInV0ZjhcIikgPiBNQVhfTk9USUZZX0JZVEVTKSB7XG4gICAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcihgUG9zdGdyZXMgbm90aWZpY2F0aW9uIGV4Y2VlZHMgJHtNQVhfTk9USUZZX0JZVEVTfSBieXRlc2ApO1xuICAgIH1cbiAgICBhd2FpdCB0aGlzLnBvb2wucXVlcnkoXCJTRUxFQ1QgcGdfbm90aWZ5KCQxLCAkMilcIiwgW3RoaXMuY2hhbm5lbE5hbWUsIGVuY29kZWRdKTtcbiAgICByZXR1cm4gbWVzc2FnZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmUgdG8gYHRvcGljYCwgcmV0dXJuaW5nIHRoZSBmdW5jdGlvbiB0aGF0IHVuc3Vic2NyaWJlcy5cbiAgICpcbiAgICogQ29ubmVjdHMgb24gZmlyc3QgdXNlLiBTZXZlcmFsIGxpc3RlbmVycyBtYXkgc2hhcmUgYSB0b3BpYzsgZWFjaCBpcyBjYWxsZWRcbiAgICogb25jZSBwZXIgbWVzc2FnZS4gT25seSBtZXNzYWdlcyBwdWJsaXNoZWQgQUZURVIgdGhpcyByZXNvbHZlcyBhcnJpdmUsIHNvXG4gICAqIHN1YnNjcmliZSBiZWZvcmUgdHJpZ2dlcmluZyB3aGF0ZXZlciB5b3UgZXhwZWN0IHRvIG9ic2VydmUuXG4gICAqXG4gICAqIFRoZSByZXR1cm5lZCBmdW5jdGlvbiByZW1vdmVzIGp1c3QgdGhpcyBsaXN0ZW5lciBhbmQgaXMgc2FmZSB0byBjYWxsIHR3aWNlLlxuICAgKiBUaGUgY29ubmVjdGlvbiBzdGF5cyBvcGVuIG9uY2UgdGhlIGxhc3QgbGlzdGVuZXIgbGVhdmVzIC0ge0BsaW5rIGNsb3NlfVxuICAgKiByZWxlYXNlcyBpdCAtIHNvIGEgYnVzIHRoYXQgc3Vic2NyaWJlcyBhbmQgdW5zdWJzY3JpYmVzIHBlciByZXF1ZXN0IGRvZXMgbm90XG4gICAqIGNodXJuIGNvbm5lY3Rpb25zLlxuICAgKi9cbiAgYXN5bmMgbGlzdGVuPFRCb2R5IGV4dGVuZHMgU2VyaWFsaXphYmxlVmFsdWU+KFxuICAgIHRvcGljOiBzdHJpbmcsXG4gICAgbGlzdGVuZXI6IFRvcGljTGlzdGVuZXI8VEJvZHk+LFxuICApOiBQcm9taXNlPCgpID0+IFByb21pc2U8dm9pZD4+IHtcbiAgICBpZiAoIXRvcGljLnRyaW0oKSkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlRvcGljIG11c3Qgbm90IGJlIGVtcHR5XCIpO1xuICAgIGF3YWl0IHRoaXMuc3RhcnQoKTtcbiAgICBjb25zdCBsaXN0ZW5lcnMgPSB0aGlzLmxpc3RlbmVycy5nZXQodG9waWMpID8/IG5ldyBTZXQ8VG9waWNMaXN0ZW5lcj4oKTtcbiAgICBsaXN0ZW5lcnMuYWRkKGxpc3RlbmVyIGFzIFRvcGljTGlzdGVuZXIpO1xuICAgIHRoaXMubGlzdGVuZXJzLnNldCh0b3BpYywgbGlzdGVuZXJzKTtcbiAgICByZXR1cm4gYXN5bmMgKCkgPT4ge1xuICAgICAgbGlzdGVuZXJzLmRlbGV0ZShsaXN0ZW5lciBhcyBUb3BpY0xpc3RlbmVyKTtcbiAgICAgIGlmIChsaXN0ZW5lcnMuc2l6ZSA9PT0gMCkgdGhpcy5saXN0ZW5lcnMuZGVsZXRlKHRvcGljKTtcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkIHRoZSBhdXRvbWF0aWMgaGFsZiBvZiBhIG1lc3NhZ2UncyBtZXRhZGF0YS5cbiAgICpcbiAgICogUHJlY2VkZW5jZSwgd2Vha2VzdCBmaXJzdDogbWFjaGluZS9wcm9jZXNzIGNvbnRleHQsIHRoZW4gQXBwS2l0IHNlbmRlclxuICAgKiBpZGVudGl0eSwgdGhlbiB0aGlzIGJ1cydzIGNvbmZpZ3VyZWQgbWV0YWRhdGEuIFRoZSBjYWxsZXIncyBwZXItbWVzc2FnZVxuICAgKiBtZXRhZGF0YSBpcyBsYXllcmVkIG92ZXIgdGhlIHJlc3VsdCBpbiB7QGxpbmsgYnJvYWRjYXN0fSwgc28gdGhlIG1vc3Qgc3BlY2lmaWNcbiAgICogc291cmNlIGFsd2F5cyB3aW5zIGFuZCBub3RoaW5nIGhlcmUgY2FuIG92ZXJ3cml0ZSBhbiBleHBsaWNpdCBrZXkuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlc29sdmVNZXRhZGF0YSgpOiBQcm9taXNlPFRvcGljTWV0YWRhdGE+IHtcbiAgICBjb25zdCBzZW5kZXIgPSBhd2FpdCBzZW5kZXJNZXRhZGF0YSgpO1xuICAgIGNvbnN0IGNvbmZpZ3VyZWQgPVxuICAgICAgdHlwZW9mIHRoaXMubWV0YWRhdGEgPT09IFwiZnVuY3Rpb25cIiA/IGF3YWl0IHRoaXMubWV0YWRhdGEoKSA6ICh0aGlzLm1ldGFkYXRhID8/IHt9KTtcbiAgICByZXR1cm4geyAuLi5tYWNoaW5lTWV0YWRhdGEoKSwgLi4uc2VuZGVyLCAuLi5jb25maWd1cmVkIH07XG4gIH1cblxuICAvKipcbiAgICogUmVsZWFzZSB0aGUgbm90aWZpY2F0aW9uIGNvbm5lY3Rpb24gYW5kIHN0b3AgZGVsaXZlcmluZyBtZXNzYWdlcy4gSWRlbXBvdGVudC5cbiAgICpcbiAgICogQ2FuY2VscyBhbnkgcGVuZGluZyByZWNvbm5lY3QsIGRyb3BzIGFsbCBsaXN0ZW5lcnMsIHRoZW4gYFVOTElTVEVOYHMgYW5kXG4gICAqIHJldHVybnMgdGhlIGNsaWVudCB0byB0aGUgcG9vbC4gQSBmYWlsZWQgYFVOTElTVEVOYCBpcyByZXBvcnRlZCB0byB0aGUgcG9vbCBhc1xuICAgKiBhIHJlbGVhc2UgZXJyb3Igc28gdGhlIGNvbm5lY3Rpb24gaXMgRElTQ0FSREVEIHJhdGhlciB0aGFuIGhhbmRlZCB0byB0aGUgbmV4dFxuICAgKiBjYWxsZXIgc3RpbGwgc3Vic2NyaWJlZCB0byB0aGUgY2hhbm5lbC4gRG9lcyBub3QgdGhyb3c7IHRoZSBidXMgc3RheXMgY2xvc2VkLlxuICAgKi9cbiAgYXN5bmMgY2xvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuY2xvc2VkKSByZXR1cm47XG4gICAgdGhpcy5jbG9zZWQgPSB0cnVlO1xuICAgIHRoaXMucmVjb25uZWN0QWJvcnQuYWJvcnQobmV3IEVycm9yKFwiUG9zdGdyZXMgdG9waWMgYnVzIGlzIGNsb3NlZFwiKSk7XG4gICAgYXdhaXQgdGhpcy5zdGFydGluZz8uY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICBhd2FpdCB0aGlzLnJlY29ubmVjdGluZz8uY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICBjb25zdCBjbGllbnQgPSB0aGlzLmNsaWVudDtcbiAgICB0aGlzLmNsaWVudCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmxpc3RlbmVycy5jbGVhcigpO1xuICAgIGlmICghY2xpZW50KSByZXR1cm47XG4gICAgY2xpZW50LnJlbW92ZUxpc3RlbmVyKFwibm90aWZpY2F0aW9uXCIsIHRoaXMuaGFuZGxlTm90aWZpY2F0aW9uKTtcbiAgICBjbGllbnQucmVtb3ZlTGlzdGVuZXIoXCJlcnJvclwiLCB0aGlzLmhhbmRsZUNsaWVudEVycm9yKTtcbiAgICBsZXQgcmVsZWFzZUVycm9yOiBFcnJvciB8IHVuZGVmaW5lZDtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgY2xpZW50LnF1ZXJ5KGBVTkxJU1RFTiAke3F1b3RlSWRlbnRpZmllcih0aGlzLmNoYW5uZWxOYW1lKX1gKTtcbiAgICB9IGNhdGNoIChjYXVzZSkge1xuICAgICAgcmVsZWFzZUVycm9yID0gZXJyb3IudG9FcnJvcihjYXVzZSk7XG4gICAgfVxuICAgIGNsaWVudC5yZWxlYXNlKHJlbGVhc2VFcnJvcik7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgb3V0IG9uZSBjbGllbnQsIGF0dGFjaCB0aGUgaGFuZGxlcnMsIGFuZCBgTElTVEVOYC5cbiAgICpcbiAgICogVGhlIGhhbmRsZXJzIGFyZSBhdHRhY2hlZCBCRUZPUkUgdGhlIGBMSVNURU5gIHJvdW5kIHRyaXAgc28gYSBub3RpZmljYXRpb24gb3JcbiAgICogZXJyb3IgYXJyaXZpbmcgbWlkLXNldHVwIGlzIG5vdCBtaXNzZWQuIEFueSBmYWlsdXJlIC0gaW5jbHVkaW5nIHRoZSBidXMgYmVpbmdcbiAgICogY2xvc2VkIHdoaWxlIHRoZSBjb25uZWN0IHdhcyBpbiBmbGlnaHQgLSBkZXRhY2hlcyB0aGUgaGFuZGxlcnMgYW5kIHJlbGVhc2VzXG4gICAqIHRoZSBjbGllbnQgYXMgZXJyb3JlZCwgc28gYSBoYWxmLWNvbmZpZ3VyZWQgY29ubmVjdGlvbiBuZXZlciByZXR1cm5zIHRvIHRoZVxuICAgKiBwb29sLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNsaWVudCA9IGF3YWl0IHRoaXMucG9vbC5jb25uZWN0KCk7XG4gICAgdHJ5IHtcbiAgICAgIGNsaWVudC5vbihcIm5vdGlmaWNhdGlvblwiLCB0aGlzLmhhbmRsZU5vdGlmaWNhdGlvbik7XG4gICAgICBjbGllbnQub24oXCJlcnJvclwiLCB0aGlzLmhhbmRsZUNsaWVudEVycm9yKTtcbiAgICAgIGF3YWl0IGNsaWVudC5xdWVyeShgTElTVEVOICR7cXVvdGVJZGVudGlmaWVyKHRoaXMuY2hhbm5lbE5hbWUpfWApO1xuICAgICAgaWYgKHRoaXMuY2xvc2VkKSB0aHJvdyBuZXcgRXJyb3IoXCJQb3N0Z3JlcyB0b3BpYyBidXMgaXMgY2xvc2VkXCIpO1xuICAgICAgdGhpcy5jbGllbnQgPSBjbGllbnQ7XG4gICAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICAgIGNsaWVudC5yZW1vdmVMaXN0ZW5lcihcIm5vdGlmaWNhdGlvblwiLCB0aGlzLmhhbmRsZU5vdGlmaWNhdGlvbik7XG4gICAgICBjbGllbnQucmVtb3ZlTGlzdGVuZXIoXCJlcnJvclwiLCB0aGlzLmhhbmRsZUNsaWVudEVycm9yKTtcbiAgICAgIGNsaWVudC5yZWxlYXNlKGVycm9yLnRvRXJyb3IoY2F1c2UpKTtcbiAgICAgIHRocm93IGNhdXNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSb3V0ZSBvbmUgaW5ib3VuZCBub3RpZmljYXRpb24gdG8gdGhlIHRvcGljJ3MgbGlzdGVuZXJzLlxuICAgKlxuICAgKiBJZ25vcmVzIG90aGVyIGNoYW5uZWxzIGFuZCB1bmRlY29kYWJsZSBwYXlsb2FkcyAtIHRoZSBjaGFubmVsIGlzIHNoYXJlZCwgc29cbiAgICogYm90aCBhcmUgb3JkaW5hcnkgdHJhZmZpYy4gTGlzdGVuZXJzIGFyZSBpbnZva2VkIGNvbmN1cnJlbnRseSBhbmQgdGhlaXJcbiAgICogcmVqZWN0aW9ucyBnbyB0byBgb25FcnJvcmAsIGtlZXBpbmcgb25lIHNsb3cgb3IgYnJva2VuIHN1YnNjcmliZXIgZnJvbVxuICAgKiBzdGFsbGluZyB0aGUgbm90aWZpY2F0aW9uIGNvbm5lY3Rpb24uXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGhhbmRsZU5vdGlmaWNhdGlvbiA9IChub3RpZmljYXRpb246IE5vdGlmaWNhdGlvbik6IHZvaWQgPT4ge1xuICAgIGlmIChub3RpZmljYXRpb24uY2hhbm5lbCAhPT0gdGhpcy5jaGFubmVsTmFtZSkgcmV0dXJuO1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBkZWNvZGUobm90aWZpY2F0aW9uLnBheWxvYWQpO1xuICAgIGlmICghbWVzc2FnZSkgcmV0dXJuO1xuICAgIGZvciAoY29uc3QgbGlzdGVuZXIgb2YgdGhpcy5saXN0ZW5lcnMuZ2V0KG1lc3NhZ2UudG9waWMpID8/IFtdKSB7XG4gICAgICBQcm9taXNlLnJlc29sdmUobGlzdGVuZXIobWVzc2FnZSkpLmNhdGNoKHRoaXMub25FcnJvcik7XG4gICAgfVxuICB9O1xuXG4gIC8qKlxuICAgKiBIYW5kbGUgdGhlIG5vdGlmaWNhdGlvbiBjb25uZWN0aW9uIGR5aW5nLCB3aGljaCBgcGdgIHJlcG9ydHMgYXMgYW4gYGVycm9yYFxuICAgKiBldmVudCByYXRoZXIgdGhhbiBhIHJlamVjdGVkIHF1ZXJ5LlxuICAgKlxuICAgKiBUaGUgY2xpZW50IGlzIHVudXNhYmxlIGF0IHRoaXMgcG9pbnQsIHNvIGl0IGlzIGRldGFjaGVkIGFuZCByZWxlYXNlZCBhc1xuICAgKiBlcnJvcmVkICh3aGljaCBkaXNjYXJkcyBpdCkgYmVmb3JlIGFueXRoaW5nIGVsc2UuIFJlY29ubmVjdGlvbiBvbmx5IHN0YXJ0c1xuICAgKiB3aGVuIHRoZXJlIGlzIHN0aWxsIHNvbWVvbmUgdG8gZGVsaXZlciB0bywgc28gYW4gaWRsZSBvciBjbG9zaW5nIGJ1cyBkb2VzIG5vdFxuICAgKiBob2xkIGEgY29ubmVjdGlvbiBvcGVuIGNoYXNpbmcgYSBjaGFubmVsIG5vYm9keSByZWFkcy5cbiAgICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaGFuZGxlQ2xpZW50RXJyb3IgPSAoY2F1c2U6IEVycm9yKTogdm9pZCA9PiB7XG4gICAgdGhpcy5vbkVycm9yKGNhdXNlKTtcbiAgICBjb25zdCBjbGllbnQgPSB0aGlzLmNsaWVudDtcbiAgICBpZiAoIWNsaWVudCkgcmV0dXJuO1xuICAgIHRoaXMuY2xpZW50ID0gdW5kZWZpbmVkO1xuICAgIGNsaWVudC5yZW1vdmVMaXN0ZW5lcihcIm5vdGlmaWNhdGlvblwiLCB0aGlzLmhhbmRsZU5vdGlmaWNhdGlvbik7XG4gICAgY2xpZW50LnJlbW92ZUxpc3RlbmVyKFwiZXJyb3JcIiwgdGhpcy5oYW5kbGVDbGllbnRFcnJvcik7XG4gICAgY2xpZW50LnJlbGVhc2UoY2F1c2UpO1xuICAgIGlmICh0aGlzLmNsb3NlZCB8fCB0aGlzLmxpc3RlbmVycy5zaXplID09PSAwKSByZXR1cm47XG4gICAgdGhpcy5yZWNvbm5lY3RpbmcgPz89IHRoaXMucmVjb25uZWN0KCkuZmluYWxseSgoKSA9PiB7XG4gICAgICB0aGlzLnJlY29ubmVjdGluZyA9IHVuZGVmaW5lZDtcbiAgICB9KTtcbiAgfTtcblxuICAvKipcbiAgICogUmUtZXN0YWJsaXNoIHRoZSBub3RpZmljYXRpb24gY29ubmVjdGlvbiB3aXRoIGJvdW5kZWQgZXhwb25lbnRpYWwgYmFja29mZi5cbiAgICpcbiAgICogVGhlIGZpcnN0IGF0dGVtcHQgaXMgaW1tZWRpYXRlLCBzaW5jZSB0aGUgY29tbW9uIGNhc2UgaXMgYSBzaW5nbGUgZHJvcHBlZFxuICAgKiBjb25uZWN0aW9uIHRoYXQgcmVjb25uZWN0cyBhdCBvbmNlOyBzdWJzZXF1ZW50IGRlbGF5cyBkb3VibGUgZnJvbSAyNTBtcyB1cCB0b1xuICAgKiA1cyBhbmQgc3RheSB0aGVyZS4gUmV0cmllcyBpbmRlZmluaXRlbHkgcmF0aGVyIHRoYW4gZ2l2aW5nIHVwLCBiZWNhdXNlIGEgUG9zdGdyZXMgcmVzdGFydFxuICAgKiBvciBhIHJvdGF0ZWQgTGFrZWJhc2UgY3JlZGVudGlhbCBpcyBhIHJlY292ZXJhYmxlIG91dGFnZSBhbmQgYSBzaWxlbnRseSBkZWFkXG4gICAqIGxpc3RlbmVyIGlzIHdvcnNlIHRoYW4gYSBub2lzeSBvbmUuIFN0b3BzIHdoZW4gdGhlIGJ1cyBjbG9zZXMgb3IgdGhlIGxhc3RcbiAgICogbGlzdGVuZXIgbGVhdmVzLCBhbmQgcmVwb3J0cyBldmVyeSBmYWlsZWQgYXR0ZW1wdCB0aHJvdWdoIGBvbkVycm9yYC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGxldCBkZWxheSA9IDA7XG4gICAgd2hpbGUgKCF0aGlzLmNsb3NlZCAmJiB0aGlzLmxpc3RlbmVycy5zaXplID4gMCkge1xuICAgICAgaWYgKGRlbGF5ID4gMCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGFzeW5jVXRpbC5zbGVlcChkZWxheSwgdGhpcy5yZWNvbm5lY3RBYm9ydC5zaWduYWwpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMuc3RhcnQoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICAgICAgaWYgKHRoaXMuY2xvc2VkKSByZXR1cm47XG4gICAgICAgIHRoaXMub25FcnJvcihjYXVzZSk7XG4gICAgICAgIGRlbGF5ID0gZGVsYXkgPT09IDAgPyBNSU5fUkVDT05ORUNUX0RFTEFZX01TIDogTWF0aC5taW4oZGVsYXkgKiAyLCBNQVhfUkVDT05ORUNUX0RFTEFZX01TKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/advisory-lock.ts","../src/topic-bus.ts","../index.ts"],"version":"5.9.3"}
|