velocious 1.0.720 → 1.0.722
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/build/background-jobs/main.js +173 -4
- package/build/configuration-types.js +1 -1
- package/build/database/advisory-lock-reentrancy-node.js +9 -0
- package/build/database/advisory-lock-reentrancy.js +66 -0
- package/build/database/advisory-lock-runner.js +9 -12
- package/build/environment-handlers/node.js +1 -0
- package/build/routes/base-route.js +3 -2
- package/build/routes/basic-route.js +33 -8
- package/build/routes/get-route.js +17 -1
- package/build/routes/post-route.js +17 -1
- package/build/routes/resolver.js +5 -3
- package/build/src/background-jobs/main.d.ts +60 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +164 -6
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.js +2 -2
- package/build/src/database/advisory-lock-reentrancy-node.d.ts +2 -0
- package/build/src/database/advisory-lock-reentrancy-node.d.ts.map +1 -0
- package/build/src/database/advisory-lock-reentrancy-node.js +7 -0
- package/build/src/database/advisory-lock-reentrancy.d.ts +67 -0
- package/build/src/database/advisory-lock-reentrancy.d.ts.map +1 -0
- package/build/src/database/advisory-lock-reentrancy.js +60 -0
- package/build/src/database/advisory-lock-runner.d.ts +25 -33
- package/build/src/database/advisory-lock-runner.d.ts.map +1 -1
- package/build/src/database/advisory-lock-runner.js +10 -13
- package/build/src/environment-handlers/node.d.ts +1 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +2 -1
- package/build/src/routes/base-route.d.ts +3 -2
- package/build/src/routes/base-route.d.ts.map +1 -1
- package/build/src/routes/base-route.js +4 -3
- package/build/src/routes/basic-route.d.ts +19 -6
- package/build/src/routes/basic-route.d.ts.map +1 -1
- package/build/src/routes/basic-route.js +29 -9
- package/build/src/routes/get-route.d.ts +12 -1
- package/build/src/routes/get-route.d.ts.map +1 -1
- package/build/src/routes/get-route.js +16 -2
- package/build/src/routes/post-route.d.ts +12 -1
- package/build/src/routes/post-route.d.ts.map +1 -1
- package/build/src/routes/post-route.js +16 -2
- package/build/src/routes/resolver.d.ts +2 -1
- package/build/src/routes/resolver.d.ts.map +1 -1
- package/build/src/routes/resolver.js +6 -4
- package/build/src/sync/sync-cursor.d.ts +30 -0
- package/build/src/sync/sync-cursor.d.ts.map +1 -0
- package/build/src/sync/sync-cursor.js +54 -0
- package/build/src/sync/sync-realtime-bridge.d.ts +127 -24
- package/build/src/sync/sync-realtime-bridge.d.ts.map +1 -1
- package/build/src/sync/sync-realtime-bridge.js +92 -56
- package/build/src/sync/sync-websocket-channel.d.ts +49 -0
- package/build/src/sync/sync-websocket-channel.d.ts.map +1 -1
- package/build/src/sync/sync-websocket-channel.js +138 -2
- package/build/sync/sync-cursor.js +56 -0
- package/build/sync/sync-realtime-bridge.js +97 -51
- package/build/sync/sync-websocket-channel.js +153 -1
- package/package.json +1 -1
- package/src/background-jobs/main.js +173 -4
- package/src/configuration-types.js +1 -1
- package/src/database/advisory-lock-reentrancy-node.js +9 -0
- package/src/database/advisory-lock-reentrancy.js +66 -0
- package/src/database/advisory-lock-runner.js +9 -12
- package/src/environment-handlers/node.js +1 -0
- package/src/routes/base-route.js +3 -2
- package/src/routes/basic-route.js +33 -8
- package/src/routes/get-route.js +17 -1
- package/src/routes/post-route.js +17 -1
- package/src/routes/resolver.js +5 -3
- package/src/sync/sync-cursor.js +56 -0
- package/src/sync/sync-realtime-bridge.js +97 -51
- package/src/sync/sync-websocket-channel.js +153 -1
|
@@ -1,8 +1,46 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import VelociousWebsocketChannel from "../http-server/websocket-channel.js";
|
|
3
3
|
import { VELOCIOUS_SYNC_CHANNEL } from "./sync-channel-name.js";
|
|
4
|
+
import { cursorFromSyncEntry, isCursorAfter } from "./sync-cursor.js";
|
|
5
|
+
import SyncModelChangeFeedService from "./sync-model-change-feed-service.js";
|
|
4
6
|
/** Configurations whose framework sync channel has already been registered. */
|
|
5
7
|
const registeredConfigurations = new WeakSet();
|
|
8
|
+
/** Default catch-up replay page size when the subscriber sends no `limit` param. */
|
|
9
|
+
const DEFAULT_REPLAY_LIMIT = 1000;
|
|
10
|
+
/**
|
|
11
|
+
* Serializes a cursor into the change-feed request params under the given prefix.
|
|
12
|
+
* @param {"after" | "upTo"} prefix - Request field prefix.
|
|
13
|
+
* @param {import("./sync-api-client-types.js").SyncCursor} cursor - Cursor to serialize.
|
|
14
|
+
* @returns {Record<string, string | number | null>} Serialized cursor params.
|
|
15
|
+
*/
|
|
16
|
+
function cursorParams(prefix, cursor) {
|
|
17
|
+
if (!cursor)
|
|
18
|
+
return {};
|
|
19
|
+
return {
|
|
20
|
+
[`${prefix}Id`]: cursor.id,
|
|
21
|
+
...(cursor.serverSequence !== null ? { [`${prefix}ServerSequence`]: cursor.serverSequence } : {}),
|
|
22
|
+
[`${prefix}UpdatedAt`]: cursor.updatedAt
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reads the after-cursor the subscriber declared in its subscribe params, or null
|
|
27
|
+
* when it sent none (a fresh subscription replays the whole scope).
|
|
28
|
+
* @param {Record<string, ReturnType<typeof JSON.parse>>} params - Subscribe params.
|
|
29
|
+
* @returns {import("./sync-api-client-types.js").SyncCursor} Declared after-cursor, or null.
|
|
30
|
+
*/
|
|
31
|
+
function cursorFromParams(params) {
|
|
32
|
+
const updatedAt = typeof params.afterUpdatedAt === "string" ? params.afterUpdatedAt : null;
|
|
33
|
+
if (!updatedAt)
|
|
34
|
+
return null;
|
|
35
|
+
const serverSequence = params.afterServerSequence === null || params.afterServerSequence === undefined
|
|
36
|
+
? null
|
|
37
|
+
: Number(params.afterServerSequence);
|
|
38
|
+
return {
|
|
39
|
+
id: params.afterId === null || params.afterId === undefined ? null : String(params.afterId),
|
|
40
|
+
serverSequence,
|
|
41
|
+
updatedAt
|
|
42
|
+
};
|
|
43
|
+
}
|
|
6
44
|
/**
|
|
7
45
|
* Framework-owned websocket channel for synced resources
|
|
8
46
|
* ({@link VELOCIOUS_SYNC_CHANNEL}).
|
|
@@ -21,6 +59,13 @@ export default class SyncWebsocketChannel extends VelociousWebsocketChannel {
|
|
|
21
59
|
* @type {import("./sync-resource-base.js").SerializedChangesScope | null}
|
|
22
60
|
*/
|
|
23
61
|
_scope = null;
|
|
62
|
+
/**
|
|
63
|
+
* Cursor of the last sync entry delivered to this subscription, advanced by
|
|
64
|
+
* live broadcasts and catch-up replay so a resume replays only the rows missed
|
|
65
|
+
* while the socket was down.
|
|
66
|
+
* @type {import("./sync-api-client-types.js").SyncCursor}
|
|
67
|
+
*/
|
|
68
|
+
_deliveredCursor = null;
|
|
24
69
|
/**
|
|
25
70
|
* Registers the framework sync channel on a configuration declaring a
|
|
26
71
|
* `sync.api` block (guarded so repeated server boots with the same
|
|
@@ -154,11 +199,14 @@ export default class SyncWebsocketChannel extends VelociousWebsocketChannel {
|
|
|
154
199
|
async deliverBroadcast(body, meta) {
|
|
155
200
|
if (!this._isUserScope()) {
|
|
156
201
|
this.sendMessage(body, meta);
|
|
202
|
+
this._trackDeliveredCursor(body);
|
|
157
203
|
return;
|
|
158
204
|
}
|
|
159
205
|
const deliverableBody = await this._userScopeDeliverableBody(body);
|
|
160
|
-
if (deliverableBody !== null)
|
|
206
|
+
if (deliverableBody !== null) {
|
|
161
207
|
this.sendMessage(deliverableBody, meta);
|
|
208
|
+
this._trackDeliveredCursor(deliverableBody);
|
|
209
|
+
}
|
|
162
210
|
}
|
|
163
211
|
/**
|
|
164
212
|
* Whether this subscription is a user scope: authorized with empty conditions
|
|
@@ -221,6 +269,94 @@ export default class SyncWebsocketChannel extends VelociousWebsocketChannel {
|
|
|
221
269
|
return { ...envelope, syncs: deliverableSyncs };
|
|
222
270
|
return deliverableSyncs[0];
|
|
223
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Replays the catch-up backlog to this subscriber once its subscription is
|
|
274
|
+
* authorized: pages the scoped change feed from the cursor the client declared
|
|
275
|
+
* in its subscribe params and sends each page as a standard sync envelope, then
|
|
276
|
+
* live broadcasts continue from there. A fresh subscription with no cursor
|
|
277
|
+
* replays the whole authorized scope.
|
|
278
|
+
* @returns {Promise<void>}
|
|
279
|
+
*/
|
|
280
|
+
async subscribed() {
|
|
281
|
+
if (!this._scope)
|
|
282
|
+
return;
|
|
283
|
+
await this._replayFrom(cursorFromParams(this.params));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Replays rows missed while the socket was down, resuming from the cursor of
|
|
287
|
+
* the last entry this subscription delivered instead of re-reading the whole
|
|
288
|
+
* scope from the subscribe-time cursor.
|
|
289
|
+
* @returns {Promise<void>}
|
|
290
|
+
*/
|
|
291
|
+
async onResume() {
|
|
292
|
+
if (!this._scope)
|
|
293
|
+
return;
|
|
294
|
+
await this._replayFrom(this._deliveredCursor ?? cursorFromParams(this.params));
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Pages the scoped change feed from a cursor and delivers each page to this
|
|
298
|
+
* subscriber as a `{echoOrigin, syncs}` envelope. The snapshot high-water is
|
|
299
|
+
* resolved once and pinned across pages so rows arriving mid-replay are left to
|
|
300
|
+
* the live broadcast path instead of looping the replay. The delivered cursor
|
|
301
|
+
* advances so a later resume resumes from here.
|
|
302
|
+
* @param {import("./sync-api-client-types.js").SyncCursor} afterCursor - Cursor to replay after.
|
|
303
|
+
* @returns {Promise<void>}
|
|
304
|
+
*/
|
|
305
|
+
async _replayFrom(afterCursor) {
|
|
306
|
+
const configuration = this.session.configuration;
|
|
307
|
+
const scope = this._scope;
|
|
308
|
+
await configuration.ensureConnections({ name: `${VELOCIOUS_SYNC_CHANNEL} catch-up replay` }, async () => {
|
|
309
|
+
const resource = await this.buildSyncResource();
|
|
310
|
+
const limit = this._replayLimit(resource);
|
|
311
|
+
/** @type {import("./sync-api-client-types.js").SyncCursor} */
|
|
312
|
+
let upToCursor = null;
|
|
313
|
+
while (true) {
|
|
314
|
+
const page = await resource.changeFeedService({
|
|
315
|
+
params: { ...this.params, ...cursorParams("after", afterCursor), ...(upToCursor ? cursorParams("upTo", upToCursor) : {}) },
|
|
316
|
+
scope
|
|
317
|
+
}).changes();
|
|
318
|
+
if (!upToCursor)
|
|
319
|
+
upToCursor = page.upToCursor;
|
|
320
|
+
if (page.syncs.length === 0)
|
|
321
|
+
break;
|
|
322
|
+
await this.deliverBroadcast({ echoOrigin: null, syncs: page.syncs });
|
|
323
|
+
afterCursor = page.nextCursor;
|
|
324
|
+
if (afterCursor)
|
|
325
|
+
this._deliveredCursor = afterCursor;
|
|
326
|
+
if (page.syncs.length < limit)
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Resolves the catch-up replay page size from the subscriber's `limit` param
|
|
333
|
+
* (defaulting to {@link DEFAULT_REPLAY_LIMIT}).
|
|
334
|
+
* @param {import("./sync-resource-base.js").default} resource - Authorizing sync resource.
|
|
335
|
+
* @returns {number} Replay page size.
|
|
336
|
+
*/
|
|
337
|
+
_replayLimit(resource) {
|
|
338
|
+
return new SyncModelChangeFeedService({ modelClass: resource.syncModelClass(), params: this.params }).normalizedLimit(this.params.limit);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Advances the delivered-cursor watermark from a delivered envelope so a resume
|
|
342
|
+
* resumes after the newest row actually sent to this subscriber. Broadcasts may
|
|
343
|
+
* arrive out of order across resources, so each entry only advances the
|
|
344
|
+
* watermark when it is later than the current one.
|
|
345
|
+
* @param {import("../http-server/websocket-channel.js").WebsocketJsonValue} body - Delivered envelope.
|
|
346
|
+
* @returns {void}
|
|
347
|
+
*/
|
|
348
|
+
_trackDeliveredCursor(body) {
|
|
349
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
350
|
+
return;
|
|
351
|
+
const envelope = /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (body);
|
|
352
|
+
const syncs = Array.isArray(envelope.syncs) ? envelope.syncs : [envelope];
|
|
353
|
+
for (const entry of syncs) {
|
|
354
|
+
const syncEntry = /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (entry);
|
|
355
|
+
const cursor = cursorFromSyncEntry(syncEntry);
|
|
356
|
+
if (cursor && isCursorAfter(cursor, this._deliveredCursor))
|
|
357
|
+
this._deliveredCursor = cursor;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
224
360
|
/**
|
|
225
361
|
* Returns the authorized scope for debug snapshots.
|
|
226
362
|
* @returns {Record<string, ReturnType<typeof JSON.parse>>} Debug-safe subscription details.
|
|
@@ -229,4 +365,4 @@ export default class SyncWebsocketChannel extends VelociousWebsocketChannel {
|
|
|
229
365
|
return { scope: this._scope, userScope: this._isUserScope() };
|
|
230
366
|
}
|
|
231
367
|
}
|
|
232
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy13ZWJzb2NrZXQtY2hhbm5lbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zeW5jL3N5bmMtd2Vic29ja2V0LWNoYW5uZWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8seUJBQXlCLE1BQU0scUNBQXFDLENBQUE7QUFDM0UsT0FBTyxFQUFDLHNCQUFzQixFQUFDLE1BQU0sd0JBQXdCLENBQUE7QUFFN0QsK0VBQStFO0FBQy9FLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQTtBQUU5Qzs7Ozs7Ozs7Ozs7R0FXRztBQUNILE1BQU0sQ0FBQyxPQUFPLE9BQU8sb0JBQXFCLFNBQVEseUJBQXlCO0lBQ3pFOzs7T0FHRztJQUNILE1BQU0sR0FBRyxJQUFJLENBQUE7SUFFYjs7Ozs7OztPQU9HO0lBQ0gsTUFBTSxDQUFDLHlCQUF5QixDQUFDLGFBQWE7UUFDNUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDLEdBQUcsSUFBSSx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDO1lBQUUsT0FBTTtRQUVwRyx3QkFBd0IsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUE7UUFDM0MsYUFBYSxDQUFDLHdCQUF3QixDQUFDLHNCQUFzQixFQUFFLElBQUksQ0FBQyxDQUFBO0lBQ3RFLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsWUFBWTtRQUNoQixNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFBO1FBQy9DLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxZQUFZLENBQUMsRUFBQyxLQUFLLEVBQUUsRUFBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsWUFBWSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsWUFBWSxFQUFFLGFBQWEsRUFBRSxJQUFJLENBQUMsTUFBTSxD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QUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxZQUFZLEVBQUUsRUFBQyxDQUFBO0lBQzdELENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgVmVsb2Npb3VzV2Vic29ja2V0Q2hhbm5lbCBmcm9tIFwiLi4vaHR0cC1zZXJ2ZXIvd2Vic29ja2V0LWNoYW5uZWwuanNcIlxuaW1wb3J0IHtWRUxPQ0lPVVNfU1lOQ19DSEFOTkVMfSBmcm9tIFwiLi9zeW5jLWNoYW5uZWwtbmFtZS5qc1wiXG5cbi8qKiBDb25maWd1cmF0aW9ucyB3aG9zZSBmcmFtZXdvcmsgc3luYyBjaGFubmVsIGhhcyBhbHJlYWR5IGJlZW4gcmVnaXN0ZXJlZC4gKi9cbmNvbnN0IHJlZ2lzdGVyZWRDb25maWd1cmF0aW9ucyA9IG5ldyBXZWFrU2V0KClcblxuLyoqXG4gKiBGcmFtZXdvcmstb3duZWQgd2Vic29ja2V0IGNoYW5uZWwgZm9yIHN5bmNlZCByZXNvdXJjZXNcbiAqICh7QGxpbmsgVkVMT0NJT1VTX1NZTkNfQ0hBTk5FTH0pLlxuICpcbiAqIFN1YnNjcmliZSBwYXJhbXMgbWlycm9yIGEgZGVjbGFyZWQgcHVsbCBzY29wZSDigJQgYHtyZXNvdXJjZVR5cGUsIGNvbmRpdGlvbnN9YFxuICogcGx1cyB0aGUgY2xpZW50LWluamVjdGVkIGBhdXRoZW50aWNhdGlvblRva2VuYCDigJQgYW5kIHN1YnNjcmliZS10aW1lXG4gKiBhdXRob3JpemF0aW9uIGRlbGVnYXRlcyB0byB0aGUgYXBwIHN5bmMgcmVzb3VyY2UncyBleGlzdGluZ1xuICogYGF1dGhvcml6ZUNoYW5nZXMoe3BhcmFtcywgc2NvcGV9KWAgKHRoZSBgc3luYy5hcGkucmVzb3VyY2VDbGFzc2ApLCBzbyBhcHBzXG4gKiBob29rIGluIHRocm91Z2ggdGhlIGF1dGhvcml6YXRpb24gdGhleSBhbHJlYWR5IGRlY2xhcmVkIGluc3RlYWQgb2Ygd3JpdGluZ1xuICogdGhlaXIgb3duIGNoYW5uZWwgY2xhc3Nlcy4gQnJvYWRjYXN0IHJvdXRpbmcgbWF0Y2hlcyB0aGUgcHVibGlzaGVyJ3NcbiAqIHNjb3BpbmcgcGFyYW1zIGFnYWluc3QgdGhlIHN1YnNjcmlwdGlvbidzIHNjb3BlIGNvbmRpdGlvbnMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFN5bmNXZWJzb2NrZXRDaGFubmVsIGV4dGVuZHMgVmVsb2Npb3VzV2Vic29ja2V0Q2hhbm5lbCB7XG4gIC8qKlxuICAgKiBTY29wZSB0aGUgc3Vic2NyaXB0aW9uIHdhcyBhdXRob3JpemVkIGZvciwgc2V0IGJ5IHtAbGluayBTeW5jV2Vic29ja2V0Q2hhbm5lbCNjYW5TdWJzY3JpYmV9LlxuICAgKiBAdHlwZSB7aW1wb3J0KFwiLi9zeW5jLXJlc291cmNlLWJhc2UuanNcIikuU2VyaWFsaXplZENoYW5nZXNTY29wZSB8IG51bGx9XG4gICAqL1xuICBfc2NvcGUgPSBudWxsXG5cbiAgLyoqXG4gICAqIFJlZ2lzdGVycyB0aGUgZnJhbWV3b3JrIHN5bmMgY2hhbm5lbCBvbiBhIGNvbmZpZ3VyYXRpb24gZGVjbGFyaW5nIGFcbiAgICogYHN5bmMuYXBpYCBibG9jayAoZ3VhcmRlZCBzbyByZXBlYXRlZCBzZXJ2ZXIgYm9vdHMgd2l0aCB0aGUgc2FtZVxuICAgKiBjb25maWd1cmF0aW9uIHJlZ2lzdGVyIGl0IG9ubHkgb25jZSkuIE5vLW9wIHdpdGhvdXQgYHN5bmMuYXBpYCDigJQgdGhlXG4gICAqIGNoYW5uZWwgYXV0aG9yaXplcyB0aHJvdWdoIHRoZSBhcHAncyBzeW5jIHJlc291cmNlIGNsYXNzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24gaW5zdGFuY2UuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgc3RhdGljIHJlZ2lzdGVyRnJvbUNvbmZpZ3VyYXRpb24oY29uZmlndXJhdGlvbikge1xuICAgIGlmICghY29uZmlndXJhdGlvbi5nZXRTeW5jQ29uZmlndXJhdGlvbigpLmFwaSB8fCByZWdpc3RlcmVkQ29uZmlndXJhdGlvbnMuaGFzKGNvbmZpZ3VyYXRpb24pKSByZXR1cm5cblxuICAgIHJlZ2lzdGVyZWRDb25maWd1cmF0aW9ucy5hZGQoY29uZmlndXJhdGlvbilcbiAgICBjb25maWd1cmF0aW9uLnJlZ2lzdGVyV2Vic29ja2V0Q2hhbm5lbChWRUxPQ0lPVVNfU1lOQ19DSEFOTkVMLCB0aGlzKVxuICB9XG5cbiAgLyoqXG4gICAqIEF1dGhvcml6ZXMgdGhlIHN1YnNjcmlwdGlvbiB0aHJvdWdoIHRoZSBhcHAgc3luYyByZXNvdXJjZTogdGhlIHN1YnNjcmliZVxuICAgKiBwYXJhbXMgYXJlIHBhcnNlZCBpbnRvIHRoZSBzYW1lIHNlcmlhbGl6ZWQgc2NvcGUgdGhlIGNoYW5nZXMgZW5kcG9pbnRcbiAgICogY29uc3VtZXMgYW5kIHBhc3NlZCB0byB0aGUgcmVzb3VyY2UncyBgYXV0aG9yaXplQ2hhbmdlcyh7cGFyYW1zLCBzY29wZX0pYC5cbiAgICogRGVuaWFscyBhbmQgbWFsZm9ybWVkIHNjb3BlcyB0aHJvdywgcmVqZWN0aW5nIHRoZSBzdWJzY3JpcHRpb24uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBXaGV0aGVyIHRoZSBzdWJzY3JpcHRpb24gaXMgYWxsb3dlZC5cbiAgICovXG4gIGFzeW5jIGNhblN1YnNjcmliZSgpIHtcbiAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHRoaXMuYnVpbGRTeW5jUmVzb3VyY2UoKVxuICAgIGNvbnN0IHNjb3BlID0gcmVzb3VyY2UuY2hhbmdlc1Njb3BlKHtzY29wZToge2NvbmRpdGlvbnM6IHRoaXMucGFyYW1zLmNvbmRpdGlvbnMsIHJlc291cmNlVHlwZTogdGhpcy5wYXJhbXMucmVzb3VyY2VUeXBlLCByZXNvdXJjZVR5cGVzOiB0aGlzLnBhcmFtcy5yZXNvdXJjZVR5cGVzfX0pXG5cbiAgICBhd2FpdCByZXNvdXJjZS5hdXRob3JpemVDaGFuZ2VzKHtwYXJhbXM6IHRoaXMucGFyYW1zLCBzY29wZX0pXG5cbiAgICB0aGlzLl9zY29wZSA9IHNjb3BlXG5cbiAgICByZXR1cm4gdHJ1ZVxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyB0aGUgYXBwIHN5bmMgcmVzb3VyY2UgYXV0aG9yaXppbmcgdGhpcyBzdWJzY3JpcHRpb24sIG1pcnJvcmluZyB0aGVcbiAgICogc3luYyB0cmFuc3BvcnQgY29udHJvbGxlcidzIHJlc291cmNlIGNvbnN0cnVjdGlvbiB3aXRoIHRoZSBhYmlsaXR5XG4gICAqIHJlc29sdmVkIGZyb20gdGhlIHN1YnNjcmliZSBwYXJhbXMgKHdoaWNoIGNhcnJ5IHRoZSBjbGllbnQtaW5qZWN0ZWRcbiAgICogYXV0aGVudGljYXRpb25Ub2tlbikuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vc3luYy1yZXNvdXJjZS1iYXNlLmpzXCIpLmRlZmF1bHQ+fSBBcHAgc3luYyByZXNvdXJjZSBpbnN0YW5jZS5cbiAgICovXG4gIGFzeW5jIGJ1aWxkU3luY1Jlc291cmNlKCkge1xuICAgIGNvbnN0IGNvbmZpZ3VyYXRpb24gPSB0aGlzLnNlc3Npb24uY29uZmlndXJhdGlvblxuICAgIGNvbnN0IGFwaSA9IGNvbmZpZ3VyYXRpb24uZ2V0U3luY0NvbmZpZ3VyYXRpb24oKS5hcGlcblxuICAgIGlmICghYXBpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRoZSAke1ZFTE9DSU9VU19TWU5DX0NIQU5ORUx9IGNoYW5uZWwgcmVxdWlyZXMgYSBzeW5jLmFwaSBjb25maWd1cmF0aW9uIGJsb2NrIHdpdGggdGhlIGFwcCdzIHN5bmMgcmVzb3VyY2UgY2xhc3NgKVxuICAgIH1cblxuICAgIC8vIE5hcnJvd3MgdGhlIGNvbmZpZ3VyZWQgcmVzb3VyY2UgY2xhc3MgdG8gdGhlIHN5bmMgcmVzb3VyY2UgY29udHJhY3RcbiAgICAvLyAoY2hhbmdlc1Njb3BlL2F1dGhvcml6ZUNoYW5nZXMpIHRoZSBzeW5jLmFwaSB2YWxpZGF0aW9uIHJlcXVpcmVzLlxuICAgIGNvbnN0IFJlc291cmNlQ2xhc3MgPSAvKiogQHR5cGUge3R5cGVvZiBpbXBvcnQoXCIuL3N5bmMtcmVzb3VyY2UtYmFzZS5qc1wiKS5kZWZhdWx0fSAqLyAoYXBpLnJlc291cmNlQ2xhc3MpXG4gICAgLy8gTmFycm93cyB0aGUgd2Vic29ja2V0IHN1YnNjcmliZSBwYXJhbXMgdG8gdGhlIHJlc291cmNlIHBhcmFtcyBjb250cmFjdC5cbiAgICBjb25zdCBwYXJhbXMgPSAvKiogQHR5cGUge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuVmVsb2Npb3VzUGFyYW1zfSAqLyAoLyoqIEB0eXBlIHt1bmtub3dufSAqLyAodGhpcy5wYXJhbXMpKVxuICAgIGNvbnN0IHJlcXVlc3QgPSB0aGlzLnNlc3Npb24udXBncmFkZVJlcXVlc3RcbiAgICBjb25zdCBhYmlsaXR5ID0gYXdhaXQgY29uZmlndXJhdGlvbi5yZXNvbHZlQWJpbGl0eSh7cGFyYW1zLCByZXF1ZXN0fSlcblxuICAgIHJldHVybiBuZXcgUmVzb3VyY2VDbGFzcyh7XG4gICAgICBhYmlsaXR5LFxuICAgICAgY29uZmlndXJhdGlvbixcbiAgICAgIGNvbnRleHQ6IHtcbiAgICAgICAgLi4uKGFiaWxpdHk/LmdldENvbnRleHQoKSB8fCB7fSksXG4gICAgICAgIHBhcmFtcyxcbiAgICAgICAgcmVxdWVzdFxuICAgICAgfSxcbiAgICAgIGxvY2FsczogYWJpbGl0eT8uZ2V0TG9jYWxzKCkgfHwge30sXG4gICAgICBtb2RlbENsYXNzOiBSZXNvdXJjZUNsYXNzLk1vZGVsQ2xhc3MsXG4gICAgICBtb2RlbE5hbWU6IFJlc291cmNlQ2xhc3MuTW9kZWxDbGFzcz8ubmFtZSxcbiAgICAgIHBhcmFtcyxcbiAgICAgIHJlc291cmNlQ29uZmlndXJhdGlvbjogLyoqIEB0eXBlIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxSZXNvdXJjZUNvbmZpZ3VyYXRpb259ICovICh7XG4gICAgICAgIGF0dHJpYnV0ZXM6IFJlc291cmNlQ2xhc3MuYXR0cmlidXRlcyB8fCB7fSxcbiAgICAgICAgc3luYzoge2VuYWJsZWQ6IHRydWV9XG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogUm91dGVzIGEgcHVibGlzaGVyIGJyb2FkY2FzdCB0byB0aGlzIHN1YnNjcmlwdGlvbiB3aGVuIHRoZSBwdWJsaXNoZWRcbiAgICogcmVzb3VyY2UgdHlwZSBlcXVhbHMgdGhlIHR5cGUgdGhlIHN1YnNjcmlwdGlvbiB3YXMgYXV0aG9yaXplZCBmb3IgYW5kIHRoZVxuICAgKiBzY29waW5nIHBhcmFtcyBzYXRpc2Z5IGV2ZXJ5IHNjb3BlIGNvbmRpdGlvbjogZWFjaCBjb25kaXRpb24ga2V5IG11c3QgYmVcbiAgICogcHJlc2VudCBpbiB0aGUgYnJvYWRjYXN0IHBhcmFtcyBhbmQgbWF0Y2ggYnkgc3RyaW5nIGNvbXBhcmlzb24gKGFycmF5XG4gICAqIGNvbmRpdGlvbnMgbWF0Y2ggYnkgbWVtYmVyc2hpcCkuIEJyb2FkY2FzdHMgd2l0aG91dCBhIHJlc291cmNlIHR5cGUgYW5kXG4gICAqIGNvbmRpdGlvbnMgdGhlIHB1Ymxpc2hlcidzIHNjb3BpbmcgcGFyYW1zIGRvIG5vdCBjYXJyeSBuZXZlciBtYXRjaCwgc28gYVxuICAgKiBzdWJzY3JpcHRpb24gY2Fubm90IHJlY2VpdmUgY2hhbmdlcyBvdXRzaWRlIGl0cyBhdXRob3JpemVkIHNjb3BlLlxuICAgKlxuICAgKiBUaGUgYWxsLXR5cGVzICh1c2VyKSBzY29wZSBkZWNsYXJlcyBubyBzaW5nbGUgcmVzb3VyY2UgdHlwZSAtIGl0IHN1YnNjcmliZXNcbiAgICogb25jZSBmb3IgZXZlcnkgdHlwZSBpdCBjYW4gYXBwbHksIGxpc3RlZCBpbiBgc2NvcGUucmVzb3VyY2VUeXBlc2AgLSBzbyBpdFxuICAgKiBtYXRjaGVzIGEgYnJvYWRjYXN0IG9mIGFueSBvZiB0aG9zZSB0eXBlcy4gVHlwZXMgb3V0c2lkZSB0aGF0IGxpc3QgYXJlXG4gICAqIGRyb3BwZWQgaGVyZSwgYnkgdGhpcyBjaGVhcCBjaGVjaywgYW5kIG5ldmVyIHJlYWNoIHRoZSBwZXItZGVsaXZlcnkgYWNjZXNzXG4gICAqIHJlLWNoZWNrIGluIHtAbGluayBTeW5jV2Vic29ja2V0Q2hhbm5lbCNfdXNlclNjb3BlRGVsaXZlcmFibGVCb2R5fTogdGhhdFxuICAgKiByZS1jaGVjayBjaGVja3Mgb3V0IGEgZGF0YWJhc2UgY29ubmVjdGlvbiBhbmQgcnVucyBhIHF1ZXJ5IHBlciBtYXRjaGVkXG4gICAqIGJyb2FkY2FzdCwgc28gbWF0Y2hpbmcgZXZlcnkgdHlwZSB3b3VsZCBwdXQgREIgd29yayBvbiBldmVyeSBicm9hZGNhc3QgZm9yXG4gICAqIGV2ZXJ5IHN1YnNjcmliZWQgZGV2aWNlLiBBIGJyb2FkY2FzdCBjYXJyeWluZyBubyByZXNvdXJjZSB0eXBlIGF0IGFsbCBzdGlsbFxuICAgKiBuZXZlciBtYXRjaGVzLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2h0dHAtc2VydmVyL3dlYnNvY2tldC1jaGFubmVsLmpzXCIpLldlYnNvY2tldEpzb25WYWx1ZX0gYnJvYWRjYXN0UGFyYW1zIC0gUHVibGlzaGVyIHNjb3BpbmcgcGFyYW1zICh0aGUgcHVibGlzaGVkIHJlc291cmNlVHlwZSBwbHVzIHRoZSBjaGFuZ2UncyBzY29wZS1wYXJ0aXRpb24gdmFsdWVzKS5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIgdGhlIGJyb2FkY2FzdCBiZWxvbmdzIHRvIHRoaXMgc3Vic2NyaXB0aW9uJ3Mgc2NvcGUuXG4gICAqL1xuICBtYXRjaGVzKGJyb2FkY2FzdFBhcmFtcykge1xuICAgIGNvbnN0IHNjb3BlID0gdGhpcy5fc2NvcGVcblxuICAgIGlmICghc2NvcGUpIHJldHVybiBmYWxzZVxuXG4gICAgY29uc3Qgc2NvcGluZ1BhcmFtcyA9IGJyb2FkY2FzdFBhcmFtcyAmJiB0eXBlb2YgYnJvYWRjYXN0UGFyYW1zID09PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KGJyb2FkY2FzdFBhcmFtcylcbiAgICAgID8gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59ICovIChicm9hZGNhc3RQYXJhbXMpXG4gICAgICA6IHt9XG5cbiAgICBpZiAoIU9iamVjdC5oYXNPd24oc2NvcGluZ1BhcmFtcywgXCJyZXNvdXJjZVR5cGVcIikpIHJldHVybiBmYWxzZVxuXG4gICAgaWYgKHNjb3BlLnJlc291cmNlVHlwZSA9PT0gbnVsbCkge1xuICAgICAgaWYgKHNjb3BlLnJlc291cmNlVHlwZXMgJiYgIXNjb3BlLnJlc291cmNlVHlwZXMuc29tZSgocmVzb3VyY2VUeXBlKSA9PiByZXNvdXJjZVR5cGUgPT09IFN0cmluZyhzY29waW5nUGFyYW1zLnJlc291cmNlVHlwZSkpKSByZXR1cm4gZmFsc2VcbiAgICB9IGVsc2UgaWYgKFN0cmluZyhzY29waW5nUGFyYW1zLnJlc291cmNlVHlwZSkgIT09IFN0cmluZyhzY29wZS5yZXNvdXJjZVR5cGUpKSB7XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IFtjb25kaXRpb25OYW1lLCBjb25kaXRpb25WYWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2NvcGUuY29uZGl0aW9ucykpIHtcbiAgICAgIGlmICghT2JqZWN0Lmhhc093bihzY29waW5nUGFyYW1zLCBjb25kaXRpb25OYW1lKSkgcmV0dXJuIGZhbHNlXG5cbiAgICAgIGNvbnN0IHNjb3BpbmdWYWx1ZSA9IHNjb3BpbmdQYXJhbXNbY29uZGl0aW9uTmFtZV1cblxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoY29uZGl0aW9uVmFsdWUpKSB7XG4gICAgICAgIGlmICghY29uZGl0aW9uVmFsdWUuc29tZSgodmFsdWUpID0+IFN0cmluZyh2YWx1ZSkgPT09IFN0cmluZyhzY29waW5nVmFsdWUpKSkgcmV0dXJuIGZhbHNlXG4gICAgICB9IGVsc2UgaWYgKFN0cmluZyhjb25kaXRpb25WYWx1ZSkgIT09IFN0cmluZyhzY29waW5nVmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICAvKipcbiAgICogRGVsaXZlcnMgYSBtYXRjaGVkIGJyb2FkY2FzdC4gU2NvcGVkIHN1YnNjcmlwdGlvbnMgKHdpdGggZXhwbGljaXRcbiAgICogY29uZGl0aW9ucykgYWxyZWFkeSByb3V0ZWQgdGhyb3VnaCB7QGxpbmsgU3luY1dlYnNvY2tldENoYW5uZWwjbWF0Y2hlc30sIHNvXG4gICAqIHRoZSBjaGFuZ2UgaXMgaW4gc2NvcGUgYW5kIGRlbGl2ZXJzIHVuY2hhbmdlZC4gVXNlci1zY29wZSBzdWJzY3JpcHRpb25zXG4gICAqIChlbXB0eSBjb25kaXRpb25zLCBcImV2ZXJ5dGhpbmcgbXkgYWJpbGl0eSBjYW4gc2VlXCIpIG1hdGNoIGV2ZXJ5IGJyb2FkY2FzdFxuICAgKiBvZiB0aGUgcmVzb3VyY2UgdHlwZSwgc28gZWFjaCBwdWJsaXNoZWQgY2hhbmdlIGlzIHJlLWNoZWNrZWQgYWdhaW5zdCB0aGVcbiAgICogc3Vic2NyaWJlcidzIGFiaWxpdHkgYXQgZmFuLW91dCB0aHJvdWdoIHRoZSBhcHAgc3luYyByZXNvdXJjZSdzXG4gICAqIGBjaGFuZ2VEZWxpdmVyYWJsZWA7IG9ubHkgYWNjZXNzaWJsZSBjaGFuZ2VzIGFyZSBkZWxpdmVyZWQsIGFuZCBhIGJyb2FkY2FzdFxuICAgKiB3aXRoIG5vIGFjY2Vzc2libGUgY2hhbmdlIGlzIGRyb3BwZWQuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vaHR0cC1zZXJ2ZXIvd2Vic29ja2V0LWNoYW5uZWwuanNcIikuV2Vic29ja2V0SnNvblZhbHVlfSBib2R5IC0gQnJvYWRjYXN0IGJvZHkgKHN5bmMgZW52ZWxvcGUpLlxuICAgKiBAcGFyYW0ge3tldmVudElkPzogc3RyaW5nfX0gW21ldGFdIC0gT3B0aW9uYWwgZXZlbnQgbWV0YWRhdGEuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgZGVsaXZlckJyb2FkY2FzdChib2R5LCBtZXRhKSB7XG4gICAgaWYgKCF0aGlzLl9pc1VzZXJTY29wZSgpKSB7XG4gICAgICB0aGlzLnNlbmRNZXNzYWdlKGJvZHksIG1ldGEpXG5cbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IGRlbGl2ZXJhYmxlQm9keSA9IGF3YWl0IHRoaXMuX3VzZXJTY29wZURlbGl2ZXJhYmxlQm9keShib2R5KVxuXG4gICAgaWYgKGRlbGl2ZXJhYmxlQm9keSAhPT0gbnVsbCkgdGhpcy5zZW5kTWVzc2FnZShkZWxpdmVyYWJsZUJvZHksIG1ldGEpXG4gIH1cblxuICAvKipcbiAgICogV2hldGhlciB0aGlzIHN1YnNjcmlwdGlvbiBpcyBhIHVzZXIgc2NvcGU6IGF1dGhvcml6ZWQgd2l0aCBlbXB0eSBjb25kaXRpb25zXG4gICAqIChcImV2ZXJ5dGhpbmcgbXkgYWJpbGl0eSBjYW4gc2VlXCIpLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gV2hldGhlciB0aGUgc3Vic2NyaXB0aW9uIGlzIGEgdXNlciBzY29wZS5cbiAgICovXG4gIF9pc1VzZXJTY29wZSgpIHtcbiAgICByZXR1cm4gQm9vbGVhbih0aGlzLl9zY29wZSkgJiYgT2JqZWN0LmtleXMoLyoqIEB0eXBlIHtpbXBvcnQoXCIuL3N5bmMtcmVzb3VyY2UtYmFzZS5qc1wiKS5TZXJpYWxpemVkQ2hhbmdlc1Njb3BlfSAqLyAodGhpcy5fc2NvcGUpLmNvbmRpdGlvbnMpLmxlbmd0aCA9PT0gMFxuICB9XG5cbiAgLyoqXG4gICAqIEZpbHRlcnMgYSB1c2VyLXNjb3BlIGJyb2FkY2FzdCB0byB0aGUgc3luYyBlbnRyaWVzIHRoZSBzdWJzY3JpYmVyJ3MgYWJpbGl0eVxuICAgKiBjYW4gYWNjZXNzLCByZS1jaGVja2luZyBlYWNoIHRocm91Z2ggdGhlIGFwcCBzeW5jIHJlc291cmNlJ3NcbiAgICogYGNoYW5nZURlbGl2ZXJhYmxlYC4gUmV0dXJucyB0aGUgYnJvYWRjYXN0IG5hcnJvd2VkIHRvIGFjY2Vzc2libGUgZW50cmllcyxcbiAgICogb3IgbnVsbCB3aGVuIG5vbmUgYXJlIGFjY2Vzc2libGUuIE5vbi1lbnZlbG9wZSBib2RpZXMgYW5kIGVudHJpZXMgd2l0aG91dCBhXG4gICAqIHJlc291cmNlIGlkIGFyZSBkcm9wcGVkIChmYWlsIGNsb3NlZCkuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vaHR0cC1zZXJ2ZXIvd2Vic29ja2V0LWNoYW5uZWwuanNcIikuV2Vic29ja2V0SnNvblZhbHVlfSBib2R5IC0gQnJvYWRjYXN0IGJvZHkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4uL2h0dHAtc2VydmVyL3dlYnNvY2tldC1jaGFubmVsLmpzXCIpLldlYnNvY2tldEpzb25WYWx1ZSB8IG51bGw+fSBEZWxpdmVyYWJsZSBib2R5LCBvciBudWxsLlxuICAgKi9cbiAgYXN5bmMgX3VzZXJTY29wZURlbGl2ZXJhYmxlQm9keShib2R5KSB7XG4gICAgaWYgKCFib2R5IHx8IHR5cGVvZiBib2R5ICE9PSBcIm9iamVjdFwiIHx8IEFycmF5LmlzQXJyYXkoYm9keSkpIHJldHVybiBudWxsXG5cbiAgICBjb25zdCBlbnZlbG9wZSA9IC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+fSAqLyAoYm9keSlcbiAgICBjb25zdCBzY29wZSA9IC8qKiBAdHlwZSB7aW1wb3J0KFwiLi9zeW5jLXJlc291cmNlLWJhc2UuanNcIikuU2VyaWFsaXplZENoYW5nZXNTY29wZX0gKi8gKHRoaXMuX3Njb3BlKVxuICAgIC8qKiBAdHlwZSB7QXJyYXk8UmVjb3JkPHN0cmluZywgUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+Pn0gKi9cbiAgICBjb25zdCBzeW5jcyA9IEFycmF5LmlzQXJyYXkoZW52ZWxvcGUuc3luY3MpID8gZW52ZWxvcGUuc3luY3MgOiBbZW52ZWxvcGVdXG4gICAgY29uc3QgY29uZmlndXJhdGlvbiA9IHRoaXMuc2Vzc2lvbi5jb25maWd1cmF0aW9uXG4gICAgLyoqIEB0eXBlIHtBcnJheTxSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj4+fSAqL1xuICAgIGNvbnN0IGRlbGl2ZXJhYmxlU3luY3MgPSBbXVxuXG4gICAgLy8gQnJvYWRjYXN0IGZhbi1vdXQgcnVucyB0aHJvdWdoIGB3aXRob3V0Q3VycmVudENvbm5lY3Rpb25Db250ZXh0c2AgKHNlZVxuICAgIC8vIENvbmZpZ3VyYXRpb24jX2Jyb2FkY2FzdFRvQ2hhbm5lbExvY2FsKSwgc28gdGhlcmUgaXMgbm8gYW1iaWVudCBkYXRhYmFzZVxuICAgIC8vIGNvbm5lY3Rpb24gaGVyZS4gUmVzb2x2ZSB0aGUgcmVzb3VyY2UncyBhYmlsaXR5IGFuZCBydW4gdGhlIHBlci1kZWxpdmVyeVxuICAgIC8vIGFjY2VzcyBxdWVyeSBpbnNpZGUgYSBjaGVja2VkLW91dCBjb25uZWN0aW9uIGNvbnRleHQsIG1pcnJvcmluZyBob3cgb3RoZXJcbiAgICAvLyBicm9hZGNhc3QtdGltZSBEQiB3b3JrICh0aGUgZnJvbnRlbmQtbW9kZWwgY2hhbm5lbCkgb2J0YWlucyBjb25uZWN0aW9ucy5cbiAgICBhd2FpdCBjb25maWd1cmF0aW9uLmVuc3VyZUNvbm5lY3Rpb25zKHtuYW1lOiBgJHtWRUxPQ0lPVVNfU1lOQ19DSEFOTkVMfSB1c2VyLXNjb3BlIGRlbGl2ZXJ5IGFjY2VzcyBjaGVja2B9LCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHRoaXMuYnVpbGRTeW5jUmVzb3VyY2UoKVxuXG4gICAgICBmb3IgKGNvbnN0IHN5bmMgb2Ygc3luY3MpIHtcbiAgICAgICAgY29uc3QgcmVzb3VyY2VJZCA9IHN5bmM/LnJlc291cmNlSWRcbiAgICAgICAgY29uc3QgcmVzb3VyY2VUeXBlID0gc3luYz8ucmVzb3VyY2VUeXBlID8/IHNjb3BlLnJlc291cmNlVHlwZVxuXG4gICAgICAgIGlmIChyZXNvdXJjZUlkID09PSB1bmRlZmluZWQgfHwgcmVzb3VyY2VJZCA9PT0gbnVsbCkgY29udGludWVcblxuICAgICAgICAvLyBQYXNzIHRoZSBjb21wbGV0ZSBicm9hZGNhc3QgZW50cnkgdGhyb3VnaCBzbyB0aGUgYXBwJ3NcbiAgICAgICAgLy8gY2hhbmdlRGVsaXZlcmFibGUgY2FuIGF1dGhvcml6ZSBieSBleGFjdC1yb3cgaWRlbnRpdHkgKGltbXV0YWJsZVxuICAgICAgICAvLyBzeW5jLXJvdyBpZCwgYWN0b3Itc3BlY2lmaWMgbWV0YWRhdGEpIOKAlCBjb25jdXJyZW50IHRhcmdldGVkIGFuZFxuICAgICAgICAvLyBzaGFyZWQgYnJvYWRjYXN0cyBmb3IgdGhlIHNhbWUgcmVzb3VyY2UgaWRlbnRpdHkgYXV0aG9yaXplXG4gICAgICAgIC8vIGluZGVwZW5kZW50bHkuIE9ubHkgcmVzb3VyY2VJZC9yZXNvdXJjZVR5cGUgYXJlIG5vcm1hbGl6ZWQgdG9cbiAgICAgICAgLy8gc3RyaW5ncywgb24gYSBjb3B5IHNvIHRoZSBwdWJsaXNoZWQgZW50cnkgaXMgbmV2ZXIgbXV0YXRlZC5cbiAgICAgICAgY29uc3Qgc3luY0VudHJ5ID0ge1xuICAgICAgICAgIC4uLnN5bmMsXG4gICAgICAgICAgcmVzb3VyY2VJZDogU3RyaW5nKHJlc291cmNlSWQpLFxuICAgICAgICAgIHJlc291cmNlVHlwZTogU3RyaW5nKHJlc291cmNlVHlwZSlcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChhd2FpdCByZXNvdXJjZS5jaGFuZ2VEZWxpdmVyYWJsZSh7cGFyYW1zOiB0aGlzLnBhcmFtcywgc2NvcGUsIHN5bmM6IHN5bmNFbnRyeX0pKSB7XG4gICAgICAgICAgZGVsaXZlcmFibGVTeW5jcy5wdXNoKHN5bmMpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KVxuXG4gICAgaWYgKGRlbGl2ZXJhYmxlU3luY3MubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbFxuICAgIGlmIChBcnJheS5pc0FycmF5KGVudmVsb3BlLnN5bmNzKSkgcmV0dXJuIHsuLi5lbnZlbG9wZSwgc3luY3M6IGRlbGl2ZXJhYmxlU3luY3N9XG5cbiAgICByZXR1cm4gZGVsaXZlcmFibGVTeW5jc1swXVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGF1dGhvcml6ZWQgc2NvcGUgZm9yIGRlYnVnIHNuYXBzaG90cy5cbiAgICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gRGVidWctc2FmZSBzdWJzY3JpcHRpb24gZGV0YWlscy5cbiAgICovXG4gIGRlYnVnU25hcHNob3QoKSB7XG4gICAgcmV0dXJuIHtzY29wZTogdGhpcy5fc2NvcGUsIHVzZXJTY29wZTogdGhpcy5faXNVc2VyU2NvcGUoKX1cbiAgfVxufVxuIl19
|
|
368
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy13ZWJzb2NrZXQtY2hhbm5lbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zeW5jL3N5bmMtd2Vic29ja2V0LWNoYW5uZWwuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8seUJBQXlCLE1BQU0scUNBQXFDLENBQUE7QUFDM0UsT0FBTyxFQUFDLHNCQUFzQixFQUFDLE1BQU0sd0JBQXdCLENBQUE7QUFDN0QsT0FBTyxFQUFDLG1CQUFtQixFQUFFLGFBQWEsRUFBQyxNQUFNLGtCQUFrQixDQUFBO0FBQ25FLE9BQU8sMEJBQTBCLE1BQU0scUNBQXFDLENBQUE7QUFFNUUsK0VBQStFO0FBQy9FLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQTtBQUU5QyxvRkFBb0Y7QUFDcEYsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUE7QUFFakM7Ozs7O0dBS0c7QUFDSCxTQUFTLFlBQVksQ0FBQyxNQUFNLEVBQUUsTUFBTTtJQUNsQyxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sRUFBRSxDQUFBO0lBRXRCLE9BQU87UUFDTCxDQUFDLEdBQUcsTUFBTSxJQUFJLENBQUMsRUFBRSxNQUFNLENBQUMsRUFBRTtRQUMxQixHQUFHLENBQUMsTUFBTSxDQUFDLGNBQWMsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxHQUFHLE1BQU0sZ0JBQWdCLENBQUMsRUFBRSxNQUFNLENBQUMsY0FBYyxFQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMvRixDQUFDLEdBQUcsTUFBTSxXQUFXLENBQUMsRUFBRSxNQUFNLENBQUMsU0FBUztLQUN6QyxDQUFBO0FBQ0gsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxNQUFNO0lBQzlCLE1BQU0sU0FBUyxHQUFHLE9BQU8sTUFBTSxDQUFDLGNBQWMsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE1BQU0s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LENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN4SSxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILHFCQUFxQixDQUFDLElBQUk7UUFDeEIsSUFBSSxDQUFDLElBQUksSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFBRSxPQUFNO1FBRXBFLE1BQU0sUUFBUSxHQUFHLDREQUE0RCxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDcEYsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUE7UUFFekUsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUMxQixNQUFNLFNBQVMsR0FBRyw0REFBNEQsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFBO1lBQ3RGLE1BQU0sTUFBTSxHQUFHLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxDQUFBO1lBRTdDLElBQUksTUFBTSxJQUFJLGFBQWEsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixDQUFDO2dCQUFFLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxNQUFNLENBQUE7UUFDNUYsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSCxhQUFhO1FBQ1gsT0FBTyxFQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsWUFBWSxFQUFFLEVBQUMsQ0FBQTtJQUM3RCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IFZlbG9jaW91c1dlYnNvY2tldENoYW5uZWwgZnJvbSBcIi4uL2h0dHAtc2VydmVyL3dlYnNvY2tldC1jaGFubmVsLmpzXCJcbmltcG9ydCB7VkVMT0NJT1VTX1NZTkNfQ0hBTk5FTH0gZnJvbSBcIi4vc3luYy1jaGFubmVsLW5hbWUuanNcIlxuaW1wb3J0IHtjdXJzb3JGcm9tU3luY0VudHJ5LCBpc0N1cnNvckFmdGVyfSBmcm9tIFwiLi9zeW5jLWN1cnNvci5qc1wiXG5pbXBvcnQgU3luY01vZGVsQ2hhbmdlRmVlZFNlcnZpY2UgZnJvbSBcIi4vc3luYy1tb2RlbC1jaGFuZ2UtZmVlZC1zZXJ2aWNlLmpzXCJcblxuLyoqIENvbmZpZ3VyYXRpb25zIHdob3NlIGZyYW1ld29yayBzeW5jIGNoYW5uZWwgaGFzIGFscmVhZHkgYmVlbiByZWdpc3RlcmVkLiAqL1xuY29uc3QgcmVnaXN0ZXJlZENvbmZpZ3VyYXRpb25zID0gbmV3IFdlYWtTZXQoKVxuXG4vKiogRGVmYXVsdCBjYXRjaC11cCByZXBsYXkgcGFnZSBzaXplIHdoZW4gdGhlIHN1YnNjcmliZXIgc2VuZHMgbm8gYGxpbWl0YCBwYXJhbS4gKi9cbmNvbnN0IERFRkFVTFRfUkVQTEFZX0xJTUlUID0gMTAwMFxuXG4vKipcbiAqIFNlcmlhbGl6ZXMgYSBjdXJzb3IgaW50byB0aGUgY2hhbmdlLWZlZWQgcmVxdWVzdCBwYXJhbXMgdW5kZXIgdGhlIGdpdmVuIHByZWZpeC5cbiAqIEBwYXJhbSB7XCJhZnRlclwiIHwgXCJ1cFRvXCJ9IHByZWZpeCAtIFJlcXVlc3QgZmllbGQgcHJlZml4LlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ3Vyc29yfSBjdXJzb3IgLSBDdXJzb3IgdG8gc2VyaWFsaXplLlxuICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlciB8IG51bGw+fSBTZXJpYWxpemVkIGN1cnNvciBwYXJhbXMuXG4gKi9cbmZ1bmN0aW9uIGN1cnNvclBhcmFtcyhwcmVmaXgsIGN1cnNvcikge1xuICBpZiAoIWN1cnNvcikgcmV0dXJuIHt9XG5cbiAgcmV0dXJuIHtcbiAgICBbYCR7cHJlZml4fUlkYF06IGN1cnNvci5pZCxcbiAgICAuLi4oY3Vyc29yLnNlcnZlclNlcXVlbmNlICE9PSBudWxsID8ge1tgJHtwcmVmaXh9U2VydmVyU2VxdWVuY2VgXTogY3Vyc29yLnNlcnZlclNlcXVlbmNlfSA6IHt9KSxcbiAgICBbYCR7cHJlZml4fVVwZGF0ZWRBdGBdOiBjdXJzb3IudXBkYXRlZEF0XG4gIH1cbn1cblxuLyoqXG4gKiBSZWFkcyB0aGUgYWZ0ZXItY3Vyc29yIHRoZSBzdWJzY3JpYmVyIGRlY2xhcmVkIGluIGl0cyBzdWJzY3JpYmUgcGFyYW1zLCBvciBudWxsXG4gKiB3aGVuIGl0IHNlbnQgbm9uZSAoYSBmcmVzaCBzdWJzY3JpcHRpb24gcmVwbGF5cyB0aGUgd2hvbGUgc2NvcGUpLlxuICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59IHBhcmFtcyAtIFN1YnNjcmliZSBwYXJhbXMuXG4gKiBAcmV0dXJucyB7aW1wb3J0KFwiLi9zeW5jLWFwaS1jbGllbnQtdHlwZXMuanNcIikuU3luY0N1cnNvcn0gRGVjbGFyZWQgYWZ0ZXItY3Vyc29yLCBvciBudWxsLlxuICovXG5mdW5jdGlvbiBjdXJzb3JGcm9tUGFyYW1zKHBhcmFtcykge1xuICBjb25zdCB1cGRhdGVkQXQgPSB0eXBlb2YgcGFyYW1zLmFmdGVyVXBkYXRlZEF0ID09PSBcInN0cmluZ1wiID8gcGFyYW1zLmFmdGVyVXBkYXRlZEF0IDogbnVsbFxuXG4gIGlmICghdXBkYXRlZEF0KSByZXR1cm4gbnVsbFxuXG4gIGNvbnN0IHNlcnZlclNlcXVlbmNlID0gcGFyYW1zLmFmdGVyU2VydmVyU2VxdWVuY2UgPT09IG51bGwgfHwgcGFyYW1zLmFmdGVyU2VydmVyU2VxdWVuY2UgPT09IHVuZGVmaW5lZFxuICAgID8gbnVsbFxuICAgIDogTnVtYmVyKHBhcmFtcy5hZnRlclNlcnZlclNlcXVlbmNlKVxuXG4gIHJldHVybiB7XG4gICAgaWQ6IHBhcmFtcy5hZnRlcklkID09PSBudWxsIHx8IHBhcmFtcy5hZnRlcklkID09PSB1bmRlZmluZWQgPyBudWxsIDogU3RyaW5nKHBhcmFtcy5hZnRlcklkKSxcbiAgICBzZXJ2ZXJTZXF1ZW5jZSxcbiAgICB1cGRhdGVkQXRcbiAgfVxufVxuXG4vKipcbiAqIEZyYW1ld29yay1vd25lZCB3ZWJzb2NrZXQgY2hhbm5lbCBmb3Igc3luY2VkIHJlc291cmNlc1xuICogKHtAbGluayBWRUxPQ0lPVVNfU1lOQ19DSEFOTkVMfSkuXG4gKlxuICogU3Vic2NyaWJlIHBhcmFtcyBtaXJyb3IgYSBkZWNsYXJlZCBwdWxsIHNjb3BlIOKAlCBge3Jlc291cmNlVHlwZSwgY29uZGl0aW9uc31gXG4gKiBwbHVzIHRoZSBjbGllbnQtaW5qZWN0ZWQgYGF1dGhlbnRpY2F0aW9uVG9rZW5gIOKAlCBhbmQgc3Vic2NyaWJlLXRpbWVcbiAqIGF1dGhvcml6YXRpb24gZGVsZWdhdGVzIHRvIHRoZSBhcHAgc3luYyByZXNvdXJjZSdzIGV4aXN0aW5nXG4gKiBgYXV0aG9yaXplQ2hhbmdlcyh7cGFyYW1zLCBzY29wZX0pYCAodGhlIGBzeW5jLmFwaS5yZXNvdXJjZUNsYXNzYCksIHNvIGFwcHNcbiAqIGhvb2sgaW4gdGhyb3VnaCB0aGUgYXV0aG9yaXphdGlvbiB0aGV5IGFscmVhZHkgZGVjbGFyZWQgaW5zdGVhZCBvZiB3cml0aW5nXG4gKiB0aGVpciBvd24gY2hhbm5lbCBjbGFzc2VzLiBCcm9hZGNhc3Qgcm91dGluZyBtYXRjaGVzIHRoZSBwdWJsaXNoZXInc1xuICogc2NvcGluZyBwYXJhbXMgYWdhaW5zdCB0aGUgc3Vic2NyaXB0aW9uJ3Mgc2NvcGUgY29uZGl0aW9ucy5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU3luY1dlYnNvY2tldENoYW5uZWwgZXh0ZW5kcyBWZWxvY2lvdXNXZWJzb2NrZXRDaGFubmVsIHtcbiAgLyoqXG4gICAqIFNjb3BlIHRoZSBzdWJzY3JpcHRpb24gd2FzIGF1dGhvcml6ZWQgZm9yLCBzZXQgYnkge0BsaW5rIFN5bmNXZWJzb2NrZXRDaGFubmVsI2NhblN1YnNjcmliZX0uXG4gICAqIEB0eXBlIHtpbXBvcnQoXCIuL3N5bmMtcmVzb3VyY2UtYmFzZS5qc1wiKS5TZXJpYWxpemVkQ2hhbmdlc1Njb3BlIHwgbnVsbH1cbiAgICovXG4gIF9zY29wZSA9IG51bGxcblxuICAvKipcbiAgICogQ3Vyc29yIG9mIHRoZSBsYXN0IHN5bmMgZW50cnkgZGVsaXZlcmVkIHRvIHRoaXMgc3Vic2NyaXB0aW9uLCBhZHZhbmNlZCBieVxuICAgKiBsaXZlIGJyb2FkY2FzdHMgYW5kIGNhdGNoLXVwIHJlcGxheSBzbyBhIHJlc3VtZSByZXBsYXlzIG9ubHkgdGhlIHJvd3MgbWlzc2VkXG4gICAqIHdoaWxlIHRoZSBzb2NrZXQgd2FzIGRvd24uXG4gICAqIEB0eXBlIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ3Vyc29yfVxuICAgKi9cbiAgX2RlbGl2ZXJlZEN1cnNvciA9IG51bGxcblxuICAvKipcbiAgICogUmVnaXN0ZXJzIHRoZSBmcmFtZXdvcmsgc3luYyBjaGFubmVsIG9uIGEgY29uZmlndXJhdGlvbiBkZWNsYXJpbmcgYVxuICAgKiBgc3luYy5hcGlgIGJsb2NrIChndWFyZGVkIHNvIHJlcGVhdGVkIHNlcnZlciBib290cyB3aXRoIHRoZSBzYW1lXG4gICAqIGNvbmZpZ3VyYXRpb24gcmVnaXN0ZXIgaXQgb25seSBvbmNlKS4gTm8tb3Agd2l0aG91dCBgc3luYy5hcGlgIOKAlCB0aGVcbiAgICogY2hhbm5lbCBhdXRob3JpemVzIHRocm91Z2ggdGhlIGFwcCdzIHN5bmMgcmVzb3VyY2UgY2xhc3MuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi5qc1wiKS5kZWZhdWx0fSBjb25maWd1cmF0aW9uIC0gQ29uZmlndXJhdGlvbiBpbnN0YW5jZS5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBzdGF0aWMgcmVnaXN0ZXJGcm9tQ29uZmlndXJhdGlvbihjb25maWd1cmF0aW9uKSB7XG4gICAgaWYgKCFjb25maWd1cmF0aW9uLmdldFN5bmNDb25maWd1cmF0aW9uKCkuYXBpIHx8IHJlZ2lzdGVyZWRDb25maWd1cmF0aW9ucy5oYXMoY29uZmlndXJhdGlvbikpIHJldHVyblxuXG4gICAgcmVnaXN0ZXJlZENvbmZpZ3VyYXRpb25zLmFkZChjb25maWd1cmF0aW9uKVxuICAgIGNvbmZpZ3VyYXRpb24ucmVnaXN0ZXJXZWJzb2NrZXRDaGFubmVsKFZFTE9DSU9VU19TWU5DX0NIQU5ORUwsIHRoaXMpXG4gIH1cblxuICAvKipcbiAgICogQXV0aG9yaXplcyB0aGUgc3Vic2NyaXB0aW9uIHRocm91Z2ggdGhlIGFwcCBzeW5jIHJlc291cmNlOiB0aGUgc3Vic2NyaWJlXG4gICAqIHBhcmFtcyBhcmUgcGFyc2VkIGludG8gdGhlIHNhbWUgc2VyaWFsaXplZCBzY29wZSB0aGUgY2hhbmdlcyBlbmRwb2ludFxuICAgKiBjb25zdW1lcyBhbmQgcGFzc2VkIHRvIHRoZSByZXNvdXJjZSdzIGBhdXRob3JpemVDaGFuZ2VzKHtwYXJhbXMsIHNjb3BlfSlgLlxuICAgKiBEZW5pYWxzIGFuZCBtYWxmb3JtZWQgc2NvcGVzIHRocm93LCByZWplY3RpbmcgdGhlIHN1YnNjcmlwdGlvbi5cbiAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IFdoZXRoZXIgdGhlIHN1YnNjcmlwdGlvbiBpcyBhbGxvd2VkLlxuICAgKi9cbiAgYXN5bmMgY2FuU3Vic2NyaWJlKCkge1xuICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgdGhpcy5idWlsZFN5bmNSZXNvdXJjZSgpXG4gICAgY29uc3Qgc2NvcGUgPSByZXNvdXJjZS5jaGFuZ2VzU2NvcGUoe3Njb3BlOiB7Y29uZGl0aW9uczogdGhpcy5wYXJhbXMuY29uZGl0aW9ucywgcmVzb3VyY2VUeXBlOiB0aGlzLnBhcmFtcy5yZXNvdXJjZVR5cGUsIHJlc291cmNlVHlwZXM6IHRoaXMucGFyYW1zLnJlc291cmNlVHlwZXN9fSlcblxuICAgIGF3YWl0IHJlc291cmNlLmF1dGhvcml6ZUNoYW5nZXMoe3BhcmFtczogdGhpcy5wYXJhbXMsIHNjb3BlfSlcblxuICAgIHRoaXMuX3Njb3BlID0gc2NvcGVcblxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIHRoZSBhcHAgc3luYyByZXNvdXJjZSBhdXRob3JpemluZyB0aGlzIHN1YnNjcmlwdGlvbiwgbWlycm9yaW5nIHRoZVxuICAgKiBzeW5jIHRyYW5zcG9ydCBjb250cm9sbGVyJ3MgcmVzb3VyY2UgY29uc3RydWN0aW9uIHdpdGggdGhlIGFiaWxpdHlcbiAgICogcmVzb2x2ZWQgZnJvbSB0aGUgc3Vic2NyaWJlIHBhcmFtcyAod2hpY2ggY2FycnkgdGhlIGNsaWVudC1pbmplY3RlZFxuICAgKiBhdXRoZW50aWNhdGlvblRva2VuKS5cbiAgICogQHJldHVybnMge1Byb21pc2U8aW1wb3J0KFwiLi9zeW5jLXJlc291cmNlLWJhc2UuanNcIikuZGVmYXVsdD59IEFwcCBzeW5jIHJlc291cmNlIGluc3RhbmNlLlxuICAgKi9cbiAgYXN5bmMgYnVpbGRTeW5jUmVzb3VyY2UoKSB7XG4gICAgY29uc3QgY29uZmlndXJhdGlvbiA9IHRoaXMuc2Vzc2lvbi5jb25maWd1cmF0aW9uXG4gICAgY29uc3QgYXBpID0gY29uZmlndXJhdGlvbi5nZXRTeW5jQ29uZmlndXJhdGlvbigpLmFwaVxuXG4gICAgaWYgKCFhcGkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlICR7VkVMT0NJT1VTX1NZTkNfQ0hBTk5FTH0gY2hhbm5lbCByZXF1aXJlcyBhIHN5bmMuYXBpIGNvbmZpZ3VyYXRpb24gYmxvY2sgd2l0aCB0aGUgYXBwJ3Mgc3luYyByZXNvdXJjZSBjbGFzc2ApXG4gICAgfVxuXG4gICAgLy8gTmFycm93cyB0aGUgY29uZmlndXJlZCByZXNvdXJjZSBjbGFzcyB0byB0aGUgc3luYyByZXNvdXJjZSBjb250cmFjdFxuICAgIC8vIChjaGFuZ2VzU2NvcGUvYXV0aG9yaXplQ2hhbmdlcykgdGhlIHN5bmMuYXBpIHZhbGlkYXRpb24gcmVxdWlyZXMuXG4gICAgY29uc3QgUmVzb3VyY2VDbGFzcyA9IC8qKiBAdHlwZSB7dHlwZW9mIGltcG9ydChcIi4vc3luYy1yZXNvdXJjZS1iYXNlLmpzXCIpLmRlZmF1bHR9ICovIChhcGkucmVzb3VyY2VDbGFzcylcbiAgICAvLyBOYXJyb3dzIHRoZSB3ZWJzb2NrZXQgc3Vic2NyaWJlIHBhcmFtcyB0byB0aGUgcmVzb3VyY2UgcGFyYW1zIGNvbnRyYWN0LlxuICAgIGNvbnN0IHBhcmFtcyA9IC8qKiBAdHlwZSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5WZWxvY2lvdXNQYXJhbXN9ICovICgvKiogQHR5cGUge3Vua25vd259ICovICh0aGlzLnBhcmFtcykpXG4gICAgY29uc3QgcmVxdWVzdCA9IHRoaXMuc2Vzc2lvbi51cGdyYWRlUmVxdWVzdFxuICAgIGNvbnN0IGFiaWxpdHkgPSBhd2FpdCBjb25maWd1cmF0aW9uLnJlc29sdmVBYmlsaXR5KHtwYXJhbXMsIHJlcXVlc3R9KVxuXG4gICAgcmV0dXJuIG5ldyBSZXNvdXJjZUNsYXNzKHtcbiAgICAgIGFiaWxpdHksXG4gICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgY29udGV4dDoge1xuICAgICAgICAuLi4oYWJpbGl0eT8uZ2V0Q29udGV4dCgpIHx8IHt9KSxcbiAgICAgICAgcGFyYW1zLFxuICAgICAgICByZXF1ZXN0XG4gICAgICB9LFxuICAgICAgbG9jYWxzOiBhYmlsaXR5Py5nZXRMb2NhbHMoKSB8fCB7fSxcbiAgICAgIG1vZGVsQ2xhc3M6IFJlc291cmNlQ2xhc3MuTW9kZWxDbGFzcyxcbiAgICAgIG1vZGVsTmFtZTogUmVzb3VyY2VDbGFzcy5Nb2RlbENsYXNzPy5uYW1lLFxuICAgICAgcGFyYW1zLFxuICAgICAgcmVzb3VyY2VDb25maWd1cmF0aW9uOiAvKiogQHR5cGUge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFJlc291cmNlQ29uZmlndXJhdGlvbn0gKi8gKHtcbiAgICAgICAgYXR0cmlidXRlczogUmVzb3VyY2VDbGFzcy5hdHRyaWJ1dGVzIHx8IHt9LFxuICAgICAgICBzeW5jOiB7ZW5hYmxlZDogdHJ1ZX1cbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSb3V0ZXMgYSBwdWJsaXNoZXIgYnJvYWRjYXN0IHRvIHRoaXMgc3Vic2NyaXB0aW9uIHdoZW4gdGhlIHB1Ymxpc2hlZFxuICAgKiByZXNvdXJjZSB0eXBlIGVxdWFscyB0aGUgdHlwZSB0aGUgc3Vic2NyaXB0aW9uIHdhcyBhdXRob3JpemVkIGZvciBhbmQgdGhlXG4gICAqIHNjb3BpbmcgcGFyYW1zIHNhdGlzZnkgZXZlcnkgc2NvcGUgY29uZGl0aW9uOiBlYWNoIGNvbmRpdGlvbiBrZXkgbXVzdCBiZVxuICAgKiBwcmVzZW50IGluIHRoZSBicm9hZGNhc3QgcGFyYW1zIGFuZCBtYXRjaCBieSBzdHJpbmcgY29tcGFyaXNvbiAoYXJyYXlcbiAgICogY29uZGl0aW9ucyBtYXRjaCBieSBtZW1iZXJzaGlwKS4gQnJvYWRjYXN0cyB3aXRob3V0IGEgcmVzb3VyY2UgdHlwZSBhbmRcbiAgICogY29uZGl0aW9ucyB0aGUgcHVibGlzaGVyJ3Mgc2NvcGluZyBwYXJhbXMgZG8gbm90IGNhcnJ5IG5ldmVyIG1hdGNoLCBzbyBhXG4gICAqIHN1YnNjcmlwdGlvbiBjYW5ub3QgcmVjZWl2ZSBjaGFuZ2VzIG91dHNpZGUgaXRzIGF1dGhvcml6ZWQgc2NvcGUuXG4gICAqXG4gICAqIFRoZSBhbGwtdHlwZXMgKHVzZXIpIHNjb3BlIGRlY2xhcmVzIG5vIHNpbmdsZSByZXNvdXJjZSB0eXBlIC0gaXQgc3Vic2NyaWJlc1xuICAgKiBvbmNlIGZvciBldmVyeSB0eXBlIGl0IGNhbiBhcHBseSwgbGlzdGVkIGluIGBzY29wZS5yZXNvdXJjZVR5cGVzYCAtIHNvIGl0XG4gICAqIG1hdGNoZXMgYSBicm9hZGNhc3Qgb2YgYW55IG9mIHRob3NlIHR5cGVzLiBUeXBlcyBvdXRzaWRlIHRoYXQgbGlzdCBhcmVcbiAgICogZHJvcHBlZCBoZXJlLCBieSB0aGlzIGNoZWFwIGNoZWNrLCBhbmQgbmV2ZXIgcmVhY2ggdGhlIHBlci1kZWxpdmVyeSBhY2Nlc3NcbiAgICogcmUtY2hlY2sgaW4ge0BsaW5rIFN5bmNXZWJzb2NrZXRDaGFubmVsI191c2VyU2NvcGVEZWxpdmVyYWJsZUJvZHl9OiB0aGF0XG4gICAqIHJlLWNoZWNrIGNoZWNrcyBvdXQgYSBkYXRhYmFzZSBjb25uZWN0aW9uIGFuZCBydW5zIGEgcXVlcnkgcGVyIG1hdGNoZWRcbiAgICogYnJvYWRjYXN0LCBzbyBtYXRjaGluZyBldmVyeSB0eXBlIHdvdWxkIHB1dCBEQiB3b3JrIG9uIGV2ZXJ5IGJyb2FkY2FzdCBmb3JcbiAgICogZXZlcnkgc3Vic2NyaWJlZCBkZXZpY2UuIEEgYnJvYWRjYXN0IGNhcnJ5aW5nIG5vIHJlc291cmNlIHR5cGUgYXQgYWxsIHN0aWxsXG4gICAqIG5ldmVyIG1hdGNoZXMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vaHR0cC1zZXJ2ZXIvd2Vic29ja2V0LWNoYW5uZWwuanNcIikuV2Vic29ja2V0SnNvblZhbHVlfSBicm9hZGNhc3RQYXJhbXMgLSBQdWJsaXNoZXIgc2NvcGluZyBwYXJhbXMgKHRoZSBwdWJsaXNoZWQgcmVzb3VyY2VUeXBlIHBsdXMgdGhlIGNoYW5nZSdzIHNjb3BlLXBhcnRpdGlvbiB2YWx1ZXMpLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gV2hldGhlciB0aGUgYnJvYWRjYXN0IGJlbG9uZ3MgdG8gdGhpcyBzdWJzY3JpcHRpb24ncyBzY29wZS5cbiAgICovXG4gIG1hdGNoZXMoYnJvYWRjYXN0UGFyYW1zKSB7XG4gICAgY29uc3Qgc2NvcGUgPSB0aGlzLl9zY29wZVxuXG4gICAgaWYgKCFzY29wZSkgcmV0dXJuIGZhbHNlXG5cbiAgICBjb25zdCBzY29waW5nUGFyYW1zID0gYnJvYWRjYXN0UGFyYW1zICYmIHR5cGVvZiBicm9hZGNhc3RQYXJhbXMgPT09IFwib2JqZWN0XCIgJiYgIUFycmF5LmlzQXJyYXkoYnJvYWRjYXN0UGFyYW1zKVxuICAgICAgPyAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gKi8gKGJyb2FkY2FzdFBhcmFtcylcbiAgICAgIDoge31cblxuICAgIGlmICghT2JqZWN0Lmhhc093bihzY29waW5nUGFyYW1zLCBcInJlc291cmNlVHlwZVwiKSkgcmV0dXJuIGZhbHNlXG5cbiAgICBpZiAoc2NvcGUucmVzb3VyY2VUeXBlID09PSBudWxsKSB7XG4gICAgICBpZiAoc2NvcGUucmVzb3VyY2VUeXBlcyAmJiAhc2NvcGUucmVzb3VyY2VUeXBlcy5zb21lKChyZXNvdXJjZVR5cGUpID0+IHJlc291cmNlVHlwZSA9PT0gU3RyaW5nKHNjb3BpbmdQYXJhbXMucmVzb3VyY2VUeXBlKSkpIHJldHVybiBmYWxzZVxuICAgIH0gZWxzZSBpZiAoU3RyaW5nKHNjb3BpbmdQYXJhbXMucmVzb3VyY2VUeXBlKSAhPT0gU3RyaW5nKHNjb3BlLnJlc291cmNlVHlwZSkpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgW2NvbmRpdGlvbk5hbWUsIGNvbmRpdGlvblZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhzY29wZS5jb25kaXRpb25zKSkge1xuICAgICAgaWYgKCFPYmplY3QuaGFzT3duKHNjb3BpbmdQYXJhbXMsIGNvbmRpdGlvbk5hbWUpKSByZXR1cm4gZmFsc2VcblxuICAgICAgY29uc3Qgc2NvcGluZ1ZhbHVlID0gc2NvcGluZ1BhcmFtc1tjb25kaXRpb25OYW1lXVxuXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShjb25kaXRpb25WYWx1ZSkpIHtcbiAgICAgICAgaWYgKCFjb25kaXRpb25WYWx1ZS5zb21lKCh2YWx1ZSkgPT4gU3RyaW5nKHZhbHVlKSA9PT0gU3RyaW5nKHNjb3BpbmdWYWx1ZSkpKSByZXR1cm4gZmFsc2VcbiAgICAgIH0gZWxzZSBpZiAoU3RyaW5nKGNvbmRpdGlvblZhbHVlKSAhPT0gU3RyaW5nKHNjb3BpbmdWYWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWVcbiAgfVxuXG4gIC8qKlxuICAgKiBEZWxpdmVycyBhIG1hdGNoZWQgYnJvYWRjYXN0LiBTY29wZWQgc3Vic2NyaXB0aW9ucyAod2l0aCBleHBsaWNpdFxuICAgKiBjb25kaXRpb25zKSBhbHJlYWR5IHJvdXRlZCB0aHJvdWdoIHtAbGluayBTeW5jV2Vic29ja2V0Q2hhbm5lbCNtYXRjaGVzfSwgc29cbiAgICogdGhlIGNoYW5nZSBpcyBpbiBzY29wZSBhbmQgZGVsaXZlcnMgdW5jaGFuZ2VkLiBVc2VyLXNjb3BlIHN1YnNjcmlwdGlvbnNcbiAgICogKGVtcHR5IGNvbmRpdGlvbnMsIFwiZXZlcnl0aGluZyBteSBhYmlsaXR5IGNhbiBzZWVcIikgbWF0Y2ggZXZlcnkgYnJvYWRjYXN0XG4gICAqIG9mIHRoZSByZXNvdXJjZSB0eXBlLCBzbyBlYWNoIHB1Ymxpc2hlZCBjaGFuZ2UgaXMgcmUtY2hlY2tlZCBhZ2FpbnN0IHRoZVxuICAgKiBzdWJzY3JpYmVyJ3MgYWJpbGl0eSBhdCBmYW4tb3V0IHRocm91Z2ggdGhlIGFwcCBzeW5jIHJlc291cmNlJ3NcbiAgICogYGNoYW5nZURlbGl2ZXJhYmxlYDsgb25seSBhY2Nlc3NpYmxlIGNoYW5nZXMgYXJlIGRlbGl2ZXJlZCwgYW5kIGEgYnJvYWRjYXN0XG4gICAqIHdpdGggbm8gYWNjZXNzaWJsZSBjaGFuZ2UgaXMgZHJvcHBlZC5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9odHRwLXNlcnZlci93ZWJzb2NrZXQtY2hhbm5lbC5qc1wiKS5XZWJzb2NrZXRKc29uVmFsdWV9IGJvZHkgLSBCcm9hZGNhc3QgYm9keSAoc3luYyBlbnZlbG9wZSkuXG4gICAqIEBwYXJhbSB7e2V2ZW50SWQ/OiBzdHJpbmd9fSBbbWV0YV0gLSBPcHRpb25hbCBldmVudCBtZXRhZGF0YS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBkZWxpdmVyQnJvYWRjYXN0KGJvZHksIG1ldGEpIHtcbiAgICBpZiAoIXRoaXMuX2lzVXNlclNjb3BlKCkpIHtcbiAgICAgIHRoaXMuc2VuZE1lc3NhZ2UoYm9keSwgbWV0YSlcbiAgICAgIHRoaXMuX3RyYWNrRGVsaXZlcmVkQ3Vyc29yKGJvZHkpXG5cbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IGRlbGl2ZXJhYmxlQm9keSA9IGF3YWl0IHRoaXMuX3VzZXJTY29wZURlbGl2ZXJhYmxlQm9keShib2R5KVxuXG4gICAgaWYgKGRlbGl2ZXJhYmxlQm9keSAhPT0gbnVsbCkge1xuICAgICAgdGhpcy5zZW5kTWVzc2FnZShkZWxpdmVyYWJsZUJvZHksIG1ldGEpXG4gICAgICB0aGlzLl90cmFja0RlbGl2ZXJlZEN1cnNvcihkZWxpdmVyYWJsZUJvZHkpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhpcyBzdWJzY3JpcHRpb24gaXMgYSB1c2VyIHNjb3BlOiBhdXRob3JpemVkIHdpdGggZW1wdHkgY29uZGl0aW9uc1xuICAgKiAoXCJldmVyeXRoaW5nIG15IGFiaWxpdHkgY2FuIHNlZVwiKS5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IFdoZXRoZXIgdGhlIHN1YnNjcmlwdGlvbiBpcyBhIHVzZXIgc2NvcGUuXG4gICAqL1xuICBfaXNVc2VyU2NvcGUoKSB7XG4gICAgcmV0dXJuIEJvb2xlYW4odGhpcy5fc2NvcGUpICYmIE9iamVjdC5rZXlzKC8qKiBAdHlwZSB7aW1wb3J0KFwiLi9zeW5jLXJlc291cmNlLWJhc2UuanNcIikuU2VyaWFsaXplZENoYW5nZXNTY29wZX0gKi8gKHRoaXMuX3Njb3BlKS5jb25kaXRpb25zKS5sZW5ndGggPT09IDBcbiAgfVxuXG4gIC8qKlxuICAgKiBGaWx0ZXJzIGEgdXNlci1zY29wZSBicm9hZGNhc3QgdG8gdGhlIHN5bmMgZW50cmllcyB0aGUgc3Vic2NyaWJlcidzIGFiaWxpdHlcbiAgICogY2FuIGFjY2VzcywgcmUtY2hlY2tpbmcgZWFjaCB0aHJvdWdoIHRoZSBhcHAgc3luYyByZXNvdXJjZSdzXG4gICAqIGBjaGFuZ2VEZWxpdmVyYWJsZWAuIFJldHVybnMgdGhlIGJyb2FkY2FzdCBuYXJyb3dlZCB0byBhY2Nlc3NpYmxlIGVudHJpZXMsXG4gICAqIG9yIG51bGwgd2hlbiBub25lIGFyZSBhY2Nlc3NpYmxlLiBOb24tZW52ZWxvcGUgYm9kaWVzIGFuZCBlbnRyaWVzIHdpdGhvdXQgYVxuICAgKiByZXNvdXJjZSBpZCBhcmUgZHJvcHBlZCAoZmFpbCBjbG9zZWQpLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2h0dHAtc2VydmVyL3dlYnNvY2tldC1jaGFubmVsLmpzXCIpLldlYnNvY2tldEpzb25WYWx1ZX0gYm9keSAtIEJyb2FkY2FzdCBib2R5LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxpbXBvcnQoXCIuLi9odHRwLXNlcnZlci93ZWJzb2NrZXQtY2hhbm5lbC5qc1wiKS5XZWJzb2NrZXRKc29uVmFsdWUgfCBudWxsPn0gRGVsaXZlcmFibGUgYm9keSwgb3IgbnVsbC5cbiAgICovXG4gIGFzeW5jIF91c2VyU2NvcGVEZWxpdmVyYWJsZUJvZHkoYm9keSkge1xuICAgIGlmICghYm9keSB8fCB0eXBlb2YgYm9keSAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGJvZHkpKSByZXR1cm4gbnVsbFxuXG4gICAgY29uc3QgZW52ZWxvcGUgPSAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gKi8gKGJvZHkpXG4gICAgY29uc3Qgc2NvcGUgPSAvKiogQHR5cGUge2ltcG9ydChcIi4vc3luYy1yZXNvdXJjZS1iYXNlLmpzXCIpLlNlcmlhbGl6ZWRDaGFuZ2VzU2NvcGV9ICovICh0aGlzLl9zY29wZSlcbiAgICAvKiogQHR5cGUge0FycmF5PFJlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pj59ICovXG4gICAgY29uc3Qgc3luY3MgPSBBcnJheS5pc0FycmF5KGVudmVsb3BlLnN5bmNzKSA/IGVudmVsb3BlLnN5bmNzIDogW2VudmVsb3BlXVxuICAgIGNvbnN0IGNvbmZpZ3VyYXRpb24gPSB0aGlzLnNlc3Npb24uY29uZmlndXJhdGlvblxuICAgIC8qKiBAdHlwZSB7QXJyYXk8UmVjb3JkPHN0cmluZywgUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+Pn0gKi9cbiAgICBjb25zdCBkZWxpdmVyYWJsZVN5bmNzID0gW11cblxuICAgIC8vIEJyb2FkY2FzdCBmYW4tb3V0IHJ1bnMgdGhyb3VnaCBgd2l0aG91dEN1cnJlbnRDb25uZWN0aW9uQ29udGV4dHNgIChzZWVcbiAgICAvLyBDb25maWd1cmF0aW9uI19icm9hZGNhc3RUb0NoYW5uZWxMb2NhbCksIHNvIHRoZXJlIGlzIG5vIGFtYmllbnQgZGF0YWJhc2VcbiAgICAvLyBjb25uZWN0aW9uIGhlcmUuIFJlc29sdmUgdGhlIHJlc291cmNlJ3MgYWJpbGl0eSBhbmQgcnVuIHRoZSBwZXItZGVsaXZlcnlcbiAgICAvLyBhY2Nlc3MgcXVlcnkgaW5zaWRlIGEgY2hlY2tlZC1vdXQgY29ubmVjdGlvbiBjb250ZXh0LCBtaXJyb3JpbmcgaG93IG90aGVyXG4gICAgLy8gYnJvYWRjYXN0LXRpbWUgREIgd29yayAodGhlIGZyb250ZW5kLW1vZGVsIGNoYW5uZWwpIG9idGFpbnMgY29ubmVjdGlvbnMuXG4gICAgYXdhaXQgY29uZmlndXJhdGlvbi5lbnN1cmVDb25uZWN0aW9ucyh7bmFtZTogYCR7VkVMT0NJT1VTX1NZTkNfQ0hBTk5FTH0gdXNlci1zY29wZSBkZWxpdmVyeSBhY2Nlc3MgY2hlY2tgfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCB0aGlzLmJ1aWxkU3luY1Jlc291cmNlKClcblxuICAgICAgZm9yIChjb25zdCBzeW5jIG9mIHN5bmNzKSB7XG4gICAgICAgIGNvbnN0IHJlc291cmNlSWQgPSBzeW5jPy5yZXNvdXJjZUlkXG4gICAgICAgIGNvbnN0IHJlc291cmNlVHlwZSA9IHN5bmM/LnJlc291cmNlVHlwZSA/PyBzY29wZS5yZXNvdXJjZVR5cGVcblxuICAgICAgICBpZiAocmVzb3VyY2VJZCA9PT0gdW5kZWZpbmVkIHx8IHJlc291cmNlSWQgPT09IG51bGwpIGNvbnRpbnVlXG5cbiAgICAgICAgLy8gUGFzcyB0aGUgY29tcGxldGUgYnJvYWRjYXN0IGVudHJ5IHRocm91Z2ggc28gdGhlIGFwcCdzXG4gICAgICAgIC8vIGNoYW5nZURlbGl2ZXJhYmxlIGNhbiBhdXRob3JpemUgYnkgZXhhY3Qtcm93IGlkZW50aXR5IChpbW11dGFibGVcbiAgICAgICAgLy8gc3luYy1yb3cgaWQsIGFjdG9yLXNwZWNpZmljIG1ldGFkYXRhKSDigJQgY29uY3VycmVudCB0YXJnZXRlZCBhbmRcbiAgICAgICAgLy8gc2hhcmVkIGJyb2FkY2FzdHMgZm9yIHRoZSBzYW1lIHJlc291cmNlIGlkZW50aXR5IGF1dGhvcml6ZVxuICAgICAgICAvLyBpbmRlcGVuZGVudGx5LiBPbmx5IHJlc291cmNlSWQvcmVzb3VyY2VUeXBlIGFyZSBub3JtYWxpemVkIHRvXG4gICAgICAgIC8vIHN0cmluZ3MsIG9uIGEgY29weSBzbyB0aGUgcHVibGlzaGVkIGVudHJ5IGlzIG5ldmVyIG11dGF0ZWQuXG4gICAgICAgIGNvbnN0IHN5bmNFbnRyeSA9IHtcbiAgICAgICAgICAuLi5zeW5jLFxuICAgICAgICAgIHJlc291cmNlSWQ6IFN0cmluZyhyZXNvdXJjZUlkKSxcbiAgICAgICAgICByZXNvdXJjZVR5cGU6IFN0cmluZyhyZXNvdXJjZVR5cGUpXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYXdhaXQgcmVzb3VyY2UuY2hhbmdlRGVsaXZlcmFibGUoe3BhcmFtczogdGhpcy5wYXJhbXMsIHNjb3BlLCBzeW5jOiBzeW5jRW50cnl9KSkge1xuICAgICAgICAgIGRlbGl2ZXJhYmxlU3luY3MucHVzaChzeW5jKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSlcblxuICAgIGlmIChkZWxpdmVyYWJsZVN5bmNzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG51bGxcbiAgICBpZiAoQXJyYXkuaXNBcnJheShlbnZlbG9wZS5zeW5jcykpIHJldHVybiB7Li4uZW52ZWxvcGUsIHN5bmNzOiBkZWxpdmVyYWJsZVN5bmNzfVxuXG4gICAgcmV0dXJuIGRlbGl2ZXJhYmxlU3luY3NbMF1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYXlzIHRoZSBjYXRjaC11cCBiYWNrbG9nIHRvIHRoaXMgc3Vic2NyaWJlciBvbmNlIGl0cyBzdWJzY3JpcHRpb24gaXNcbiAgICogYXV0aG9yaXplZDogcGFnZXMgdGhlIHNjb3BlZCBjaGFuZ2UgZmVlZCBmcm9tIHRoZSBjdXJzb3IgdGhlIGNsaWVudCBkZWNsYXJlZFxuICAgKiBpbiBpdHMgc3Vic2NyaWJlIHBhcmFtcyBhbmQgc2VuZHMgZWFjaCBwYWdlIGFzIGEgc3RhbmRhcmQgc3luYyBlbnZlbG9wZSwgdGhlblxuICAgKiBsaXZlIGJyb2FkY2FzdHMgY29udGludWUgZnJvbSB0aGVyZS4gQSBmcmVzaCBzdWJzY3JpcHRpb24gd2l0aCBubyBjdXJzb3JcbiAgICogcmVwbGF5cyB0aGUgd2hvbGUgYXV0aG9yaXplZCBzY29wZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBzdWJzY3JpYmVkKCkge1xuICAgIGlmICghdGhpcy5fc2NvcGUpIHJldHVyblxuXG4gICAgYXdhaXQgdGhpcy5fcmVwbGF5RnJvbShjdXJzb3JGcm9tUGFyYW1zKHRoaXMucGFyYW1zKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXBsYXlzIHJvd3MgbWlzc2VkIHdoaWxlIHRoZSBzb2NrZXQgd2FzIGRvd24sIHJlc3VtaW5nIGZyb20gdGhlIGN1cnNvciBvZlxuICAgKiB0aGUgbGFzdCBlbnRyeSB0aGlzIHN1YnNjcmlwdGlvbiBkZWxpdmVyZWQgaW5zdGVhZCBvZiByZS1yZWFkaW5nIHRoZSB3aG9sZVxuICAgKiBzY29wZSBmcm9tIHRoZSBzdWJzY3JpYmUtdGltZSBjdXJzb3IuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgb25SZXN1bWUoKSB7XG4gICAgaWYgKCF0aGlzLl9zY29wZSkgcmV0dXJuXG5cbiAgICBhd2FpdCB0aGlzLl9yZXBsYXlGcm9tKHRoaXMuX2RlbGl2ZXJlZEN1cnNvciA/PyBjdXJzb3JGcm9tUGFyYW1zKHRoaXMucGFyYW1zKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBQYWdlcyB0aGUgc2NvcGVkIGNoYW5nZSBmZWVkIGZyb20gYSBjdXJzb3IgYW5kIGRlbGl2ZXJzIGVhY2ggcGFnZSB0byB0aGlzXG4gICAqIHN1YnNjcmliZXIgYXMgYSBge2VjaG9PcmlnaW4sIHN5bmNzfWAgZW52ZWxvcGUuIFRoZSBzbmFwc2hvdCBoaWdoLXdhdGVyIGlzXG4gICAqIHJlc29sdmVkIG9uY2UgYW5kIHBpbm5lZCBhY3Jvc3MgcGFnZXMgc28gcm93cyBhcnJpdmluZyBtaWQtcmVwbGF5IGFyZSBsZWZ0IHRvXG4gICAqIHRoZSBsaXZlIGJyb2FkY2FzdCBwYXRoIGluc3RlYWQgb2YgbG9vcGluZyB0aGUgcmVwbGF5LiBUaGUgZGVsaXZlcmVkIGN1cnNvclxuICAgKiBhZHZhbmNlcyBzbyBhIGxhdGVyIHJlc3VtZSByZXN1bWVzIGZyb20gaGVyZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ3Vyc29yfSBhZnRlckN1cnNvciAtIEN1cnNvciB0byByZXBsYXkgYWZ0ZXIuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgX3JlcGxheUZyb20oYWZ0ZXJDdXJzb3IpIHtcbiAgICBjb25zdCBjb25maWd1cmF0aW9uID0gdGhpcy5zZXNzaW9uLmNvbmZpZ3VyYXRpb25cbiAgICBjb25zdCBzY29wZSA9IHRoaXMuX3Njb3BlXG5cbiAgICBhd2FpdCBjb25maWd1cmF0aW9uLmVuc3VyZUNvbm5lY3Rpb25zKHtuYW1lOiBgJHtWRUxPQ0lPVVNfU1lOQ19DSEFOTkVMfSBjYXRjaC11cCByZXBsYXlgfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCB0aGlzLmJ1aWxkU3luY1Jlc291cmNlKClcbiAgICAgIGNvbnN0IGxpbWl0ID0gdGhpcy5fcmVwbGF5TGltaXQocmVzb3VyY2UpXG4gICAgICAvKiogQHR5cGUge2ltcG9ydChcIi4vc3luYy1hcGktY2xpZW50LXR5cGVzLmpzXCIpLlN5bmNDdXJzb3J9ICovXG4gICAgICBsZXQgdXBUb0N1cnNvciA9IG51bGxcblxuICAgICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgY29uc3QgcGFnZSA9IGF3YWl0IHJlc291cmNlLmNoYW5nZUZlZWRTZXJ2aWNlKHtcbiAgICAgICAgICBwYXJhbXM6IHsuLi50aGlzLnBhcmFtcywgLi4uY3Vyc29yUGFyYW1zKFwiYWZ0ZXJcIiwgYWZ0ZXJDdXJzb3IpLCAuLi4odXBUb0N1cnNvciA/IGN1cnNvclBhcmFtcyhcInVwVG9cIiwgdXBUb0N1cnNvcikgOiB7fSl9LFxuICAgICAgICAgIHNjb3BlXG4gICAgICAgIH0pLmNoYW5nZXMoKVxuXG4gICAgICAgIGlmICghdXBUb0N1cnNvcikgdXBUb0N1cnNvciA9IHBhZ2UudXBUb0N1cnNvclxuXG4gICAgICAgIGlmIChwYWdlLnN5bmNzLmxlbmd0aCA9PT0gMCkgYnJlYWtcblxuICAgICAgICBhd2FpdCB0aGlzLmRlbGl2ZXJCcm9hZGNhc3Qoe2VjaG9PcmlnaW46IG51bGwsIHN5bmNzOiBwYWdlLnN5bmNzfSlcblxuICAgICAgICBhZnRlckN1cnNvciA9IHBhZ2UubmV4dEN1cnNvclxuICAgICAgICBpZiAoYWZ0ZXJDdXJzb3IpIHRoaXMuX2RlbGl2ZXJlZEN1cnNvciA9IGFmdGVyQ3Vyc29yXG5cbiAgICAgICAgaWYgKHBhZ2Uuc3luY3MubGVuZ3RoIDwgbGltaXQpIGJyZWFrXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlcyB0aGUgY2F0Y2gtdXAgcmVwbGF5IHBhZ2Ugc2l6ZSBmcm9tIHRoZSBzdWJzY3JpYmVyJ3MgYGxpbWl0YCBwYXJhbVxuICAgKiAoZGVmYXVsdGluZyB0byB7QGxpbmsgREVGQVVMVF9SRVBMQVlfTElNSVR9KS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuL3N5bmMtcmVzb3VyY2UtYmFzZS5qc1wiKS5kZWZhdWx0fSByZXNvdXJjZSAtIEF1dGhvcml6aW5nIHN5bmMgcmVzb3VyY2UuXG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IFJlcGxheSBwYWdlIHNpemUuXG4gICAqL1xuICBfcmVwbGF5TGltaXQocmVzb3VyY2UpIHtcbiAgICByZXR1cm4gbmV3IFN5bmNNb2RlbENoYW5nZUZlZWRTZXJ2aWNlKHttb2RlbENsYXNzOiByZXNvdXJjZS5zeW5jTW9kZWxDbGFzcygpLCBwYXJhbXM6IHRoaXMucGFyYW1zfSkubm9ybWFsaXplZExpbWl0KHRoaXMucGFyYW1zLmxpbWl0KVxuICB9XG5cbiAgLyoqXG4gICAqIEFkdmFuY2VzIHRoZSBkZWxpdmVyZWQtY3Vyc29yIHdhdGVybWFyayBmcm9tIGEgZGVsaXZlcmVkIGVudmVsb3BlIHNvIGEgcmVzdW1lXG4gICAqIHJlc3VtZXMgYWZ0ZXIgdGhlIG5ld2VzdCByb3cgYWN0dWFsbHkgc2VudCB0byB0aGlzIHN1YnNjcmliZXIuIEJyb2FkY2FzdHMgbWF5XG4gICAqIGFycml2ZSBvdXQgb2Ygb3JkZXIgYWNyb3NzIHJlc291cmNlcywgc28gZWFjaCBlbnRyeSBvbmx5IGFkdmFuY2VzIHRoZVxuICAgKiB3YXRlcm1hcmsgd2hlbiBpdCBpcyBsYXRlciB0aGFuIHRoZSBjdXJyZW50IG9uZS5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9odHRwLXNlcnZlci93ZWJzb2NrZXQtY2hhbm5lbC5qc1wiKS5XZWJzb2NrZXRKc29uVmFsdWV9IGJvZHkgLSBEZWxpdmVyZWQgZW52ZWxvcGUuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgX3RyYWNrRGVsaXZlcmVkQ3Vyc29yKGJvZHkpIHtcbiAgICBpZiAoIWJvZHkgfHwgdHlwZW9mIGJvZHkgIT09IFwib2JqZWN0XCIgfHwgQXJyYXkuaXNBcnJheShib2R5KSkgcmV0dXJuXG5cbiAgICBjb25zdCBlbnZlbG9wZSA9IC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgUmV0dXJuVHlwZTx0eXBlb2YgSlNPTi5wYXJzZT4+fSAqLyAoYm9keSlcbiAgICBjb25zdCBzeW5jcyA9IEFycmF5LmlzQXJyYXkoZW52ZWxvcGUuc3luY3MpID8gZW52ZWxvcGUuc3luY3MgOiBbZW52ZWxvcGVdXG5cbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIHN5bmNzKSB7XG4gICAgICBjb25zdCBzeW5jRW50cnkgPSAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIEpTT04ucGFyc2U+Pn0gKi8gKGVudHJ5KVxuICAgICAgY29uc3QgY3Vyc29yID0gY3Vyc29yRnJvbVN5bmNFbnRyeShzeW5jRW50cnkpXG5cbiAgICAgIGlmIChjdXJzb3IgJiYgaXNDdXJzb3JBZnRlcihjdXJzb3IsIHRoaXMuX2RlbGl2ZXJlZEN1cnNvcikpIHRoaXMuX2RlbGl2ZXJlZEN1cnNvciA9IGN1cnNvclxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBhdXRob3JpemVkIHNjb3BlIGZvciBkZWJ1ZyBzbmFwc2hvdHMuXG4gICAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCBSZXR1cm5UeXBlPHR5cGVvZiBKU09OLnBhcnNlPj59IERlYnVnLXNhZmUgc3Vic2NyaXB0aW9uIGRldGFpbHMuXG4gICAqL1xuICBkZWJ1Z1NuYXBzaG90KCkge1xuICAgIHJldHVybiB7c2NvcGU6IHRoaXMuX3Njb3BlLCB1c2VyU2NvcGU6IHRoaXMuX2lzVXNlclNjb3BlKCl9XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module sync-cursor
|
|
5
|
+
*
|
|
6
|
+
* Shared cursor helpers for the sync change feed: building a cursor from a
|
|
7
|
+
* serialized sync entry and ordering two cursors under the feed's stable order
|
|
8
|
+
* (server sequence when present, else updated-at then id). The server channel
|
|
9
|
+
* advances its per-subscription replay watermark with these across out-of-order
|
|
10
|
+
* live broadcasts, and the realtime bridge advances the persisted scope cursor
|
|
11
|
+
* monotonically on pushed changes.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** @typedef {import("./sync-api-client-types.js").SyncCursor} SyncCursor */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Builds a cursor from a serialized sync entry, or null when the entry carries
|
|
18
|
+
* no cursor identity (no updated-at).
|
|
19
|
+
* @param {Record<string, ReturnType<typeof JSON.parse>>} entry - Serialized sync entry.
|
|
20
|
+
* @returns {SyncCursor} Cursor for the entry, or null.
|
|
21
|
+
*/
|
|
22
|
+
export function cursorFromSyncEntry(entry) {
|
|
23
|
+
const updatedAt = entry.updatedAt
|
|
24
|
+
|
|
25
|
+
if (typeof updatedAt !== "string" || updatedAt === "") return null
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
id: entry.id === null || entry.id === undefined ? null : String(entry.id),
|
|
29
|
+
serverSequence: typeof entry.serverSequence === "number" ? entry.serverSequence : null,
|
|
30
|
+
updatedAt
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether one cursor is strictly later than another under the change-feed order.
|
|
36
|
+
* A sequenced cursor is always later than an unsequenced one; both sequenced
|
|
37
|
+
* cursors order by server sequence; both unsequenced cursors order by updated-at
|
|
38
|
+
* then id.
|
|
39
|
+
* @param {SyncCursor} candidate - Candidate cursor.
|
|
40
|
+
* @param {SyncCursor} current - Current cursor.
|
|
41
|
+
* @returns {boolean} Whether the candidate is later.
|
|
42
|
+
*/
|
|
43
|
+
export function isCursorAfter(candidate, current) {
|
|
44
|
+
if (!candidate) return false
|
|
45
|
+
if (!current) return true
|
|
46
|
+
|
|
47
|
+
if (candidate.serverSequence !== null && current.serverSequence !== null) {
|
|
48
|
+
return candidate.serverSequence > current.serverSequence
|
|
49
|
+
}
|
|
50
|
+
if (candidate.serverSequence !== null) return true
|
|
51
|
+
if (current.serverSequence !== null) return false
|
|
52
|
+
|
|
53
|
+
if (candidate.updatedAt !== current.updatedAt) return candidate.updatedAt > current.updatedAt
|
|
54
|
+
|
|
55
|
+
return (candidate.id ?? "") > (current.id ?? "")
|
|
56
|
+
}
|
|
@@ -5,10 +5,46 @@ import {mergeRemoteRequestContext} from "../remote-request-context.js"
|
|
|
5
5
|
|
|
6
6
|
import SyncApiClient from "./sync-api-client.js"
|
|
7
7
|
import {VELOCIOUS_SYNC_CHANNEL} from "./sync-channel-name.js"
|
|
8
|
+
import {cursorFromSyncEntry, isCursorAfter} from "./sync-cursor.js"
|
|
8
9
|
|
|
9
10
|
/** @typedef {import("../configuration-types.js").VelociousSyncRealtimeChannelDescriptor} VelociousSyncRealtimeChannelDescriptor */
|
|
10
11
|
/** @typedef {import("../configuration-types.js").VelociousSyncRealtimeWebsocketClient} VelociousSyncRealtimeWebsocketClient */
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* The persisted scope row the bridge carries so it can advance the scope's cursor
|
|
15
|
+
* on applied pushes. Mirrors the framework-owned scope store row shape.
|
|
16
|
+
* @typedef {object} SyncRealtimeScopeRow
|
|
17
|
+
* @property {Record<string, ReturnType<typeof JSON.parse>>} conditions - Scope attribute conditions.
|
|
18
|
+
* @property {string | null} cursorPayload - Persisted cursor JSON payload.
|
|
19
|
+
* @property {string} id - Scope row id.
|
|
20
|
+
* @property {string | null} resourceType - Scope resource type.
|
|
21
|
+
* @property {string} scopeDigest - Fixed-size scope digest.
|
|
22
|
+
* @property {string} state - Scope state.
|
|
23
|
+
* @property {string} storeIdentity - Physical store identity.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One derived channel subscription carrying its owning scope row (null for legacy
|
|
28
|
+
* app channels) and the in-memory cursor watermark advanced by applied pushes.
|
|
29
|
+
* @typedef {object} DerivedSyncChannel
|
|
30
|
+
* @property {string} channel - Channel name.
|
|
31
|
+
* @property {import("./sync-api-client-types.js").SyncCursor} cursor - In-memory cursor watermark.
|
|
32
|
+
* @property {string | null} resourceType - Default resource type for pushed changes.
|
|
33
|
+
* @property {SyncRealtimeScopeRow | null} scopeRow - Owning scope row, or null for legacy channels.
|
|
34
|
+
* @property {import("../configuration-types.js").VelociousSyncRealtimeSubscription} subscription - Subscription handle.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* One derived channel to subscribe, carrying its owning scope row and cursor for
|
|
39
|
+
* framework sync channel subscriptions (null scope row for legacy app channels).
|
|
40
|
+
* @typedef {object} DerivedSyncChannelDescriptor
|
|
41
|
+
* @property {string} channel - Channel name.
|
|
42
|
+
* @property {import("./sync-api-client-types.js").SyncCursor} cursor - In-memory cursor watermark (null for legacy channels).
|
|
43
|
+
* @property {Record<string, ReturnType<typeof JSON.parse>>} params - Subscribe params (without the framework-injected auth token).
|
|
44
|
+
* @property {string | null} resourceType - Default resource type for pushed changes.
|
|
45
|
+
* @property {SyncRealtimeScopeRow | null} scopeRow - Owning scope row, or null for legacy channels.
|
|
46
|
+
*/
|
|
47
|
+
|
|
12
48
|
/**
|
|
13
49
|
* Derived realtime push bridge for the sync client. Subscribes every declared
|
|
14
50
|
* pull scope to the framework sync channel ({@link VELOCIOUS_SYNC_CHANNEL})
|
|
@@ -29,7 +65,7 @@ export default class SyncRealtimeBridge {
|
|
|
29
65
|
*/
|
|
30
66
|
constructor({syncClient}) {
|
|
31
67
|
this.syncClient = syncClient
|
|
32
|
-
/** @type {Array<
|
|
68
|
+
/** @type {Array<DerivedSyncChannel>} */
|
|
33
69
|
this._channels = []
|
|
34
70
|
/** @type {VelociousSyncRealtimeWebsocketClient | null} */
|
|
35
71
|
this._client = null
|
|
@@ -40,8 +76,6 @@ export default class SyncRealtimeBridge {
|
|
|
40
76
|
/** @type {number} Subscription generation - bumped by unsubscribe so in-flight subscribes detect they became stale. */
|
|
41
77
|
this._generation = 0
|
|
42
78
|
/** @type {Promise<void> | null} */
|
|
43
|
-
this._scheduledPull = null
|
|
44
|
-
/** @type {Promise<void> | null} */
|
|
45
79
|
this._subscribePromise = null
|
|
46
80
|
/** @type {"subscribed" | "subscribing" | "unsubscribed"} */
|
|
47
81
|
this._state = "unsubscribed"
|
|
@@ -83,7 +117,7 @@ export default class SyncRealtimeBridge {
|
|
|
83
117
|
|
|
84
118
|
this._state = "subscribing"
|
|
85
119
|
|
|
86
|
-
/** @type {Array<
|
|
120
|
+
/** @type {Array<DerivedSyncChannel>} */
|
|
87
121
|
const channels = []
|
|
88
122
|
/** @type {VelociousSyncRealtimeWebsocketClient | null} */
|
|
89
123
|
let client = null
|
|
@@ -154,13 +188,19 @@ export default class SyncRealtimeBridge {
|
|
|
154
188
|
label: "Sync client request context",
|
|
155
189
|
params: {...channelDescriptor.params, authenticationToken}
|
|
156
190
|
})
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
191
|
+
/** @type {DerivedSyncChannel} */
|
|
192
|
+
const channel = {
|
|
193
|
+
channel: channelDescriptor.channel,
|
|
194
|
+
cursor: channelDescriptor.cursor,
|
|
195
|
+
resourceType,
|
|
196
|
+
scopeRow: channelDescriptor.scopeRow,
|
|
197
|
+
subscription: client.subscribeChannel(channelDescriptor.channel, {
|
|
198
|
+
onMessage: (body) => this.enqueueApply({body, channel, generation}),
|
|
199
|
+
params
|
|
200
|
+
})
|
|
201
|
+
}
|
|
162
202
|
|
|
163
|
-
channels.push(
|
|
203
|
+
channels.push(channel)
|
|
164
204
|
}
|
|
165
205
|
|
|
166
206
|
await Promise.all(channels.map(({subscription}) => subscription.waitForReady({signal})))
|
|
@@ -175,7 +215,6 @@ export default class SyncRealtimeBridge {
|
|
|
175
215
|
this._client = client
|
|
176
216
|
this._ownsClient = ownsClient
|
|
177
217
|
this._state = "subscribed"
|
|
178
|
-
this.schedulePull(generation)
|
|
179
218
|
} catch (error) {
|
|
180
219
|
await teardown()
|
|
181
220
|
|
|
@@ -225,12 +264,11 @@ export default class SyncRealtimeBridge {
|
|
|
225
264
|
}
|
|
226
265
|
|
|
227
266
|
/**
|
|
228
|
-
* Awaits all enqueued message applies
|
|
267
|
+
* Awaits all enqueued message applies (tests, shutdown flows).
|
|
229
268
|
* @returns {Promise<void>}
|
|
230
269
|
*/
|
|
231
270
|
async waitForApplied() {
|
|
232
271
|
await this._applyPromise
|
|
233
|
-
if (this._scheduledPull) await this._scheduledPull
|
|
234
272
|
}
|
|
235
273
|
|
|
236
274
|
/**
|
|
@@ -266,33 +304,44 @@ export default class SyncRealtimeBridge {
|
|
|
266
304
|
* model-level static realtime declarations and the config channels callback.
|
|
267
305
|
* Fails loudly when nothing is subscribable.
|
|
268
306
|
* @param {ReturnType<typeof JSON.parse>} context - App context passed to the deprecated channels callback.
|
|
269
|
-
* @returns {Promise<Array<
|
|
307
|
+
* @returns {Promise<Array<DerivedSyncChannelDescriptor>>} Channel descriptors.
|
|
270
308
|
*/
|
|
271
309
|
async channelDescriptors(context) {
|
|
272
310
|
const realtime = this.realtimeConfiguration()
|
|
273
|
-
/** @type {Array<
|
|
311
|
+
/** @type {Array<DerivedSyncChannelDescriptor>} */
|
|
274
312
|
const channelDescriptors = []
|
|
275
313
|
|
|
276
314
|
for (const scopeRow of await this.syncClient.scopeStore().activeScopes()) {
|
|
315
|
+
const cursorPayload = await this.syncClient.scopeStore().loadCursor(scopeRow)
|
|
316
|
+
const cursor = SyncApiClient.syncCursorFromPayload(cursorPayload)
|
|
317
|
+
|
|
277
318
|
channelDescriptors.push({
|
|
278
319
|
channel: VELOCIOUS_SYNC_CHANNEL,
|
|
320
|
+
cursor,
|
|
279
321
|
params: {
|
|
280
322
|
conditions: this.attributeNamedConditions(scopeRow),
|
|
281
323
|
resourceType: scopeRow.resourceType,
|
|
282
324
|
// Only the all-types scope carries the type list; a type-declared scope needs none.
|
|
283
|
-
...(scopeRow.resourceType === null ? {resourceTypes: this.syncClient.userScopeResourceTypes()} : {})
|
|
284
|
-
|
|
325
|
+
...(scopeRow.resourceType === null ? {resourceTypes: this.syncClient.userScopeResourceTypes()} : {}),
|
|
326
|
+
// Reuse the changes-endpoint cursor fields so the server replays the
|
|
327
|
+
// catch-up backlog from the scope's persisted cursor on subscribe.
|
|
328
|
+
...SyncApiClient.cursorPayload("after", cursor)
|
|
329
|
+
},
|
|
330
|
+
resourceType: scopeRow.resourceType,
|
|
331
|
+
scopeRow
|
|
285
332
|
})
|
|
286
333
|
}
|
|
287
334
|
|
|
288
335
|
for (const [resourceType, resourceConfig] of Object.entries(this.syncClient.config.resources)) {
|
|
289
336
|
if (!resourceConfig.realtime) continue
|
|
290
337
|
|
|
291
|
-
channelDescriptors.push({channel: resourceConfig.realtime.channel, params: resourceConfig.realtime.params, resourceType})
|
|
338
|
+
channelDescriptors.push({channel: resourceConfig.realtime.channel, cursor: null, params: resourceConfig.realtime.params ?? {}, resourceType, scopeRow: null})
|
|
292
339
|
}
|
|
293
340
|
|
|
294
341
|
if (realtime?.channels) {
|
|
295
|
-
|
|
342
|
+
const legacyChannels = await realtime.channels(context)
|
|
343
|
+
|
|
344
|
+
channelDescriptors.push(...legacyChannels.map((descriptor) => ({channel: descriptor.channel, cursor: null, params: descriptor.params ?? {}, resourceType: descriptor.resourceType ?? null, scopeRow: null})))
|
|
296
345
|
}
|
|
297
346
|
|
|
298
347
|
if (channelDescriptors.length === 0) {
|
|
@@ -341,15 +390,15 @@ export default class SyncRealtimeBridge {
|
|
|
341
390
|
/**
|
|
342
391
|
* Chains one pushed message onto the serialized apply queue so changes apply
|
|
343
392
|
* in arrival order; failures go to the sync client's error reporting.
|
|
344
|
-
* @param {{body: ReturnType<typeof JSON.parse>, generation?: number
|
|
393
|
+
* @param {{body: ReturnType<typeof JSON.parse>, channel: DerivedSyncChannel, generation?: number}} args - Message args.
|
|
345
394
|
* @returns {void}
|
|
346
395
|
*/
|
|
347
|
-
enqueueApply({body, generation = this._generation
|
|
396
|
+
enqueueApply({body, channel, generation = this._generation}) {
|
|
348
397
|
this._applyPromise = this._applyPromise.then(async () => {
|
|
349
398
|
if (generation !== this._generation) return
|
|
350
399
|
|
|
351
400
|
try {
|
|
352
|
-
await this.applyMessage({body,
|
|
401
|
+
await this.applyMessage({body, channel})
|
|
353
402
|
} catch (error) {
|
|
354
403
|
this.syncClient.reportError(/** @type {Error} */ (error))
|
|
355
404
|
}
|
|
@@ -359,11 +408,12 @@ export default class SyncRealtimeBridge {
|
|
|
359
408
|
/**
|
|
360
409
|
* Applies one pushed message through the derived resource applier: drops
|
|
361
410
|
* own-device messages by echo origin, defaults the channel's resourceType onto
|
|
362
|
-
* envelopes without one,
|
|
363
|
-
*
|
|
411
|
+
* envelopes without one, fails loudly on unknown resource types, and advances
|
|
412
|
+
* the owning scope's cursor so a later replay resumes after the applied rows.
|
|
413
|
+
* @param {{body: ReturnType<typeof JSON.parse>, channel: DerivedSyncChannel}} args - Message args.
|
|
364
414
|
* @returns {Promise<void>}
|
|
365
415
|
*/
|
|
366
|
-
async applyMessage({body,
|
|
416
|
+
async applyMessage({body, channel}) {
|
|
367
417
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
368
418
|
throw new Error(`Realtime sync messages must be envelope objects, got: ${JSON.stringify(body)}`)
|
|
369
419
|
}
|
|
@@ -382,44 +432,40 @@ export default class SyncRealtimeBridge {
|
|
|
382
432
|
// Coalesce record-change events across the pushed batch so it triggers one live-query re-run.
|
|
383
433
|
await recordChanges.batch(async () => {
|
|
384
434
|
for (const syncPayload of syncPayloads) {
|
|
385
|
-
const sync = SyncApiClient.syncEnvelopeFromPayload({resourceType, ...syncPayload})
|
|
435
|
+
const sync = SyncApiClient.syncEnvelopeFromPayload({resourceType: channel.resourceType, ...syncPayload})
|
|
386
436
|
|
|
387
437
|
await applySync(sync)
|
|
388
438
|
}
|
|
389
439
|
})
|
|
440
|
+
|
|
441
|
+
await this.advanceScopeCursor(channel, syncPayloads)
|
|
390
442
|
}
|
|
391
443
|
|
|
392
444
|
/**
|
|
393
|
-
*
|
|
394
|
-
* (
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
* @
|
|
445
|
+
* Advances the owning scope's persisted cursor to the newest applied row,
|
|
446
|
+
* monotonically (never backwards) so an out-of-order or duplicate push cannot
|
|
447
|
+
* regress the cursor and re-deliver already-applied rows on a later replay.
|
|
448
|
+
* Legacy channels with no owning scope are skipped.
|
|
449
|
+
* @param {DerivedSyncChannel} channel - Channel owning the scope.
|
|
450
|
+
* @param {Array<ReturnType<typeof JSON.parse>>} syncPayloads - Applied sync entries.
|
|
451
|
+
* @returns {Promise<void>}
|
|
398
452
|
*/
|
|
399
|
-
|
|
400
|
-
if (
|
|
401
|
-
if (this.realtimeConfiguration()?.pullOnReconnect === false) return
|
|
453
|
+
async advanceScopeCursor(channel, syncPayloads) {
|
|
454
|
+
if (!channel.scopeRow) return
|
|
402
455
|
|
|
403
|
-
|
|
456
|
+
let latestCursor = channel.cursor
|
|
404
457
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
458
|
+
for (const syncPayload of syncPayloads) {
|
|
459
|
+
const entry = /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (syncPayload)
|
|
460
|
+
const cursor = cursorFromSyncEntry(entry)
|
|
461
|
+
|
|
462
|
+
if (cursor && isCursorAfter(cursor, latestCursor)) latestCursor = cursor
|
|
410
463
|
}
|
|
411
464
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (!this.syncClient.isLifecycleAbort(error)) {
|
|
417
|
-
this.syncClient.reportError(/** @type {Error} */ (error))
|
|
418
|
-
}
|
|
419
|
-
} finally {
|
|
420
|
-
this._scheduledPull = null
|
|
421
|
-
}
|
|
422
|
-
})()
|
|
465
|
+
if (latestCursor && latestCursor !== channel.cursor) {
|
|
466
|
+
channel.cursor = latestCursor
|
|
467
|
+
await this.syncClient.scopeStore().saveCursor(channel.scopeRow, latestCursor)
|
|
468
|
+
}
|
|
423
469
|
}
|
|
424
470
|
}
|
|
425
471
|
|