sliftutils 1.7.27 → 1.7.29

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/index.d.ts CHANGED
@@ -2186,7 +2186,6 @@ declare module "sliftutils/storage/IArchives" {
2186
2186
  noFullSync?: boolean;
2187
2187
  identity?: string;
2188
2188
  };
2189
- export declare const WRITE_PAST_WINDOW_GRACE: number;
2190
2189
  export declare const STORAGE_WRONG_VALID_WINDOW = "REMOTE_STORAGE_WRONG_VALID_WINDOW_a7c1f04e";
2191
2190
  export declare const STORAGE_WRONG_ROUTE = "REMOTE_STORAGE_WRONG_ROUTE_c94d2e17";
2192
2191
  export declare const FULL_ROUTE: [number, number];
@@ -3208,6 +3207,7 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3208
3207
  private initRetryTimer;
3209
3208
  private pollTimer;
3210
3209
  private disposed;
3210
+ private unsubscribeRoutingPush;
3211
3211
  constructor(config: RemoteConfig | RemoteConfigBase);
3212
3212
  getDebugName(): string;
3213
3213
  private getState;
@@ -3276,6 +3276,7 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3276
3276
  set(fileName: string, data: Buffer, config?: {
3277
3277
  lastModified?: number;
3278
3278
  }): Promise<string>;
3279
+ private setRoutingConfig;
3279
3280
  del(fileName: string): Promise<void>;
3280
3281
  private setVariableShard;
3281
3282
  setLargeFile(config: {
@@ -3438,6 +3439,15 @@ declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3438
3439
 
3439
3440
  }
3440
3441
 
3442
+ declare module "sliftutils/storage/remoteStorage/storageClientController" {
3443
+ /** Subscribe to server-pushed routing change notifications. Returns the unsubscribe function. */
3444
+ export declare function onServerRoutingChanged(listener: () => void): () => void;
3445
+ export declare const StorageClientController: import("socket-function/SocketFunctionTypes").SocketRegistered<{
3446
+ routingConfigChanged: () => Promise<void>;
3447
+ }>;
3448
+
3449
+ }
3450
+
3441
3451
  declare module "sliftutils/storage/remoteStorage/storageController" {
3442
3452
  /// <reference types="node" />
3443
3453
  /// <reference types="node" />
@@ -3599,6 +3609,7 @@ declare module "sliftutils/storage/remoteStorage/storageServerState" {
3599
3609
  };
3600
3610
  export declare function addExtraListenPort(port: number): void;
3601
3611
  export declare function getLoadedBucket(account: string, bucketName: string): Promise<LoadedBucket | undefined>;
3612
+ export declare function setRoutingChangedBroadcaster(broadcaster: () => void): void;
3602
3613
  export declare function assertMutable(bucket: LoadedBucket, filePath: string, writeTime: number): Promise<void>;
3603
3614
  export declare function writeBucketFile(account: string, bucketName: string, filePath: string, data: Buffer, config?: {
3604
3615
  lastModified?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.7.27",
3
+ "version": "1.7.29",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -83,7 +83,6 @@ export type ArchivesSource = {
83
83
  noFullSync?: boolean;
84
84
  identity?: string;
85
85
  };
86
- export declare const WRITE_PAST_WINDOW_GRACE: number;
87
86
  export declare const STORAGE_WRONG_VALID_WINDOW = "REMOTE_STORAGE_WRONG_VALID_WINDOW_a7c1f04e";
88
87
  export declare const STORAGE_WRONG_ROUTE = "REMOTE_STORAGE_WRONG_ROUTE_c94d2e17";
89
88
  export declare const FULL_ROUTE: [number, number];
@@ -136,7 +136,7 @@ export type ArchivesConfig = {
136
136
  export type ArchivesSource = {
137
137
  source: IArchives;
138
138
  // From the source's CommonConfig. Values with write times outside the window are ignored when
139
- // scanning, and once its end is sufficiently past (see windowAcceptsWrites) the source stops
139
+ // scanning, and once its end is past (see windowAcceptsWrites) the source stops
140
140
  // receiving writes entirely - still scanned (it holds the authoritative data for its window),
141
141
  // it just stops growing.
142
142
  validWindow: [number, number];
@@ -152,10 +152,6 @@ export type ArchivesSource = {
152
152
  identity?: string;
153
153
  };
154
154
 
155
- // The grace covers clock uncertainty around the window edge: a write slightly past the end doesn't
156
- // hurt, but once clearly past, the source is an archive and must stop growing.
157
- export const WRITE_PAST_WINDOW_GRACE = 5 * 60 * 1000;
158
-
159
155
  // Error marker a server includes when a freshly-stamped write reaches it outside its valid windows
160
156
  // (the client resolved its target, then time crossed a window boundary before the write landed).
161
157
  // Clients detect this marker and re-resolve the currently-valid source, retrying ONCE - boundaries
@@ -172,9 +168,11 @@ export const FULL_ROUTE: [number, number] = [0, 1];
172
168
  // latency, up) write shard, appends "_<value in the shard's route>" directly after the sentinel,
173
169
  // and returns the materialized key. getRoute treats that suffix as a complete route override.
174
170
  export const VARIABLE_SHARD = "VARIABLE_SHARD_f0234jfah08fgyhfgyssdds83nmp";
171
+ // No grace past the end: a window boundary is a hard handoff (clients retry a rejected write
172
+ // against the newly-valid source, so leniency here would only desynchronize the handoff)
175
173
  export function windowAcceptsWrites(validWindow: [number, number] | undefined): boolean {
176
174
  if (!validWindow) return true;
177
- return validWindow[1] + WRITE_PAST_WINDOW_GRACE > Date.now();
175
+ return validWindow[1] > Date.now();
178
176
  }
179
177
 
180
178
  export type ArchivesSyncSourceStatus = {
@@ -1,6 +1,6 @@
1
- "use strict"; var console = (function shimConsole() { let baseConsole = globalThis.console; if (typeof window !== "undefined") return baseConsole; let name = __filename.replaceAll("\\", "/").split("/").pop(); let dirName = __dirname.replaceAll("\\", "/").split("/").pop(); let context = { __FILE__: __filename, __DIR__: __dirname, __NAME__: name, __DIRNAME__: dirName }; let newConsole = { ...baseConsole }; for (let shim of ["log", "info", "warn", "error"]) { newConsole[shim] = (...args) => { args.push(context); baseConsole[shim](...args); }; } newConsole.__context = context; return newConsole; })(); "use strict";
1
+ "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
3
- //exports.windowAcceptsWrites = exports.VARIABLE_SHARD = exports.FULL_ROUTE = exports.STORAGE_WRONG_ROUTE = exports.STORAGE_WRONG_VALID_WINDOW = exports.WRITE_PAST_WINDOW_GRACE = exports.FULL_VALID_WINDOW = exports.assertValidLastModified = exports.IMMUTABLE_CACHE_TIME = exports.MAX_LAST_MODIFIED_FUTURE = void 0;
3
+ //exports.windowAcceptsWrites = exports.VARIABLE_SHARD = exports.FULL_ROUTE = exports.STORAGE_WRONG_ROUTE = exports.STORAGE_WRONG_VALID_WINDOW = exports.FULL_VALID_WINDOW = exports.assertValidLastModified = exports.IMMUTABLE_CACHE_TIME = exports.MAX_LAST_MODIFIED_FUTURE = void 0;
4
4
  module.allowclient = true;
5
5
  // The important operations of an archive bucket (extracted from ArchivesBackblaze), so other
6
6
  // backends (e.g. our own remote storage server) can be used interchangeably.
@@ -18,9 +18,6 @@ function assertValidLastModified(lastModified) {
18
18
  }
19
19
  exports.assertValidLastModified = assertValidLastModified;
20
20
  exports.FULL_VALID_WINDOW = [0, Number.MAX_SAFE_INTEGER];
21
- // The grace covers clock uncertainty around the window edge: a write slightly past the end doesn't
22
- // hurt, but once clearly past, the source is an archive and must stop growing.
23
- exports.WRITE_PAST_WINDOW_GRACE = 5 * 60 * 1000;
24
21
  // Error marker a server includes when a freshly-stamped write reaches it outside its valid windows
25
22
  // (the client resolved its target, then time crossed a window boundary before the write landed).
26
23
  // Clients detect this marker and re-resolve the currently-valid source, retrying ONCE - boundaries
@@ -34,11 +31,13 @@ exports.FULL_ROUTE = [0, 1];
34
31
  // latency, up) write shard, appends "_<value in the shard's route>" directly after the sentinel,
35
32
  // and returns the materialized key. getRoute treats that suffix as a complete route override.
36
33
  exports.VARIABLE_SHARD = "VARIABLE_SHARD_f0234jfah08fgyhfgyssdds83nmp";
34
+ // No grace past the end: a window boundary is a hard handoff (clients retry a rejected write
35
+ // against the newly-valid source, so leniency here would only desynchronize the handoff)
37
36
  function windowAcceptsWrites(validWindow) {
38
37
  if (!validWindow)
39
38
  return true;
40
- return validWindow[1] + exports.WRITE_PAST_WINDOW_GRACE > Date.now();
39
+ return validWindow[1] > Date.now();
41
40
  }
42
41
  exports.windowAcceptsWrites = windowAcceptsWrites;
43
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSUFyY2hpdmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiSUFyY2hpdmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLE1BQU0sQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBRTFCLDZGQUE2RjtBQUM3Riw2RUFBNkU7QUFFN0UsOEZBQThGO0FBQzlGLDBEQUEwRDtBQUM3QyxRQUFBLHdCQUF3QixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBRXZELGtHQUFrRztBQUNsRyx5RkFBeUY7QUFDNUUsUUFBQSxvQkFBb0IsR0FBRyxLQUFLLEdBQUcsSUFBSSxDQUFDO0FBQ2pELFNBQWdCLHVCQUF1QixDQUFDLFlBQW9CO0lBQ3hELElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxnQ0FBd0IsQ0FBQztJQUNoRCxJQUFJLFlBQVksR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNyQixNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxZQUFZLE1BQU0sR0FBRyxxQkFBcUIsQ0FBQyxDQUFDO0lBQzFHLENBQUM7QUFDTCxDQUFDO0FBTEQsMERBS0M7QUEyRVksUUFBQSxpQkFBaUIsR0FBcUIsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7QUE4RGhGLG1HQUFtRztBQUNuRywrRUFBK0U7QUFDbEUsUUFBQSx1QkFBdUIsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUVyRCxtR0FBbUc7QUFDbkcsaUdBQWlHO0FBQ2pHLG1HQUFtRztBQUNuRyx1RkFBdUY7QUFDMUUsUUFBQSwwQkFBMEIsR0FBRyw0Q0FBNEMsQ0FBQztBQUV2RixtR0FBbUc7QUFDbkcsNkZBQTZGO0FBQ2hGLFFBQUEsbUJBQW1CLEdBQUcscUNBQXFDLENBQUM7QUFFNUQsUUFBQSxVQUFVLEdBQXFCLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRW5ELGdHQUFnRztBQUNoRyxpR0FBaUc7QUFDakcsOEZBQThGO0FBQ2pGLFFBQUEsY0FBYyxHQUFHLDZDQUE2QyxDQUFDO0FBQzVFLFNBQWdCLG1CQUFtQixDQUFDLFdBQXlDO0lBQ3pFLElBQUksQ0FBQyxXQUFXO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDOUIsT0FBTyxXQUFXLENBQUMsQ0FBQyxDQUFDLEdBQUcsK0JBQXVCLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0FBQ2pFLENBQUM7QUFIRCxrREFHQyIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5hbGxvd2NsaWVudCA9IHRydWU7XG5cbi8vIFRoZSBpbXBvcnRhbnQgb3BlcmF0aW9ucyBvZiBhbiBhcmNoaXZlIGJ1Y2tldCAoZXh0cmFjdGVkIGZyb20gQXJjaGl2ZXNCYWNrYmxhemUpLCBzbyBvdGhlclxuLy8gYmFja2VuZHMgKGUuZy4gb3VyIG93biByZW1vdGUgc3RvcmFnZSBzZXJ2ZXIpIGNhbiBiZSB1c2VkIGludGVyY2hhbmdlYWJseS5cblxuLy8gQSB3cml0ZSBtYXkgbm90IGJlIHN0YW1wZWQgbW9yZSB0aGFuIHRoaXMgZmFyIGluIHRoZSBmdXR1cmUsIG9yIGNsb2NrIHNrZXcgYmV0d2VlbiBtYWNoaW5lc1xuLy8gd291bGQgbGV0IGEgYmFkIHRpbWVzdGFtcCBibG9jayB3cml0ZXMgZm9yIGEgbG9uZyB0aW1lLlxuZXhwb3J0IGNvbnN0IE1BWF9MQVNUX01PRElGSUVEX0ZVVFVSRSA9IDE1ICogNjAgKiAxMDAwO1xuXG4vLyBIb3cgbG9uZyBicm93c2VycyBtYXkgY2FjaGUgZmlsZXMgZnJvbSBpbW11dGFibGUgYnVja2V0cyAodGhlIENhY2hlLUNvbnRyb2wgbWF4LWFnZSksIHNoYXJlZCBieVxuLy8gZXZlcnkgaG9zdGluZyBwYXRoIChiYWNrYmxhemUgYnVja2V0IHNldHRpbmdzIGFuZCBvdXIgb3duIHN0b3JhZ2Ugc2VydmVyJ3MgSFRUUCByb3V0ZSlcbmV4cG9ydCBjb25zdCBJTU1VVEFCTEVfQ0FDSEVfVElNRSA9IDg2NDAwICogMTAwMDtcbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnRWYWxpZExhc3RNb2RpZmllZChsYXN0TW9kaWZpZWQ6IG51bWJlcik6IHZvaWQge1xuICAgIGxldCBtYXggPSBEYXRlLm5vdygpICsgTUFYX0xBU1RfTU9ESUZJRURfRlVUVVJFO1xuICAgIGlmIChsYXN0TW9kaWZpZWQgPiBtYXgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBsYXN0TW9kaWZpZWQgaXMgdG9vIGZhciBpbiB0aGUgZnV0dXJlOiAke2xhc3RNb2RpZmllZH0gPiAke21heH0gKG5vdyArIDE1IG1pbnV0ZXMpYCk7XG4gICAgfVxufVxuXG5cbmV4cG9ydCB0eXBlIFJlbW90ZUNvbmZpZyA9IHtcbiAgICAvLyBOT1RFOiBWZXJzaW9uIGlzIHVzZWQgd2hlbiB1cGRhdGluZyB0aGUgY29uZmlndXJhdGlvbi4gVGhlIG5ld2VyIHZlcnNpb24gaXMgYWx3YXlzIHRha2VuLiBBIG1pc3NpbmcgdmVyc2lvbiBjb3VudHMgYXMgdmVyc2lvbiAtMS5cbiAgICB2ZXJzaW9uPzogbnVtYmVyO1xuICAgIHNvdXJjZXM6IFJlbW90ZUNvbmZpZ0Jhc2VbXTtcbn07XG5cbi8qKlxuICAgIHN0cmluZyBhcmd1bWVudHMgd2lsbCBiZSBhIHVybCwgbG9va2luZyBsaWtlOlxuICAgICAgICBodHRwczovL3N0b3JhZ2UyLnZpZGdyaWR3ZWIuY29tOjQ0NDUvZmlsZS9leGFtcGxlYWNjb3VudC9leGFtcGxlYnVja2V0L3N0b3JhZ2Uvc3RvcmFnZXJvdXRpbmcuanNvblxuICAgICAgICBodHRwczovL2YwMDIuYmFja2JsYXplYjIuY29tL2ZpbGUvcXVlcnlzdWJ0ZXN0LWNvbS1wdWJsaWMtaW1tdXRhYmxlL3N0b3JhZ2Uvc3RvcmFnZXJvdXRpbmcuanNvblxuICAgICAgICAtIFRoZXNlIG1hcCB0byB7IHVybCB9LCB3aXRoIHRoZSB0eXBlIGluZmVycmVkIGZyb20gdGhlIHVybFxuICAgICAgICAtIEhvc3RlZCB1cmxzIGFyZSAvZmlsZS88YWNjb3VudD4vPGJ1Y2tldE5hbWU+Ly4uLiwgYmFja2JsYXplIHVybHMgYXJlIC9maWxlLzxidWNrZXROYW1lPi8uLi5cblxuICAgIE5PVEU6IElmIHdlIGRvIG5vdCBoYXZlIHJpZ2h0IGFjY2VzcyB0byB0aGVzZSwgdGhlbiBpdCBiZWNvbWVzIGEgcmVhZC1vbmx5IElBcmNoaXZlcywgd2hlcmUgd2Ugc29sZWx5IHJlYWQgdXNpbmcgdGhlIHVybCBmb3JtICh3aGljaCBtaWdodCB0aHJvdyBkdWUgdG8gbm90IGhhdmluZyBhY2Nlc3MgYXMgd2VsbCkuIFVOTEVTUyBPdXIgY29uZmlndXJhdGlvbiBleHBsaWNpdGx5IGhhcyBwdWJsaWM6IGZhbHNlLCBpbiB3aGljaCBjYXNlLCB3ZSBkb24ndCBldmVuIGhpdCB0aGUgVVJMIGFuZCB3ZSB0aHJvdyBvbiBhY2Nlc3MuXG5cbiAgICBOT1RFOiBJZiB3ZSdyZSBpbiB0aGUgYnJvd3Nlciwgd2Ugc2hvdWxkIGFsbG93IGRvd25sb2FkaW5nIHRoZSBmaWxlcyB2aWEgdGhlIFVSTCBmb3JtIChpZiBpdCdzIGEgcHVibGljIGJ1Y2tldCksIGhvd2V2ZXIsIHdlIHdvbid0IGFsbG93IHdyaXRpbmcsIGJlY2F1c2UgdGhlaXIgc2VydmVycyBkbyBub3QgYWxsb3cgc2VjdXJlIGJyb3dzZXIgd3JpdGVzLlxuKi9cbmV4cG9ydCB0eXBlIFJlbW90ZUNvbmZpZ0Jhc2UgPSBzdHJpbmcgfCBIb3N0ZWRDb25maWcgfCBCYWNrYmxhemVDb25maWc7XG5cbmV4cG9ydCB0eXBlIENvbW1vbkNvbmZpZyA9IHtcbiAgICAvKiogQnkgZGVmYXVsdCBhIHNlcnZlciBob3N0aW5nIHRoaXMgYnVja2V0IGVhZ2VybHkgY29waWVzIHRoaXMgc291cmNlJ3MgZnVsbCBjb250ZW50cyBvbnRvIGl0cyBvd24gZGlzayAob24gdG9wIG9mIHRoZSBsYXp5IHJlYWQtdGhyb3VnaCBjYWNoaW5nKS4gU2V0IHRoaXMgdG8gYmUgYSBmcm9udCBlbmQgZm9yIGEgdmVyeSBsYXJnZSBkYXRhYmFzZSB3aXRob3V0IGNvcHlpbmcgdGhlIGZ1bGwgZGF0YWJhc2UgLSByZWFkcyBzdGlsbCBkb3duLWNhY2hlIGluZGl2aWR1YWwgZmlsZXMgb24gZGVtYW5kLiAqL1xuICAgIG5vRnVsbFN5bmM/OiBib29sZWFuO1xuICAgIC8qKiBCeXRlcyBvZiByZWFkLWNhY2hlIHRoaXMgc2VydmVyJ3MgZGlzayBtYXkgaG9sZDsgbGVhc3QtcmVjZW50bHktdXNlZCBmaWxlcyBhcmUgZGVsZXRlZCBmcm9tIGRpc2sgdG8gc3RheSB1bmRlciBpdCAob25seSBldmVyIHdoZW4gYW5vdGhlciBzb3VyY2UgdmVyaWZpYWJseSBob2xkcyB0aGUgZmlsZSAtIHRoZSBvbmx5IGNvcHkgaXMgbmV2ZXIgZGVsZXRlZCkuIFJlcXVpcmVzIG5vRnVsbFN5bmMgKGEgZnVsbCBjb3B5IGNhbid0IGJlIGJvdW5kZWQpLiAqL1xuICAgIHJlYWRlckRpc2tMaW1pdD86IG51bWJlcjtcbiAgICAvKiogVGhlIHdyaXRlIHRpbWVzIChbc3RhcnRNcywgZW5kTXNdKSB0aGlzIHNvdXJjZSBpcyB2YWxpZCBmb3IgKHNlZSBBcmNoaXZlc1NvdXJjZS52YWxpZFdpbmRvdyBmb3IgdGhlIHN5bmNocm9uaXphdGlvbiBzZW1hbnRpY3MpLiBSZXF1aXJlZCBvbiBvYmplY3QgY29uZmlnczogY29uZmlndXJhdGlvbiBjaGFuZ2VzIG11c3QgYmUgU0NIRURVTEVEIChhIG5ldyBzb3VyY2UgYmVjb21lcyB2YWxpZCBhdCBhIGZ1dHVyZSB0aW1lIHdoaWxlIHRoZSBvbGQgb25lJ3Mgd2luZG93IGVuZHMpLCBub3QgZmxpcHBlZCBpbnN0YW50bHkuIFBsYWluIFVSTC1zdHJpbmcgc291cmNlcyBkZWZhdWx0IHRvIEZVTExfVkFMSURfV0lORE9XIC0gb25jZSB5b3UncmUgd3JpdGluZyBvYmplY3QgY29uZmlncywgeW91J3JlIGRvaW5nIHNvbWV0aGluZyBjb21wbGljYXRlZCBlbm91Z2ggdG8gdGhpbmsgYWJvdXQgd2hlbiB0aGluZ3MgY2hhbmdlLiAqL1xuICAgIHZhbGlkV2luZG93OiBbbnVtYmVyLCBudW1iZXJdO1xuICAgIC8qKiBTaGFyZGluZzogdGhlIGZyYWN0aW9uIG9mIHRoZSBrZXkgc3BhY2UgdGhpcyBzb3VyY2UgaGFuZGxlcywgYXMgW3N0YXJ0LCBlbmQpIG92ZXIgWzAsIDEpIChrZXlzIGFyZSByb3V0ZWQgYnkgZ2V0Um91dGUgaW4gcmVtb3RlQ29uZmlnLnRzKS4gRGVmYXVsdHMgdG8gRlVMTF9ST1VURSAodW5zaGFyZGVkKS4gQXQgZXZlcnkgcG9pbnQgaW4gdGltZSB0aGUgc291cmNlcycgcm91dGVzIG11c3QgZnVsbHkgY292ZXIgWzAsIDEpLCBvciBzb21lIGtleXMgY291bGQgbmV2ZXIgYmUgcmVhZC4gKi9cbiAgICByb3V0ZT86IFtudW1iZXIsIG51bWJlcl07XG59O1xuXG5leHBvcnQgdHlwZSBIb3N0ZWRDb25maWcgPSBDb21tb25Db25maWcgJiB7XG4gICAgdHlwZTogXCJyZW1vdGVcIjtcblxuICAgIC8vIEV4OiBodHRwczovLzk5LTI1MC0xMjQtOTEucXVlcnlzdWJ0ZXN0LmNvbTo1MjMzL2ZpbGUvcm9vdC91bmlxdWVidWNrZXRuYW1lL3N0b3JhZ2Uvc3RvcmFnZXJvdXRpbmcuanNvblxuICAgIC8vIE5PVEU6IFRoZSBhY2NvdW50IGFuZCBidWNrZXQgbmFtZSBhcmUgb2J0YWluZWQgZnJvbSB0aGUgVVJMLlxuICAgIHVybDogc3RyaW5nO1xuXG4gICAgLy8gTk9URTogQXV0aGVudGljYXRpb24gaXMgaGFuZGxlZCBieSBjZXJ0LnRzLCB2aWEgaGF2aW5nIHlvdXIgbWFjaGluZSB0cnVzdGVkIHRvIGFjY2VzcyB0aGlzIGFjY291bnQuIFxuICAgIGFjY291bnROYW1lPzogc3RyaW5nO1xuXG4gICAgcHVibGljPzogYm9vbGVhbjtcbiAgICAvLyBGYXN0IG1vZGU6IHRoZSBzZXJ2ZXIgYWNrbm93bGVkZ2VzIHdyaXRlcyBvbmNlIHRoZXkgYXJlIGluIG1lbW9yeSwgZmx1c2hpbmcgdG8gZGlzayBhZnRlclxuICAgIC8vIHdyaXRlRGVsYXkgKGRlZmF1bHQgNSBtaW51dGVzKSBhbmQgY29hbGVzY2luZyB3cml0ZXMgdG8gdGhlIHNhbWUgZmlsZS4gQSBzZXJ2ZXIgY3Jhc2ggbG9zZXNcbiAgICAvLyB3cml0ZXMgdGhhdCBoYXZlbid0IGZsdXNoZWQgeWV0LlxuICAgIGZhc3Q/OiBib29sZWFuO1xuICAgIHdyaXRlRGVsYXk/OiBudW1iZXI7XG4gICAgLy8gVGhlIGJ1Y2tldCBpcyBzZXJ2ZWQgc3RyYWlnaHQgZnJvbSB0aGUgc2VydmVyJ3MgZGlzaywgd2l0aCBubyBpbmRleCDigJQgc28gbm8gZmFzdCB3cml0ZXMgYW5kXG4gICAgLy8gbm8gZ2V0Q2hhbmdlc0FmdGVyL2dldFN5bmNTdGF0dXMuXG4gICAgcmF3RGlzaz86IGJvb2xlYW47XG4gICAgLy8gV3JpdGVzIHRvIHBhdGhzIHRoYXQgYWxyZWFkeSBleGlzdCBhcmUgZGlzYWxsb3dlZCAoZGVsZXRlcyBzdGlsbCB3b3JrKS5cbiAgICBpbW11dGFibGU/OiBib29sZWFuO1xufTtcblxuZXhwb3J0IHR5cGUgQmFja2JsYXplQ29uZmlnID0gQ29tbW9uQ29uZmlnICYge1xuICAgIHR5cGU6IFwiYmFja2JsYXplXCI7XG4gICAgLy8gRXg6IGh0dHBzOi8vZjAwMi5iYWNrYmxhemViMi5jb20vZmlsZS9xdWVyeXN1YnRlc3QtY29tLXB1YmxpYy1pbW11dGFibGUvc3RvcmFnZS9zdG9yYWdlcm91dGluZy5qc29uXG4gICAgLy8gTk9URTogVGhlIGJ1Y2tldCBuYW1lIGlzIG9idGFpbmVkIGZyb20gdGhlIFVSTC5cbiAgICB1cmw6IHN0cmluZztcbiAgICAvLyBQdWJsaWMgYnVja2V0cyBhcmUgc2VydmVkIG92ZXIgcGxhaW4gSFRUUFMgR0VUcyAoZ2V0VVJMKS4gUHJpdmF0ZSBidWNrZXRzIGFyZSBBUEktYWNjZXNzIG9ubHkuXG4gICAgcHVibGljPzogYm9vbGVhbjtcbiAgICAvLyBOT1RFOiBUaGlzIGlzbid0IGVuZm9yY2VkIG9uIHRoZSBiYWNrYmxhemUgbGV2ZWwsIHNvIHRoaXMgaXMganVzdCBhIGNsaWVudC1zaWRlIGd1YXJhbnRlZS4gVGhpcyBjYW4gY2hhbmdlIGhvdyB3ZSBjYWNoZSBmaWxlcy5cbiAgICAvLyAgLSBCYWNrYmxhemUgZG9lcyBzdXBwb3J0IGltbXV0YWJpbGl0eS4gSG93ZXZlciwgYXBwYXJlbnRseSwgb25jZSB3ZSBlbmFibGUgaXQgb24gYSBidWNrZXQsIHdlIGNhbid0IGRpc2FibGUgaXQsIHdoaWNoIGlzIHJlYWxseSBiYWQsIGFzIGl0IG1lYW5zIGlmIG91ciBjb2RlIGNvdWxkIGV2ZXIgZW5hYmxlIGl0IGFuZCB3ZSBhY2NpZGVudGFsbHkgZW5hYmxlIGl0IG9uIGFuIGltcG9ydGFudCBidWNrZXQsIHdlIGVzc2VudGlhbGx5IGp1c3QgYnJpY2tlZCB0aGF0IGJ1Y2tldC4gU28gd2Ugc2hvdWxkIG5ldmVyIHdyaXRlIGFueSBjb2RlIHRoYXQgZXZlciB0cmllcyB0byB1c2UgYmFja2JsYXplIHRvIG1ha2UgdGhpbmdzIGltbXV0YWJsZS4gXG4gICAgaW1tdXRhYmxlPzogYm9vbGVhbjtcbiAgICAvLyBDT1JTIG9yaWdpbnMgYWxsb3dlZCB0byBjb25zdW1lIHRoZSBidWNrZXQncyBmaWxlcyBpbiBhIGJyb3dzZXIuIE5vdCBhIHNlY3VyaXR5IGJvdW5kYXJ5XG4gICAgLy8gKGFjY2VzcyBpcyBnYXRlZCBieSB0aGUgQVBJIGtleSAvIHNpZ25lZCBVUkxzLCBuZWl0aGVyIG9mIHdoaWNoIHJpZGVzIGluIGNvb2tpZXMpIC0gaXQgb25seVxuICAgIC8vIGNvbnRyb2xzIHdoaWNoIHNpdGVzJyBpbi1wYWdlIEphdmFTY3JpcHQgY2FuIHJlYWQgcmVzcG9uc2VzLiBEZWZhdWx0cyB0byBhbnkgSFRUUFMgb3JpZ2luLlxuICAgIGFsbG93ZWRPcmlnaW5zPzogc3RyaW5nW107XG5cbiAgICAvLyBOT1RFOiBXZSB3aWxsIGFjY2VzcyB0aGUgYXBpIGtleSBmcm9tIGdldFNlY3JldCwgc2VlIGJhY2tibGF6ZS50cyBmb3IgdGhlIHNwZWNpZmljIGtleXMuXG59O1xuXG5cbmV4cG9ydCBjb25zdCBGVUxMX1ZBTElEX1dJTkRPVzogW251bWJlciwgbnVtYmVyXSA9IFswLCBOdW1iZXIuTUFYX1NBRkVfSU5URUdFUl07XG5cblxuXG5cbi8vIGNyZWF0ZVRpbWUgaXMgYSBtaXNub21lciBrZXB0IGZvciBjb21wYXRpYmlsaXR5IOKAlCBpdCBpcyByZWFsbHkgdGhlIExBU1QtV1JJVEUgdGltZSwgc2FtZSBhc1xuLy8gZ2V0SW5mbydzIHdyaXRlVGltZS4gTmVpdGhlciBCYWNrYmxhemUgbm9yIG91ciByZW1vdGUgc3RvcmFnZSB0cmFja3MgYSBkaXN0aW5jdCBjcmVhdGlvbiBkYXRlOlxuLy8gZWFjaCB3cml0ZSBzdGFtcHMgYSBmcmVzaCB0aW1lc3RhbXAgb24gdGhlIGN1cnJlbnQgdmVyc2lvbiwgc28gYm90aCBmaWVsZHMgYXJlIGp1c3QgXCJ3aGVuIHRoZVxuLy8gYnl0ZXMgc2VydmVkIGJ5IGdldCgpIHdlcmUgbW9zdCByZWNlbnRseSB3cml0dGVuXCIuXG5leHBvcnQgdHlwZSBBcmNoaXZlRmlsZUluZm8gPSB7IHBhdGg6IHN0cmluZzsgY3JlYXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfTtcblxuLy8gQW4gaW4tcHJvZ3Jlc3MgYmFja2dyb3VuZCBzeW5jaHJvbml6YXRpb24gdGFzayAoc2VlIEFyY2hpdmVzQ29uZmlnLnN5bmNpbmcpXG5leHBvcnQgdHlwZSBTeW5jQWN0aXZpdHkgPSB7XG4gICAgLy8gQSBtZXRhZGF0YSBzY2FuIGlzIGEgc2luZ2xlIGxpc3RpbmcgY2FsbCwgc28gaXQgaGFzIG5vIGluY3JlbWVudGFsIHByb2dyZXNzIC0ganVzdCB0aGF0IGl0J3NcbiAgICAvLyBydW5uaW5nIGFuZCBzaW5jZSB3aGVuLiBBIGZ1bGwgc3luYyBrbm93cyBpdHMgZXhhY3QgZmlsZS9ieXRlIHByb2dyZXNzLlxuICAgIHR5cGU6IFwibWV0YWRhdGFTY2FuXCIgfCBcImZ1bGxTeW5jXCI7XG4gICAgc291cmNlRGVidWdOYW1lOiBzdHJpbmc7XG4gICAgc3RhcnRUaW1lOiBudW1iZXI7XG4gICAgZG9uZUZpbGVzPzogbnVtYmVyO1xuICAgIHRvdGFsRmlsZXM/OiBudW1iZXI7XG4gICAgZG9uZUJ5dGVzPzogbnVtYmVyO1xuICAgIHRvdGFsQnl0ZXM/OiBudW1iZXI7XG59O1xuXG5leHBvcnQgdHlwZSBBcmNoaXZlc0NvbmZpZyA9IHtcbiAgICAvLyBXaGV0aGVyIGdldENoYW5nZXNBZnRlciBpcyBpbXBsZW1lbnRlZCAoZmFzdCBjaGFuZ2UgcG9sbGluZywgaW5zdGVhZCBvZiBmdWxsIHJlc2NhbnMpXG4gICAgc3VwcG9ydHNDaGFuZ2VzQWZ0ZXI/OiBib29sZWFuO1xuICAgIC8vIFRoZSBidWNrZXQncyBmdWxsIHJvdXRpbmcgY29uZmlnIChST1VUSU5HX0ZJTEUpLiBBYnNlbnQgZm9yIHNvdXJjZXMgdGhhdCBkb24ndCBoYXZlIG9uZSAoYVxuICAgIC8vIGJhcmUgZGlzayBzb3VyY2UsIG9yIGEgYnVja2V0IHRoYXQgZG9lc24ndCBleGlzdCB5ZXQpLlxuICAgIHJlbW90ZUNvbmZpZz86IFJlbW90ZUNvbmZpZztcbiAgICAvLyBMaXZlIGluZGV4IHRvdGFscyAodG9tYnN0b25lcyBleGNsdWRlZCksIGtlcHQgdXAgdG8gZGF0ZSBpbiBtZW1vcnkgb24gZXZlcnkgbXV0YXRpb24gYW5kXG4gICAgLy8gcmVjb21wdXRlZCBvbiBsb2FkIC0gc28gYW55IGRyaWZ0IGhlYWxzIG9uIHJlc3RhcnRcbiAgICBpbmRleD86IHsgZmlsZUNvdW50OiBudW1iZXI7IGJ5dGVDb3VudDogbnVtYmVyIH07XG4gICAgLy8gVGhlIHNhbWUgdG90YWxzIGJyb2tlbiBkb3duIGJ5IHdoaWNoIHNvdXJjZSBjdXJyZW50bHkgaG9sZHMgZWFjaCBmaWxlJ3MgYnl0ZXMgKHRoZSBmaXJzdFxuICAgIC8vIGVudHJ5IGlzIHRoZSBzZXJ2ZXIncyBvd24gZGlzaylcbiAgICBpbmRleFNvdXJjZXM/OiB7IGRlYnVnTmFtZTogc3RyaW5nOyBmaWxlQ291bnQ6IG51bWJlcjsgYnl0ZUNvdW50OiBudW1iZXIgfVtdO1xuICAgIC8vIFRoZSBzZXJ2ZXIncyBjb25maWd1cmVkIHJlYWRlckRpc2tMaW1pdCwgd2hlbiBpdCBydW5zIGFzIGEgYm91bmRlZCByZWFkIGNhY2hlXG4gICAgcmVhZGVyRGlza0xpbWl0PzogbnVtYmVyO1xuICAgIC8vIEJhY2tncm91bmQgc3luY2hyb25pemF0aW9uIGN1cnJlbnRseSBpbiBwcm9ncmVzcyAoZW1wdHkgd2hlbiBpZGxlKVxuICAgIHN5bmNpbmc/OiBTeW5jQWN0aXZpdHlbXTtcbn07XG5cbi8vIEEgc3luY2hyb25pemF0aW9uIHNvdXJjZSBvZiBhIEJsb2JTdG9yZSAod2hpY2ggc3luY2hyb25pemVzIGFuIGluZGV4ICsgbG9jYWwgY2FjaGUgZnJvbSB0aGVtKVxuZXhwb3J0IHR5cGUgQXJjaGl2ZXNTb3VyY2UgPSB7XG4gICAgc291cmNlOiBJQXJjaGl2ZXM7XG4gICAgLy8gRnJvbSB0aGUgc291cmNlJ3MgQ29tbW9uQ29uZmlnLiBWYWx1ZXMgd2l0aCB3cml0ZSB0aW1lcyBvdXRzaWRlIHRoZSB3aW5kb3cgYXJlIGlnbm9yZWQgd2hlblxuICAgIC8vIHNjYW5uaW5nLCBhbmQgb25jZSBpdHMgZW5kIGlzIHN1ZmZpY2llbnRseSBwYXN0IChzZWUgd2luZG93QWNjZXB0c1dyaXRlcykgdGhlIHNvdXJjZSBzdG9wc1xuICAgIC8vIHJlY2VpdmluZyB3cml0ZXMgZW50aXJlbHkgLSBzdGlsbCBzY2FubmVkIChpdCBob2xkcyB0aGUgYXV0aG9yaXRhdGl2ZSBkYXRhIGZvciBpdHMgd2luZG93KSxcbiAgICAvLyBpdCBqdXN0IHN0b3BzIGdyb3dpbmcuXG4gICAgdmFsaWRXaW5kb3c6IFtudW1iZXIsIG51bWJlcl07XG4gICAgLy8gRnJvbSB0aGUgc291cmNlJ3MgQ29tbW9uQ29uZmlnIChpbnRlcnNlY3RlZCB3aXRoIHRoZSBvd25pbmcgc3RvcmUncyBvd24gcm91dGUpOiBvbmx5IGtleXNcbiAgICAvLyByb3V0aW5nIGludG8gW3N0YXJ0LCBlbmQpIGFyZSBhY2NlcHRlZCBmcm9tIHRoaXMgc291cmNlJ3Mgc2NhbnMgYW5kIHNlbnQgdG8gaXQgaW5cbiAgICAvLyB3cml0ZXMvcmVjb25jaWxpYXRpb24uIFRoZSByb3V0aW5nIGZpbGUgaXMgZXhlbXB0IC0gY29uZmlnIGZsb3dzIGV2ZXJ5d2hlcmUuIEFic2VudCA9IGFsbCBrZXlzLlxuICAgIHJvdXRlPzogW251bWJlciwgbnVtYmVyXTtcbiAgICAvLyBGcm9tIHRoZSBzb3VyY2UncyBDb21tb25Db25maWc7IHNlZSB0aGVyZS5cbiAgICBub0Z1bGxTeW5jPzogYm9vbGVhbjtcbiAgICAvLyBTdGFibGUgaWRlbnRpdHkgb2YgdGhlIHVuZGVybHlpbmcgZW5kcG9pbnQgKGl0cyBjb25maWcgd2l0aCB3aW5kb3dzL3JvdXRlcyBzdHJpcHBlZCkgLSBob3dcbiAgICAvLyBCbG9iU3RvcmUudXBkYXRlU291cmNlcyByZWNvZ25pemVzIGEgc291cmNlIGFjcm9zcyBjb25maWcgY2hhbmdlcyBzbyBpdCBjYW4gdXBkYXRlIGl0IGluXG4gICAgLy8gcGxhY2UgaW5zdGVhZCBvZiByZW1vdmluZyBhbmQgcmUtYWRkaW5nIGl0XG4gICAgaWRlbnRpdHk/OiBzdHJpbmc7XG59O1xuXG4vLyBUaGUgZ3JhY2UgY292ZXJzIGNsb2NrIHVuY2VydGFpbnR5IGFyb3VuZCB0aGUgd2luZG93IGVkZ2U6IGEgd3JpdGUgc2xpZ2h0bHkgcGFzdCB0aGUgZW5kIGRvZXNuJ3Rcbi8vIGh1cnQsIGJ1dCBvbmNlIGNsZWFybHkgcGFzdCwgdGhlIHNvdXJjZSBpcyBhbiBhcmNoaXZlIGFuZCBtdXN0IHN0b3AgZ3Jvd2luZy5cbmV4cG9ydCBjb25zdCBXUklURV9QQVNUX1dJTkRPV19HUkFDRSA9IDUgKiA2MCAqIDEwMDA7XG5cbi8vIEVycm9yIG1hcmtlciBhIHNlcnZlciBpbmNsdWRlcyB3aGVuIGEgZnJlc2hseS1zdGFtcGVkIHdyaXRlIHJlYWNoZXMgaXQgb3V0c2lkZSBpdHMgdmFsaWQgd2luZG93c1xuLy8gKHRoZSBjbGllbnQgcmVzb2x2ZWQgaXRzIHRhcmdldCwgdGhlbiB0aW1lIGNyb3NzZWQgYSB3aW5kb3cgYm91bmRhcnkgYmVmb3JlIHRoZSB3cml0ZSBsYW5kZWQpLlxuLy8gQ2xpZW50cyBkZXRlY3QgdGhpcyBtYXJrZXIgYW5kIHJlLXJlc29sdmUgdGhlIGN1cnJlbnRseS12YWxpZCBzb3VyY2UsIHJldHJ5aW5nIE9OQ0UgLSBib3VuZGFyaWVzXG4vLyBhcmUgZmFyIGFwYXJ0LCBzbyBoaXR0aW5nIGl0IHR3aWNlIGluIG9uZSBhdHRlbXB0IG1lYW5zIHNvbWV0aGluZyBpcyBhY3R1YWxseSB3cm9uZy5cbmV4cG9ydCBjb25zdCBTVE9SQUdFX1dST05HX1ZBTElEX1dJTkRPVyA9IFwiUkVNT1RFX1NUT1JBR0VfV1JPTkdfVkFMSURfV0lORE9XX2E3YzFmMDRlXCI7XG5cbi8vIEVycm9yIG1hcmtlciBhIHNlcnZlciBpbmNsdWRlcyB3aGVuIGEgZnJlc2hseS1zdGFtcGVkIHdyaXRlJ3Mga2V5IHJvdXRlcyBvdXRzaWRlIHRoZSBzaGFyZHMgdGhpc1xuLy8gc2VydmVyIGhhbmRsZXMgKHRoZSBjbGllbnQncyBjb25maWcgZGlzYWdyZWVzIHdpdGggdGhlIHNlcnZlcidzIC0gY2xpZW50cyByZS1yZXNvbHZlIG9uY2UpXG5leHBvcnQgY29uc3QgU1RPUkFHRV9XUk9OR19ST1VURSA9IFwiUkVNT1RFX1NUT1JBR0VfV1JPTkdfUk9VVEVfYzk0ZDJlMTdcIjtcblxuZXhwb3J0IGNvbnN0IEZVTExfUk9VVEU6IFtudW1iZXIsIG51bWJlcl0gPSBbMCwgMV07XG5cbi8vIEEga2V5IGNvbnRhaW5pbmcgdGhpcyBzZW50aW5lbCBkb2Vzbid0IGhhdmUgYSBmaXhlZCBzaGFyZDogc2V0VmFyaWFibGVTaGFyZCBwaWNrcyB0aGUgKGxvd2VzdFxuLy8gbGF0ZW5jeSwgdXApIHdyaXRlIHNoYXJkLCBhcHBlbmRzIFwiXzx2YWx1ZSBpbiB0aGUgc2hhcmQncyByb3V0ZT5cIiBkaXJlY3RseSBhZnRlciB0aGUgc2VudGluZWwsXG4vLyBhbmQgcmV0dXJucyB0aGUgbWF0ZXJpYWxpemVkIGtleS4gZ2V0Um91dGUgdHJlYXRzIHRoYXQgc3VmZml4IGFzIGEgY29tcGxldGUgcm91dGUgb3ZlcnJpZGUuXG5leHBvcnQgY29uc3QgVkFSSUFCTEVfU0hBUkQgPSBcIlZBUklBQkxFX1NIQVJEX2YwMjM0amZhaDA4Zmd5aGZneXNzZGRzODNubXBcIjtcbmV4cG9ydCBmdW5jdGlvbiB3aW5kb3dBY2NlcHRzV3JpdGVzKHZhbGlkV2luZG93OiBbbnVtYmVyLCBudW1iZXJdIHwgdW5kZWZpbmVkKTogYm9vbGVhbiB7XG4gICAgaWYgKCF2YWxpZFdpbmRvdykgcmV0dXJuIHRydWU7XG4gICAgcmV0dXJuIHZhbGlkV2luZG93WzFdICsgV1JJVEVfUEFTVF9XSU5ET1dfR1JBQ0UgPiBEYXRlLm5vdygpO1xufVxuXG5leHBvcnQgdHlwZSBBcmNoaXZlc1N5bmNTb3VyY2VTdGF0dXMgPSB7XG4gICAgZGVidWdOYW1lOiBzdHJpbmc7XG4gICAgdmFsaWRXaW5kb3c6IFtudW1iZXIsIG51bWJlcl07XG4gICAgcm91dGU/OiBbbnVtYmVyLCBudW1iZXJdO1xuICAgIG5vRnVsbFN5bmM/OiBib29sZWFuO1xuICAgIHN1cHBvcnRzQ2hhbmdlc0FmdGVyOiBib29sZWFuO1xuICAgIGluaXRpYWxTY2FuQ29tcGxldGU6IGJvb2xlYW47XG4gICAgLy8gRmlsZXMgc2VlbiBpbiB0aGlzIHNvdXJjZSdzIHNjYW5zIC8gY2hhbmdlIHBvbGxzIHNvIGZhclxuICAgIHNjYW5uZWRDb3VudDogbnVtYmVyO1xufTtcbmV4cG9ydCB0eXBlIEFyY2hpdmVzU3luY1N0YXR1cyA9IHtcbiAgICBhbGxTY2Fuc0NvbXBsZXRlOiBib29sZWFuO1xuICAgIC8vIE51bWJlciBvZiBmaWxlcyBpbiB0aGUgaW5kZXhcbiAgICBpbmRleFNpemU6IG51bWJlcjtcbiAgICBzb3VyY2VzOiBBcmNoaXZlc1N5bmNTb3VyY2VTdGF0dXNbXTtcbn07XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUFyY2hpdmVzIHtcbiAgICBnZXREZWJ1Z05hbWUoKTogc3RyaW5nO1xuICAgIC8qKiBXaGV0aGVyIHdyaXRlcyB3b3VsZCBiZSBhY2NlcHRlZCAoY3JlZGVudGlhbHMgZXhpc3QsIHRoZSBhY2NvdW50IHRydXN0cyB0aGlzIG1hY2hpbmUsIGV0YykuXG4gICAgICogIENoZWNrZWQgd2l0aG91dCB3cml0aW5nIGFueXRoaW5nLiAqL1xuICAgIGhhc1dyaXRlQWNjZXNzKCk6IFByb21pc2U8Ym9vbGVhbj47XG4gICAgZ2V0KGZpbGVOYW1lOiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8QnVmZmVyIHwgdW5kZWZpbmVkPjtcbiAgICBnZXQyKGZpbGVOYW1lOiBzdHJpbmcsIGNvbmZpZz86IHsgcmFuZ2U/OiB7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0gfSk6IFByb21pc2U8eyBkYXRhOiBCdWZmZXI7IHdyaXRlVGltZTogbnVtYmVyOyBzaXplOiBudW1iZXIgfSB8IHVuZGVmaW5lZD47XG4gICAgLyoqXG4gICAgICogbGFzdE1vZGlmaWVkIHN0YW1wcyB0aGUgd3JpdGUgd2l0aCB0aGF0IGxhc3Qtd3JpdGUgdGltZSBpbnN0ZWFkIG9mIG5vdy4gSWYgaXQgaXMgT0xERVIgdGhhblxuICAgICAqIHRoZSBmaWxlJ3MgY3VycmVudCBsYXN0LXdyaXRlIHRpbWUgdGhlIHdyaXRlIG5vLW9wcyAoc28gZGVsYXllZCAvIHN5bmNocm9uaXplZCB3cml0ZXMgY2FuXG4gICAgICogbmV2ZXIgY2xvYmJlciBuZXdlciBkYXRhKS4gVGltZXMgbW9yZSB0aGFuIDE1IG1pbnV0ZXMgaW4gdGhlIGZ1dHVyZSBhcmUgcmVqZWN0ZWQuXG4gICAgICpcbiAgICAgKiBSZXR1cm5zIHRoZSBmdWxsIGtleSBhY3R1YWxseSB3cml0dGVuIC0gaWRlbnRpY2FsIHRvIGZpbGVOYW1lLCBFWENFUFQgZm9yIGtleXMgY29udGFpbmluZ1xuICAgICAqIFZBUklBQkxFX1NIQVJELCB3aGVyZSB0aGUgc2hhcmQgdmFsdWUgaXMgbWF0ZXJpYWxpemVkIGludG8gdGhlIGtleSAocGlja2VkIGJ5IHNoYXJkIGxhdGVuY3ksXG4gICAgICogc2VlIEFyY2hpdmVzQ2hhaW4pIGFuZCB0aGUgY2FsbGVyIG5lZWRzIHRoZSByZXR1cm5lZCBrZXkgdG8gZXZlciByZWFkIHRoZSB2YWx1ZSBiYWNrLlxuICAgICAqL1xuICAgIHNldChmaWxlTmFtZTogc3RyaW5nLCBkYXRhOiBCdWZmZXIsIGNvbmZpZz86IHsgbGFzdE1vZGlmaWVkPzogbnVtYmVyIH0pOiBQcm9taXNlPHN0cmluZz47XG4gICAgZGVsKGZpbGVOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+O1xuICAgIC8qKiBTdHJlYW1zIGEgZmlsZSB0b28gbGFyZ2UgdG8gaG9sZCBpbiBtZW1vcnkuIGdldE5leHREYXRhIHJldHVybnMgdW5kZWZpbmVkIHdoZW4gZG9uZS4gKi9cbiAgICBzZXRMYXJnZUZpbGUoY29uZmlnOiB7IHBhdGg6IHN0cmluZzsgZ2V0TmV4dERhdGEoKTogUHJvbWlzZTxCdWZmZXIgfCB1bmRlZmluZWQ+IH0pOiBQcm9taXNlPHZvaWQ+O1xuICAgIC8qKiB3cml0ZVRpbWUgaXMgdGhlIGxhc3Qtd3JpdGUgdGltZSDigJQgc2VlIEFyY2hpdmVGaWxlSW5mby5jcmVhdGVUaW1lLCB3aGljaCBpcyB0aGUgc2FtZSB2YWx1ZS4gKi9cbiAgICBnZXRJbmZvKGZpbGVOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlciB9IHwgdW5kZWZpbmVkPjtcbiAgICBmaW5kKHByZWZpeDogc3RyaW5nLCBjb25maWc/OiB7IHNoYWxsb3c/OiBib29sZWFuOyB0eXBlOiBcImZpbGVzXCIgfCBcImZvbGRlcnNcIiB9KTogUHJvbWlzZTxzdHJpbmdbXT47XG4gICAgZmluZEluZm8ocHJlZml4OiBzdHJpbmcsIGNvbmZpZz86IHsgc2hhbGxvdz86IGJvb2xlYW47IHR5cGU6IFwiZmlsZXNcIiB8IFwiZm9sZGVyc1wiIH0pOiBQcm9taXNlPEFyY2hpdmVGaWxlSW5mb1tdPjtcbiAgICAvKiogT25seSB3b3JrcyBmb3IgcHVibGljIGJ1Y2tldHMgKHByaXZhdGUgYnVja2V0cyBhcmUgQVBJLWFjY2VzcyBvbmx5KS4gKi9cbiAgICBnZXRVUkwocGF0aDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+O1xuICAgIC8qKiBUaGUgYnVja2V0J3MgY29uZmlndXJhdGlvbiwgd2hpY2ggdGVsbHMgd2hldGhlciB0aGUgb3B0aW9uYWwgZnVuY3Rpb25zIGFyZSBzdXBwb3J0ZWQuICovXG4gICAgZ2V0Q29uZmlnKCk6IFByb21pc2U8QXJjaGl2ZXNDb25maWc+O1xuICAgIC8qKlxuICAgICAqIEFsbCBmaWxlcyBjaGFuZ2VkIGFmdGVyIHRoZSBnaXZlbiB0aW1lLiBPbmx5IGV4aXN0cyB3aGVuIGdldENvbmZpZygpLnN1cHBvcnRzQ2hhbmdlc0FmdGVyO1xuICAgICAqIGJhY2tlZCBieSBhbiBpbmRleCwgc28gaXQgaXMgZmFzdCAodW5saWtlIGEgZnVsbCBmaW5kSW5mbyBzY2FuKS4gRGVsZXRpb25zIGFyZSBub3QgcmVwb3J0ZWQuXG4gICAgICovXG4gICAgZ2V0Q2hhbmdlc0FmdGVyPyh0aW1lOiBudW1iZXIpOiBQcm9taXNlPEFyY2hpdmVGaWxlSW5mb1tdPjtcbiAgICAvKiogU3luY2hyb25pemF0aW9uIGludHJvc3BlY3Rpb24sIGZvciBiYWNrZW5kcyB0aGF0IHN5bmNocm9uaXplIGZyb20gc291cmNlcyAoc2VlIEJsb2JTdG9yZSkuICovXG4gICAgZ2V0U3luY1N0YXR1cz8oKTogUHJvbWlzZTxBcmNoaXZlc1N5bmNTdGF0dXM+O1xufVxuIl19
44
- /* _JS_SOURCE_HASH = "fc4c2c00f502a1c6e908d7bb913cec4f5a86a78eeb90c93718d5181dfb7fe08a"; */
42
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSUFyY2hpdmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiSUFyY2hpdmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLE1BQU0sQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBRTFCLDZGQUE2RjtBQUM3Riw2RUFBNkU7QUFFN0UsOEZBQThGO0FBQzlGLDBEQUEwRDtBQUM3QyxRQUFBLHdCQUF3QixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBRXZELGtHQUFrRztBQUNsRyx5RkFBeUY7QUFDNUUsUUFBQSxvQkFBb0IsR0FBRyxLQUFLLEdBQUcsSUFBSSxDQUFDO0FBQ2pELFNBQWdCLHVCQUF1QixDQUFDLFlBQW9CO0lBQ3hELElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxnQ0FBd0IsQ0FBQztJQUNoRCxJQUFJLFlBQVksR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNyQixNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxZQUFZLE1BQU0sR0FBRyxxQkFBcUIsQ0FBQyxDQUFDO0lBQzFHLENBQUM7QUFDTCxDQUFDO0FBTEQsMERBS0M7QUEyRVksUUFBQSxpQkFBaUIsR0FBcUIsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7QUE4RGhGLG1HQUFtRztBQUNuRyxpR0FBaUc7QUFDakcsbUdBQW1HO0FBQ25HLHVGQUF1RjtBQUMxRSxRQUFBLDBCQUEwQixHQUFHLDRDQUE0QyxDQUFDO0FBRXZGLG1HQUFtRztBQUNuRyw2RkFBNkY7QUFDaEYsUUFBQSxtQkFBbUIsR0FBRyxxQ0FBcUMsQ0FBQztBQUU1RCxRQUFBLFVBQVUsR0FBcUIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFFbkQsZ0dBQWdHO0FBQ2hHLGlHQUFpRztBQUNqRyw4RkFBOEY7QUFDakYsUUFBQSxjQUFjLEdBQUcsNkNBQTZDLENBQUM7QUFDNUUsNkZBQTZGO0FBQzdGLHlGQUF5RjtBQUN6RixTQUFnQixtQkFBbUIsQ0FBQyxXQUF5QztJQUN6RSxJQUFJLENBQUMsV0FBVztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQzlCLE9BQU8sV0FBVyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztBQUN2QyxDQUFDO0FBSEQsa0RBR0MiLCJzb3VyY2VzQ29udGVudCI6WyJtb2R1bGUuYWxsb3djbGllbnQgPSB0cnVlO1xuXG4vLyBUaGUgaW1wb3J0YW50IG9wZXJhdGlvbnMgb2YgYW4gYXJjaGl2ZSBidWNrZXQgKGV4dHJhY3RlZCBmcm9tIEFyY2hpdmVzQmFja2JsYXplKSwgc28gb3RoZXJcbi8vIGJhY2tlbmRzIChlLmcuIG91ciBvd24gcmVtb3RlIHN0b3JhZ2Ugc2VydmVyKSBjYW4gYmUgdXNlZCBpbnRlcmNoYW5nZWFibHkuXG5cbi8vIEEgd3JpdGUgbWF5IG5vdCBiZSBzdGFtcGVkIG1vcmUgdGhhbiB0aGlzIGZhciBpbiB0aGUgZnV0dXJlLCBvciBjbG9jayBza2V3IGJldHdlZW4gbWFjaGluZXNcbi8vIHdvdWxkIGxldCBhIGJhZCB0aW1lc3RhbXAgYmxvY2sgd3JpdGVzIGZvciBhIGxvbmcgdGltZS5cbmV4cG9ydCBjb25zdCBNQVhfTEFTVF9NT0RJRklFRF9GVVRVUkUgPSAxNSAqIDYwICogMTAwMDtcblxuLy8gSG93IGxvbmcgYnJvd3NlcnMgbWF5IGNhY2hlIGZpbGVzIGZyb20gaW1tdXRhYmxlIGJ1Y2tldHMgKHRoZSBDYWNoZS1Db250cm9sIG1heC1hZ2UpLCBzaGFyZWQgYnlcbi8vIGV2ZXJ5IGhvc3RpbmcgcGF0aCAoYmFja2JsYXplIGJ1Y2tldCBzZXR0aW5ncyBhbmQgb3VyIG93biBzdG9yYWdlIHNlcnZlcidzIEhUVFAgcm91dGUpXG5leHBvcnQgY29uc3QgSU1NVVRBQkxFX0NBQ0hFX1RJTUUgPSA4NjQwMCAqIDEwMDA7XG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0VmFsaWRMYXN0TW9kaWZpZWQobGFzdE1vZGlmaWVkOiBudW1iZXIpOiB2b2lkIHtcbiAgICBsZXQgbWF4ID0gRGF0ZS5ub3coKSArIE1BWF9MQVNUX01PRElGSUVEX0ZVVFVSRTtcbiAgICBpZiAobGFzdE1vZGlmaWVkID4gbWF4KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgbGFzdE1vZGlmaWVkIGlzIHRvbyBmYXIgaW4gdGhlIGZ1dHVyZTogJHtsYXN0TW9kaWZpZWR9ID4gJHttYXh9IChub3cgKyAxNSBtaW51dGVzKWApO1xuICAgIH1cbn1cblxuXG5leHBvcnQgdHlwZSBSZW1vdGVDb25maWcgPSB7XG4gICAgLy8gTk9URTogVmVyc2lvbiBpcyB1c2VkIHdoZW4gdXBkYXRpbmcgdGhlIGNvbmZpZ3VyYXRpb24uIFRoZSBuZXdlciB2ZXJzaW9uIGlzIGFsd2F5cyB0YWtlbi4gQSBtaXNzaW5nIHZlcnNpb24gY291bnRzIGFzIHZlcnNpb24gLTEuXG4gICAgdmVyc2lvbj86IG51bWJlcjtcbiAgICBzb3VyY2VzOiBSZW1vdGVDb25maWdCYXNlW107XG59O1xuXG4vKipcbiAgICBzdHJpbmcgYXJndW1lbnRzIHdpbGwgYmUgYSB1cmwsIGxvb2tpbmcgbGlrZTpcbiAgICAgICAgaHR0cHM6Ly9zdG9yYWdlMi52aWRncmlkd2ViLmNvbTo0NDQ1L2ZpbGUvZXhhbXBsZWFjY291bnQvZXhhbXBsZWJ1Y2tldC9zdG9yYWdlL3N0b3JhZ2Vyb3V0aW5nLmpzb25cbiAgICAgICAgaHR0cHM6Ly9mMDAyLmJhY2tibGF6ZWIyLmNvbS9maWxlL3F1ZXJ5c3VidGVzdC1jb20tcHVibGljLWltbXV0YWJsZS9zdG9yYWdlL3N0b3JhZ2Vyb3V0aW5nLmpzb25cbiAgICAgICAgLSBUaGVzZSBtYXAgdG8geyB1cmwgfSwgd2l0aCB0aGUgdHlwZSBpbmZlcnJlZCBmcm9tIHRoZSB1cmxcbiAgICAgICAgLSBIb3N0ZWQgdXJscyBhcmUgL2ZpbGUvPGFjY291bnQ+LzxidWNrZXROYW1lPi8uLi4sIGJhY2tibGF6ZSB1cmxzIGFyZSAvZmlsZS88YnVja2V0TmFtZT4vLi4uXG5cbiAgICBOT1RFOiBJZiB3ZSBkbyBub3QgaGF2ZSByaWdodCBhY2Nlc3MgdG8gdGhlc2UsIHRoZW4gaXQgYmVjb21lcyBhIHJlYWQtb25seSBJQXJjaGl2ZXMsIHdoZXJlIHdlIHNvbGVseSByZWFkIHVzaW5nIHRoZSB1cmwgZm9ybSAod2hpY2ggbWlnaHQgdGhyb3cgZHVlIHRvIG5vdCBoYXZpbmcgYWNjZXNzIGFzIHdlbGwpLiBVTkxFU1MgT3VyIGNvbmZpZ3VyYXRpb24gZXhwbGljaXRseSBoYXMgcHVibGljOiBmYWxzZSwgaW4gd2hpY2ggY2FzZSwgd2UgZG9uJ3QgZXZlbiBoaXQgdGhlIFVSTCBhbmQgd2UgdGhyb3cgb24gYWNjZXNzLlxuXG4gICAgTk9URTogSWYgd2UncmUgaW4gdGhlIGJyb3dzZXIsIHdlIHNob3VsZCBhbGxvdyBkb3dubG9hZGluZyB0aGUgZmlsZXMgdmlhIHRoZSBVUkwgZm9ybSAoaWYgaXQncyBhIHB1YmxpYyBidWNrZXQpLCBob3dldmVyLCB3ZSB3b24ndCBhbGxvdyB3cml0aW5nLCBiZWNhdXNlIHRoZWlyIHNlcnZlcnMgZG8gbm90IGFsbG93IHNlY3VyZSBicm93c2VyIHdyaXRlcy5cbiovXG5leHBvcnQgdHlwZSBSZW1vdGVDb25maWdCYXNlID0gc3RyaW5nIHwgSG9zdGVkQ29uZmlnIHwgQmFja2JsYXplQ29uZmlnO1xuXG5leHBvcnQgdHlwZSBDb21tb25Db25maWcgPSB7XG4gICAgLyoqIEJ5IGRlZmF1bHQgYSBzZXJ2ZXIgaG9zdGluZyB0aGlzIGJ1Y2tldCBlYWdlcmx5IGNvcGllcyB0aGlzIHNvdXJjZSdzIGZ1bGwgY29udGVudHMgb250byBpdHMgb3duIGRpc2sgKG9uIHRvcCBvZiB0aGUgbGF6eSByZWFkLXRocm91Z2ggY2FjaGluZykuIFNldCB0aGlzIHRvIGJlIGEgZnJvbnQgZW5kIGZvciBhIHZlcnkgbGFyZ2UgZGF0YWJhc2Ugd2l0aG91dCBjb3B5aW5nIHRoZSBmdWxsIGRhdGFiYXNlIC0gcmVhZHMgc3RpbGwgZG93bi1jYWNoZSBpbmRpdmlkdWFsIGZpbGVzIG9uIGRlbWFuZC4gKi9cbiAgICBub0Z1bGxTeW5jPzogYm9vbGVhbjtcbiAgICAvKiogQnl0ZXMgb2YgcmVhZC1jYWNoZSB0aGlzIHNlcnZlcidzIGRpc2sgbWF5IGhvbGQ7IGxlYXN0LXJlY2VudGx5LXVzZWQgZmlsZXMgYXJlIGRlbGV0ZWQgZnJvbSBkaXNrIHRvIHN0YXkgdW5kZXIgaXQgKG9ubHkgZXZlciB3aGVuIGFub3RoZXIgc291cmNlIHZlcmlmaWFibHkgaG9sZHMgdGhlIGZpbGUgLSB0aGUgb25seSBjb3B5IGlzIG5ldmVyIGRlbGV0ZWQpLiBSZXF1aXJlcyBub0Z1bGxTeW5jIChhIGZ1bGwgY29weSBjYW4ndCBiZSBib3VuZGVkKS4gKi9cbiAgICByZWFkZXJEaXNrTGltaXQ/OiBudW1iZXI7XG4gICAgLyoqIFRoZSB3cml0ZSB0aW1lcyAoW3N0YXJ0TXMsIGVuZE1zXSkgdGhpcyBzb3VyY2UgaXMgdmFsaWQgZm9yIChzZWUgQXJjaGl2ZXNTb3VyY2UudmFsaWRXaW5kb3cgZm9yIHRoZSBzeW5jaHJvbml6YXRpb24gc2VtYW50aWNzKS4gUmVxdWlyZWQgb24gb2JqZWN0IGNvbmZpZ3M6IGNvbmZpZ3VyYXRpb24gY2hhbmdlcyBtdXN0IGJlIFNDSEVEVUxFRCAoYSBuZXcgc291cmNlIGJlY29tZXMgdmFsaWQgYXQgYSBmdXR1cmUgdGltZSB3aGlsZSB0aGUgb2xkIG9uZSdzIHdpbmRvdyBlbmRzKSwgbm90IGZsaXBwZWQgaW5zdGFudGx5LiBQbGFpbiBVUkwtc3RyaW5nIHNvdXJjZXMgZGVmYXVsdCB0byBGVUxMX1ZBTElEX1dJTkRPVyAtIG9uY2UgeW91J3JlIHdyaXRpbmcgb2JqZWN0IGNvbmZpZ3MsIHlvdSdyZSBkb2luZyBzb21ldGhpbmcgY29tcGxpY2F0ZWQgZW5vdWdoIHRvIHRoaW5rIGFib3V0IHdoZW4gdGhpbmdzIGNoYW5nZS4gKi9cbiAgICB2YWxpZFdpbmRvdzogW251bWJlciwgbnVtYmVyXTtcbiAgICAvKiogU2hhcmRpbmc6IHRoZSBmcmFjdGlvbiBvZiB0aGUga2V5IHNwYWNlIHRoaXMgc291cmNlIGhhbmRsZXMsIGFzIFtzdGFydCwgZW5kKSBvdmVyIFswLCAxKSAoa2V5cyBhcmUgcm91dGVkIGJ5IGdldFJvdXRlIGluIHJlbW90ZUNvbmZpZy50cykuIERlZmF1bHRzIHRvIEZVTExfUk9VVEUgKHVuc2hhcmRlZCkuIEF0IGV2ZXJ5IHBvaW50IGluIHRpbWUgdGhlIHNvdXJjZXMnIHJvdXRlcyBtdXN0IGZ1bGx5IGNvdmVyIFswLCAxKSwgb3Igc29tZSBrZXlzIGNvdWxkIG5ldmVyIGJlIHJlYWQuICovXG4gICAgcm91dGU/OiBbbnVtYmVyLCBudW1iZXJdO1xufTtcblxuZXhwb3J0IHR5cGUgSG9zdGVkQ29uZmlnID0gQ29tbW9uQ29uZmlnICYge1xuICAgIHR5cGU6IFwicmVtb3RlXCI7XG5cbiAgICAvLyBFeDogaHR0cHM6Ly85OS0yNTAtMTI0LTkxLnF1ZXJ5c3VidGVzdC5jb206NTIzMy9maWxlL3Jvb3QvdW5pcXVlYnVja2V0bmFtZS9zdG9yYWdlL3N0b3JhZ2Vyb3V0aW5nLmpzb25cbiAgICAvLyBOT1RFOiBUaGUgYWNjb3VudCBhbmQgYnVja2V0IG5hbWUgYXJlIG9idGFpbmVkIGZyb20gdGhlIFVSTC5cbiAgICB1cmw6IHN0cmluZztcblxuICAgIC8vIE5PVEU6IEF1dGhlbnRpY2F0aW9uIGlzIGhhbmRsZWQgYnkgY2VydC50cywgdmlhIGhhdmluZyB5b3VyIG1hY2hpbmUgdHJ1c3RlZCB0byBhY2Nlc3MgdGhpcyBhY2NvdW50LiBcbiAgICBhY2NvdW50TmFtZT86IHN0cmluZztcblxuICAgIHB1YmxpYz86IGJvb2xlYW47XG4gICAgLy8gRmFzdCBtb2RlOiB0aGUgc2VydmVyIGFja25vd2xlZGdlcyB3cml0ZXMgb25jZSB0aGV5IGFyZSBpbiBtZW1vcnksIGZsdXNoaW5nIHRvIGRpc2sgYWZ0ZXJcbiAgICAvLyB3cml0ZURlbGF5IChkZWZhdWx0IDUgbWludXRlcykgYW5kIGNvYWxlc2Npbmcgd3JpdGVzIHRvIHRoZSBzYW1lIGZpbGUuIEEgc2VydmVyIGNyYXNoIGxvc2VzXG4gICAgLy8gd3JpdGVzIHRoYXQgaGF2ZW4ndCBmbHVzaGVkIHlldC5cbiAgICBmYXN0PzogYm9vbGVhbjtcbiAgICB3cml0ZURlbGF5PzogbnVtYmVyO1xuICAgIC8vIFRoZSBidWNrZXQgaXMgc2VydmVkIHN0cmFpZ2h0IGZyb20gdGhlIHNlcnZlcidzIGRpc2ssIHdpdGggbm8gaW5kZXgg4oCUIHNvIG5vIGZhc3Qgd3JpdGVzIGFuZFxuICAgIC8vIG5vIGdldENoYW5nZXNBZnRlci9nZXRTeW5jU3RhdHVzLlxuICAgIHJhd0Rpc2s/OiBib29sZWFuO1xuICAgIC8vIFdyaXRlcyB0byBwYXRocyB0aGF0IGFscmVhZHkgZXhpc3QgYXJlIGRpc2FsbG93ZWQgKGRlbGV0ZXMgc3RpbGwgd29yaykuXG4gICAgaW1tdXRhYmxlPzogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCB0eXBlIEJhY2tibGF6ZUNvbmZpZyA9IENvbW1vbkNvbmZpZyAmIHtcbiAgICB0eXBlOiBcImJhY2tibGF6ZVwiO1xuICAgIC8vIEV4OiBodHRwczovL2YwMDIuYmFja2JsYXplYjIuY29tL2ZpbGUvcXVlcnlzdWJ0ZXN0LWNvbS1wdWJsaWMtaW1tdXRhYmxlL3N0b3JhZ2Uvc3RvcmFnZXJvdXRpbmcuanNvblxuICAgIC8vIE5PVEU6IFRoZSBidWNrZXQgbmFtZSBpcyBvYnRhaW5lZCBmcm9tIHRoZSBVUkwuXG4gICAgdXJsOiBzdHJpbmc7XG4gICAgLy8gUHVibGljIGJ1Y2tldHMgYXJlIHNlcnZlZCBvdmVyIHBsYWluIEhUVFBTIEdFVHMgKGdldFVSTCkuIFByaXZhdGUgYnVja2V0cyBhcmUgQVBJLWFjY2VzcyBvbmx5LlxuICAgIHB1YmxpYz86IGJvb2xlYW47XG4gICAgLy8gTk9URTogVGhpcyBpc24ndCBlbmZvcmNlZCBvbiB0aGUgYmFja2JsYXplIGxldmVsLCBzbyB0aGlzIGlzIGp1c3QgYSBjbGllbnQtc2lkZSBndWFyYW50ZWUuIFRoaXMgY2FuIGNoYW5nZSBob3cgd2UgY2FjaGUgZmlsZXMuXG4gICAgLy8gIC0gQmFja2JsYXplIGRvZXMgc3VwcG9ydCBpbW11dGFiaWxpdHkuIEhvd2V2ZXIsIGFwcGFyZW50bHksIG9uY2Ugd2UgZW5hYmxlIGl0IG9uIGEgYnVja2V0LCB3ZSBjYW4ndCBkaXNhYmxlIGl0LCB3aGljaCBpcyByZWFsbHkgYmFkLCBhcyBpdCBtZWFucyBpZiBvdXIgY29kZSBjb3VsZCBldmVyIGVuYWJsZSBpdCBhbmQgd2UgYWNjaWRlbnRhbGx5IGVuYWJsZSBpdCBvbiBhbiBpbXBvcnRhbnQgYnVja2V0LCB3ZSBlc3NlbnRpYWxseSBqdXN0IGJyaWNrZWQgdGhhdCBidWNrZXQuIFNvIHdlIHNob3VsZCBuZXZlciB3cml0ZSBhbnkgY29kZSB0aGF0IGV2ZXIgdHJpZXMgdG8gdXNlIGJhY2tibGF6ZSB0byBtYWtlIHRoaW5ncyBpbW11dGFibGUuIFxuICAgIGltbXV0YWJsZT86IGJvb2xlYW47XG4gICAgLy8gQ09SUyBvcmlnaW5zIGFsbG93ZWQgdG8gY29uc3VtZSB0aGUgYnVja2V0J3MgZmlsZXMgaW4gYSBicm93c2VyLiBOb3QgYSBzZWN1cml0eSBib3VuZGFyeVxuICAgIC8vIChhY2Nlc3MgaXMgZ2F0ZWQgYnkgdGhlIEFQSSBrZXkgLyBzaWduZWQgVVJMcywgbmVpdGhlciBvZiB3aGljaCByaWRlcyBpbiBjb29raWVzKSAtIGl0IG9ubHlcbiAgICAvLyBjb250cm9scyB3aGljaCBzaXRlcycgaW4tcGFnZSBKYXZhU2NyaXB0IGNhbiByZWFkIHJlc3BvbnNlcy4gRGVmYXVsdHMgdG8gYW55IEhUVFBTIG9yaWdpbi5cbiAgICBhbGxvd2VkT3JpZ2lucz86IHN0cmluZ1tdO1xuXG4gICAgLy8gTk9URTogV2Ugd2lsbCBhY2Nlc3MgdGhlIGFwaSBrZXkgZnJvbSBnZXRTZWNyZXQsIHNlZSBiYWNrYmxhemUudHMgZm9yIHRoZSBzcGVjaWZpYyBrZXlzLlxufTtcblxuXG5leHBvcnQgY29uc3QgRlVMTF9WQUxJRF9XSU5ET1c6IFtudW1iZXIsIG51bWJlcl0gPSBbMCwgTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVJdO1xuXG5cblxuXG4vLyBjcmVhdGVUaW1lIGlzIGEgbWlzbm9tZXIga2VwdCBmb3IgY29tcGF0aWJpbGl0eSDigJQgaXQgaXMgcmVhbGx5IHRoZSBMQVNULVdSSVRFIHRpbWUsIHNhbWUgYXNcbi8vIGdldEluZm8ncyB3cml0ZVRpbWUuIE5laXRoZXIgQmFja2JsYXplIG5vciBvdXIgcmVtb3RlIHN0b3JhZ2UgdHJhY2tzIGEgZGlzdGluY3QgY3JlYXRpb24gZGF0ZTpcbi8vIGVhY2ggd3JpdGUgc3RhbXBzIGEgZnJlc2ggdGltZXN0YW1wIG9uIHRoZSBjdXJyZW50IHZlcnNpb24sIHNvIGJvdGggZmllbGRzIGFyZSBqdXN0IFwid2hlbiB0aGVcbi8vIGJ5dGVzIHNlcnZlZCBieSBnZXQoKSB3ZXJlIG1vc3QgcmVjZW50bHkgd3JpdHRlblwiLlxuZXhwb3J0IHR5cGUgQXJjaGl2ZUZpbGVJbmZvID0geyBwYXRoOiBzdHJpbmc7IGNyZWF0ZVRpbWU6IG51bWJlcjsgc2l6ZTogbnVtYmVyIH07XG5cbi8vIEFuIGluLXByb2dyZXNzIGJhY2tncm91bmQgc3luY2hyb25pemF0aW9uIHRhc2sgKHNlZSBBcmNoaXZlc0NvbmZpZy5zeW5jaW5nKVxuZXhwb3J0IHR5cGUgU3luY0FjdGl2aXR5ID0ge1xuICAgIC8vIEEgbWV0YWRhdGEgc2NhbiBpcyBhIHNpbmdsZSBsaXN0aW5nIGNhbGwsIHNvIGl0IGhhcyBubyBpbmNyZW1lbnRhbCBwcm9ncmVzcyAtIGp1c3QgdGhhdCBpdCdzXG4gICAgLy8gcnVubmluZyBhbmQgc2luY2Ugd2hlbi4gQSBmdWxsIHN5bmMga25vd3MgaXRzIGV4YWN0IGZpbGUvYnl0ZSBwcm9ncmVzcy5cbiAgICB0eXBlOiBcIm1ldGFkYXRhU2NhblwiIHwgXCJmdWxsU3luY1wiO1xuICAgIHNvdXJjZURlYnVnTmFtZTogc3RyaW5nO1xuICAgIHN0YXJ0VGltZTogbnVtYmVyO1xuICAgIGRvbmVGaWxlcz86IG51bWJlcjtcbiAgICB0b3RhbEZpbGVzPzogbnVtYmVyO1xuICAgIGRvbmVCeXRlcz86IG51bWJlcjtcbiAgICB0b3RhbEJ5dGVzPzogbnVtYmVyO1xufTtcblxuZXhwb3J0IHR5cGUgQXJjaGl2ZXNDb25maWcgPSB7XG4gICAgLy8gV2hldGhlciBnZXRDaGFuZ2VzQWZ0ZXIgaXMgaW1wbGVtZW50ZWQgKGZhc3QgY2hhbmdlIHBvbGxpbmcsIGluc3RlYWQgb2YgZnVsbCByZXNjYW5zKVxuICAgIHN1cHBvcnRzQ2hhbmdlc0FmdGVyPzogYm9vbGVhbjtcbiAgICAvLyBUaGUgYnVja2V0J3MgZnVsbCByb3V0aW5nIGNvbmZpZyAoUk9VVElOR19GSUxFKS4gQWJzZW50IGZvciBzb3VyY2VzIHRoYXQgZG9uJ3QgaGF2ZSBvbmUgKGFcbiAgICAvLyBiYXJlIGRpc2sgc291cmNlLCBvciBhIGJ1Y2tldCB0aGF0IGRvZXNuJ3QgZXhpc3QgeWV0KS5cbiAgICByZW1vdGVDb25maWc/OiBSZW1vdGVDb25maWc7XG4gICAgLy8gTGl2ZSBpbmRleCB0b3RhbHMgKHRvbWJzdG9uZXMgZXhjbHVkZWQpLCBrZXB0IHVwIHRvIGRhdGUgaW4gbWVtb3J5IG9uIGV2ZXJ5IG11dGF0aW9uIGFuZFxuICAgIC8vIHJlY29tcHV0ZWQgb24gbG9hZCAtIHNvIGFueSBkcmlmdCBoZWFscyBvbiByZXN0YXJ0XG4gICAgaW5kZXg/OiB7IGZpbGVDb3VudDogbnVtYmVyOyBieXRlQ291bnQ6IG51bWJlciB9O1xuICAgIC8vIFRoZSBzYW1lIHRvdGFscyBicm9rZW4gZG93biBieSB3aGljaCBzb3VyY2UgY3VycmVudGx5IGhvbGRzIGVhY2ggZmlsZSdzIGJ5dGVzICh0aGUgZmlyc3RcbiAgICAvLyBlbnRyeSBpcyB0aGUgc2VydmVyJ3Mgb3duIGRpc2spXG4gICAgaW5kZXhTb3VyY2VzPzogeyBkZWJ1Z05hbWU6IHN0cmluZzsgZmlsZUNvdW50OiBudW1iZXI7IGJ5dGVDb3VudDogbnVtYmVyIH1bXTtcbiAgICAvLyBUaGUgc2VydmVyJ3MgY29uZmlndXJlZCByZWFkZXJEaXNrTGltaXQsIHdoZW4gaXQgcnVucyBhcyBhIGJvdW5kZWQgcmVhZCBjYWNoZVxuICAgIHJlYWRlckRpc2tMaW1pdD86IG51bWJlcjtcbiAgICAvLyBCYWNrZ3JvdW5kIHN5bmNocm9uaXphdGlvbiBjdXJyZW50bHkgaW4gcHJvZ3Jlc3MgKGVtcHR5IHdoZW4gaWRsZSlcbiAgICBzeW5jaW5nPzogU3luY0FjdGl2aXR5W107XG59O1xuXG4vLyBBIHN5bmNocm9uaXphdGlvbiBzb3VyY2Ugb2YgYSBCbG9iU3RvcmUgKHdoaWNoIHN5bmNocm9uaXplcyBhbiBpbmRleCArIGxvY2FsIGNhY2hlIGZyb20gdGhlbSlcbmV4cG9ydCB0eXBlIEFyY2hpdmVzU291cmNlID0ge1xuICAgIHNvdXJjZTogSUFyY2hpdmVzO1xuICAgIC8vIEZyb20gdGhlIHNvdXJjZSdzIENvbW1vbkNvbmZpZy4gVmFsdWVzIHdpdGggd3JpdGUgdGltZXMgb3V0c2lkZSB0aGUgd2luZG93IGFyZSBpZ25vcmVkIHdoZW5cbiAgICAvLyBzY2FubmluZywgYW5kIG9uY2UgaXRzIGVuZCBpcyBwYXN0IChzZWUgd2luZG93QWNjZXB0c1dyaXRlcykgdGhlIHNvdXJjZSBzdG9wc1xuICAgIC8vIHJlY2VpdmluZyB3cml0ZXMgZW50aXJlbHkgLSBzdGlsbCBzY2FubmVkIChpdCBob2xkcyB0aGUgYXV0aG9yaXRhdGl2ZSBkYXRhIGZvciBpdHMgd2luZG93KSxcbiAgICAvLyBpdCBqdXN0IHN0b3BzIGdyb3dpbmcuXG4gICAgdmFsaWRXaW5kb3c6IFtudW1iZXIsIG51bWJlcl07XG4gICAgLy8gRnJvbSB0aGUgc291cmNlJ3MgQ29tbW9uQ29uZmlnIChpbnRlcnNlY3RlZCB3aXRoIHRoZSBvd25pbmcgc3RvcmUncyBvd24gcm91dGUpOiBvbmx5IGtleXNcbiAgICAvLyByb3V0aW5nIGludG8gW3N0YXJ0LCBlbmQpIGFyZSBhY2NlcHRlZCBmcm9tIHRoaXMgc291cmNlJ3Mgc2NhbnMgYW5kIHNlbnQgdG8gaXQgaW5cbiAgICAvLyB3cml0ZXMvcmVjb25jaWxpYXRpb24uIFRoZSByb3V0aW5nIGZpbGUgaXMgZXhlbXB0IC0gY29uZmlnIGZsb3dzIGV2ZXJ5d2hlcmUuIEFic2VudCA9IGFsbCBrZXlzLlxuICAgIHJvdXRlPzogW251bWJlciwgbnVtYmVyXTtcbiAgICAvLyBGcm9tIHRoZSBzb3VyY2UncyBDb21tb25Db25maWc7IHNlZSB0aGVyZS5cbiAgICBub0Z1bGxTeW5jPzogYm9vbGVhbjtcbiAgICAvLyBTdGFibGUgaWRlbnRpdHkgb2YgdGhlIHVuZGVybHlpbmcgZW5kcG9pbnQgKGl0cyBjb25maWcgd2l0aCB3aW5kb3dzL3JvdXRlcyBzdHJpcHBlZCkgLSBob3dcbiAgICAvLyBCbG9iU3RvcmUudXBkYXRlU291cmNlcyByZWNvZ25pemVzIGEgc291cmNlIGFjcm9zcyBjb25maWcgY2hhbmdlcyBzbyBpdCBjYW4gdXBkYXRlIGl0IGluXG4gICAgLy8gcGxhY2UgaW5zdGVhZCBvZiByZW1vdmluZyBhbmQgcmUtYWRkaW5nIGl0XG4gICAgaWRlbnRpdHk/OiBzdHJpbmc7XG59O1xuXG4vLyBFcnJvciBtYXJrZXIgYSBzZXJ2ZXIgaW5jbHVkZXMgd2hlbiBhIGZyZXNobHktc3RhbXBlZCB3cml0ZSByZWFjaGVzIGl0IG91dHNpZGUgaXRzIHZhbGlkIHdpbmRvd3Ncbi8vICh0aGUgY2xpZW50IHJlc29sdmVkIGl0cyB0YXJnZXQsIHRoZW4gdGltZSBjcm9zc2VkIGEgd2luZG93IGJvdW5kYXJ5IGJlZm9yZSB0aGUgd3JpdGUgbGFuZGVkKS5cbi8vIENsaWVudHMgZGV0ZWN0IHRoaXMgbWFya2VyIGFuZCByZS1yZXNvbHZlIHRoZSBjdXJyZW50bHktdmFsaWQgc291cmNlLCByZXRyeWluZyBPTkNFIC0gYm91bmRhcmllc1xuLy8gYXJlIGZhciBhcGFydCwgc28gaGl0dGluZyBpdCB0d2ljZSBpbiBvbmUgYXR0ZW1wdCBtZWFucyBzb21ldGhpbmcgaXMgYWN0dWFsbHkgd3JvbmcuXG5leHBvcnQgY29uc3QgU1RPUkFHRV9XUk9OR19WQUxJRF9XSU5ET1cgPSBcIlJFTU9URV9TVE9SQUdFX1dST05HX1ZBTElEX1dJTkRPV19hN2MxZjA0ZVwiO1xuXG4vLyBFcnJvciBtYXJrZXIgYSBzZXJ2ZXIgaW5jbHVkZXMgd2hlbiBhIGZyZXNobHktc3RhbXBlZCB3cml0ZSdzIGtleSByb3V0ZXMgb3V0c2lkZSB0aGUgc2hhcmRzIHRoaXNcbi8vIHNlcnZlciBoYW5kbGVzICh0aGUgY2xpZW50J3MgY29uZmlnIGRpc2FncmVlcyB3aXRoIHRoZSBzZXJ2ZXIncyAtIGNsaWVudHMgcmUtcmVzb2x2ZSBvbmNlKVxuZXhwb3J0IGNvbnN0IFNUT1JBR0VfV1JPTkdfUk9VVEUgPSBcIlJFTU9URV9TVE9SQUdFX1dST05HX1JPVVRFX2M5NGQyZTE3XCI7XG5cbmV4cG9ydCBjb25zdCBGVUxMX1JPVVRFOiBbbnVtYmVyLCBudW1iZXJdID0gWzAsIDFdO1xuXG4vLyBBIGtleSBjb250YWluaW5nIHRoaXMgc2VudGluZWwgZG9lc24ndCBoYXZlIGEgZml4ZWQgc2hhcmQ6IHNldFZhcmlhYmxlU2hhcmQgcGlja3MgdGhlIChsb3dlc3Rcbi8vIGxhdGVuY3ksIHVwKSB3cml0ZSBzaGFyZCwgYXBwZW5kcyBcIl88dmFsdWUgaW4gdGhlIHNoYXJkJ3Mgcm91dGU+XCIgZGlyZWN0bHkgYWZ0ZXIgdGhlIHNlbnRpbmVsLFxuLy8gYW5kIHJldHVybnMgdGhlIG1hdGVyaWFsaXplZCBrZXkuIGdldFJvdXRlIHRyZWF0cyB0aGF0IHN1ZmZpeCBhcyBhIGNvbXBsZXRlIHJvdXRlIG92ZXJyaWRlLlxuZXhwb3J0IGNvbnN0IFZBUklBQkxFX1NIQVJEID0gXCJWQVJJQUJMRV9TSEFSRF9mMDIzNGpmYWgwOGZneWhmZ3lzc2Rkczgzbm1wXCI7XG4vLyBObyBncmFjZSBwYXN0IHRoZSBlbmQ6IGEgd2luZG93IGJvdW5kYXJ5IGlzIGEgaGFyZCBoYW5kb2ZmIChjbGllbnRzIHJldHJ5IGEgcmVqZWN0ZWQgd3JpdGVcbi8vIGFnYWluc3QgdGhlIG5ld2x5LXZhbGlkIHNvdXJjZSwgc28gbGVuaWVuY3kgaGVyZSB3b3VsZCBvbmx5IGRlc3luY2hyb25pemUgdGhlIGhhbmRvZmYpXG5leHBvcnQgZnVuY3Rpb24gd2luZG93QWNjZXB0c1dyaXRlcyh2YWxpZFdpbmRvdzogW251bWJlciwgbnVtYmVyXSB8IHVuZGVmaW5lZCk6IGJvb2xlYW4ge1xuICAgIGlmICghdmFsaWRXaW5kb3cpIHJldHVybiB0cnVlO1xuICAgIHJldHVybiB2YWxpZFdpbmRvd1sxXSA+IERhdGUubm93KCk7XG59XG5cbmV4cG9ydCB0eXBlIEFyY2hpdmVzU3luY1NvdXJjZVN0YXR1cyA9IHtcbiAgICBkZWJ1Z05hbWU6IHN0cmluZztcbiAgICB2YWxpZFdpbmRvdzogW251bWJlciwgbnVtYmVyXTtcbiAgICByb3V0ZT86IFtudW1iZXIsIG51bWJlcl07XG4gICAgbm9GdWxsU3luYz86IGJvb2xlYW47XG4gICAgc3VwcG9ydHNDaGFuZ2VzQWZ0ZXI6IGJvb2xlYW47XG4gICAgaW5pdGlhbFNjYW5Db21wbGV0ZTogYm9vbGVhbjtcbiAgICAvLyBGaWxlcyBzZWVuIGluIHRoaXMgc291cmNlJ3Mgc2NhbnMgLyBjaGFuZ2UgcG9sbHMgc28gZmFyXG4gICAgc2Nhbm5lZENvdW50OiBudW1iZXI7XG59O1xuZXhwb3J0IHR5cGUgQXJjaGl2ZXNTeW5jU3RhdHVzID0ge1xuICAgIGFsbFNjYW5zQ29tcGxldGU6IGJvb2xlYW47XG4gICAgLy8gTnVtYmVyIG9mIGZpbGVzIGluIHRoZSBpbmRleFxuICAgIGluZGV4U2l6ZTogbnVtYmVyO1xuICAgIHNvdXJjZXM6IEFyY2hpdmVzU3luY1NvdXJjZVN0YXR1c1tdO1xufTtcblxuZXhwb3J0IGludGVyZmFjZSBJQXJjaGl2ZXMge1xuICAgIGdldERlYnVnTmFtZSgpOiBzdHJpbmc7XG4gICAgLyoqIFdoZXRoZXIgd3JpdGVzIHdvdWxkIGJlIGFjY2VwdGVkIChjcmVkZW50aWFscyBleGlzdCwgdGhlIGFjY291bnQgdHJ1c3RzIHRoaXMgbWFjaGluZSwgZXRjKS5cbiAgICAgKiAgQ2hlY2tlZCB3aXRob3V0IHdyaXRpbmcgYW55dGhpbmcuICovXG4gICAgaGFzV3JpdGVBY2Nlc3MoKTogUHJvbWlzZTxib29sZWFuPjtcbiAgICBnZXQoZmlsZU5hbWU6IHN0cmluZywgY29uZmlnPzogeyByYW5nZT86IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB9KTogUHJvbWlzZTxCdWZmZXIgfCB1bmRlZmluZWQ+O1xuICAgIGdldDIoZmlsZU5hbWU6IHN0cmluZywgY29uZmlnPzogeyByYW5nZT86IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB9KTogUHJvbWlzZTx7IGRhdGE6IEJ1ZmZlcjsgd3JpdGVUaW1lOiBudW1iZXI7IHNpemU6IG51bWJlciB9IHwgdW5kZWZpbmVkPjtcbiAgICAvKipcbiAgICAgKiBsYXN0TW9kaWZpZWQgc3RhbXBzIHRoZSB3cml0ZSB3aXRoIHRoYXQgbGFzdC13cml0ZSB0aW1lIGluc3RlYWQgb2Ygbm93LiBJZiBpdCBpcyBPTERFUiB0aGFuXG4gICAgICogdGhlIGZpbGUncyBjdXJyZW50IGxhc3Qtd3JpdGUgdGltZSB0aGUgd3JpdGUgbm8tb3BzIChzbyBkZWxheWVkIC8gc3luY2hyb25pemVkIHdyaXRlcyBjYW5cbiAgICAgKiBuZXZlciBjbG9iYmVyIG5ld2VyIGRhdGEpLiBUaW1lcyBtb3JlIHRoYW4gMTUgbWludXRlcyBpbiB0aGUgZnV0dXJlIGFyZSByZWplY3RlZC5cbiAgICAgKlxuICAgICAqIFJldHVybnMgdGhlIGZ1bGwga2V5IGFjdHVhbGx5IHdyaXR0ZW4gLSBpZGVudGljYWwgdG8gZmlsZU5hbWUsIEVYQ0VQVCBmb3Iga2V5cyBjb250YWluaW5nXG4gICAgICogVkFSSUFCTEVfU0hBUkQsIHdoZXJlIHRoZSBzaGFyZCB2YWx1ZSBpcyBtYXRlcmlhbGl6ZWQgaW50byB0aGUga2V5IChwaWNrZWQgYnkgc2hhcmQgbGF0ZW5jeSxcbiAgICAgKiBzZWUgQXJjaGl2ZXNDaGFpbikgYW5kIHRoZSBjYWxsZXIgbmVlZHMgdGhlIHJldHVybmVkIGtleSB0byBldmVyIHJlYWQgdGhlIHZhbHVlIGJhY2suXG4gICAgICovXG4gICAgc2V0KGZpbGVOYW1lOiBzdHJpbmcsIGRhdGE6IEJ1ZmZlciwgY29uZmlnPzogeyBsYXN0TW9kaWZpZWQ/OiBudW1iZXIgfSk6IFByb21pc2U8c3RyaW5nPjtcbiAgICBkZWwoZmlsZU5hbWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD47XG4gICAgLyoqIFN0cmVhbXMgYSBmaWxlIHRvbyBsYXJnZSB0byBob2xkIGluIG1lbW9yeS4gZ2V0TmV4dERhdGEgcmV0dXJucyB1bmRlZmluZWQgd2hlbiBkb25lLiAqL1xuICAgIHNldExhcmdlRmlsZShjb25maWc6IHsgcGF0aDogc3RyaW5nOyBnZXROZXh0RGF0YSgpOiBQcm9taXNlPEJ1ZmZlciB8IHVuZGVmaW5lZD4gfSk6IFByb21pc2U8dm9pZD47XG4gICAgLyoqIHdyaXRlVGltZSBpcyB0aGUgbGFzdC13cml0ZSB0aW1lIOKAlCBzZWUgQXJjaGl2ZUZpbGVJbmZvLmNyZWF0ZVRpbWUsIHdoaWNoIGlzIHRoZSBzYW1lIHZhbHVlLiAqL1xuICAgIGdldEluZm8oZmlsZU5hbWU6IHN0cmluZyk6IFByb21pc2U8eyB3cml0ZVRpbWU6IG51bWJlcjsgc2l6ZTogbnVtYmVyIH0gfCB1bmRlZmluZWQ+O1xuICAgIGZpbmQocHJlZml4OiBzdHJpbmcsIGNvbmZpZz86IHsgc2hhbGxvdz86IGJvb2xlYW47IHR5cGU6IFwiZmlsZXNcIiB8IFwiZm9sZGVyc1wiIH0pOiBQcm9taXNlPHN0cmluZ1tdPjtcbiAgICBmaW5kSW5mbyhwcmVmaXg6IHN0cmluZywgY29uZmlnPzogeyBzaGFsbG93PzogYm9vbGVhbjsgdHlwZTogXCJmaWxlc1wiIHwgXCJmb2xkZXJzXCIgfSk6IFByb21pc2U8QXJjaGl2ZUZpbGVJbmZvW10+O1xuICAgIC8qKiBPbmx5IHdvcmtzIGZvciBwdWJsaWMgYnVja2V0cyAocHJpdmF0ZSBidWNrZXRzIGFyZSBBUEktYWNjZXNzIG9ubHkpLiAqL1xuICAgIGdldFVSTChwYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz47XG4gICAgLyoqIFRoZSBidWNrZXQncyBjb25maWd1cmF0aW9uLCB3aGljaCB0ZWxscyB3aGV0aGVyIHRoZSBvcHRpb25hbCBmdW5jdGlvbnMgYXJlIHN1cHBvcnRlZC4gKi9cbiAgICBnZXRDb25maWcoKTogUHJvbWlzZTxBcmNoaXZlc0NvbmZpZz47XG4gICAgLyoqXG4gICAgICogQWxsIGZpbGVzIGNoYW5nZWQgYWZ0ZXIgdGhlIGdpdmVuIHRpbWUuIE9ubHkgZXhpc3RzIHdoZW4gZ2V0Q29uZmlnKCkuc3VwcG9ydHNDaGFuZ2VzQWZ0ZXI7XG4gICAgICogYmFja2VkIGJ5IGFuIGluZGV4LCBzbyBpdCBpcyBmYXN0ICh1bmxpa2UgYSBmdWxsIGZpbmRJbmZvIHNjYW4pLiBEZWxldGlvbnMgYXJlIG5vdCByZXBvcnRlZC5cbiAgICAgKi9cbiAgICBnZXRDaGFuZ2VzQWZ0ZXI/KHRpbWU6IG51bWJlcik6IFByb21pc2U8QXJjaGl2ZUZpbGVJbmZvW10+O1xuICAgIC8qKiBTeW5jaHJvbml6YXRpb24gaW50cm9zcGVjdGlvbiwgZm9yIGJhY2tlbmRzIHRoYXQgc3luY2hyb25pemUgZnJvbSBzb3VyY2VzIChzZWUgQmxvYlN0b3JlKS4gKi9cbiAgICBnZXRTeW5jU3RhdHVzPygpOiBQcm9taXNlPEFyY2hpdmVzU3luY1N0YXR1cz47XG59XG4iXX0=
43
+ /* _JS_SOURCE_HASH = "fa300268b0f1cd22c5c3bdd260738fb54ecb68811ab06ff2f2859fe4d2f1f76c"; */
@@ -11,6 +11,7 @@ export declare class ArchivesChain implements IArchives {
11
11
  private initRetryTimer;
12
12
  private pollTimer;
13
13
  private disposed;
14
+ private unsubscribeRoutingPush;
14
15
  constructor(config: RemoteConfig | RemoteConfigBase);
15
16
  getDebugName(): string;
16
17
  private getState;
@@ -79,6 +80,7 @@ export declare class ArchivesChain implements IArchives {
79
80
  set(fileName: string, data: Buffer, config?: {
80
81
  lastModified?: number;
81
82
  }): Promise<string>;
83
+ private setRoutingConfig;
82
84
  del(fileName: string): Promise<void>;
83
85
  private setVariableShard;
84
86
  setLargeFile(config: {
@@ -4,7 +4,7 @@ import { isNode, sort } from "socket-function/src/misc";
4
4
  import { delay } from "socket-function/src/batching";
5
5
  import {
6
6
  IArchives, RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig,
7
- ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, WRITE_PAST_WINDOW_GRACE, STORAGE_WRONG_VALID_WINDOW,
7
+ ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, STORAGE_WRONG_VALID_WINDOW,
8
8
  STORAGE_WRONG_ROUTE, FULL_ROUTE, VARIABLE_SHARD,
9
9
  } from "../IArchives";
10
10
  import {
@@ -14,6 +14,7 @@ import {
14
14
  } from "./remoteConfig";
15
15
  import { SocketFunction } from "socket-function/SocketFunction";
16
16
  import { ArchivesRemote, parseStorageUrl, authenticateStorage } from "./ArchivesRemote";
17
+ import { onServerRoutingChanged } from "./storageClientController";
17
18
  import { ArchivesBackblaze } from "../backblaze";
18
19
  import { getStorageServerConfigOptional, getLocalArchives, ServerBucketInfo } from "./storageServerState";
19
20
  import { RemoteStorageController, STORAGE_NOT_AUTHENTICATED } from "./storageController";
@@ -63,10 +64,13 @@ type ChainState = {
63
64
  sources: SourceWrapper[];
64
65
  };
65
66
 
67
+ // The window must CONTAIN now (half-open, so a boundary resolves unambiguously). No grace in
68
+ // either direction: a boundary is a hard handoff, and a write racing it is simply rejected by the
69
+ // server and retried against the newly-valid source.
66
70
  function configWindowCurrent(config: HostedConfig | BackblazeConfig): boolean {
67
71
  let now = Date.now();
68
72
  let [start, end] = config.validWindow;
69
- return start - WRITE_PAST_WINDOW_GRACE <= now && now <= end + WRITE_PAST_WINDOW_GRACE;
73
+ return start <= now && now < end;
70
74
  }
71
75
 
72
76
  // Client writes target only the CURRENTLY valid source: a future-window source receives its data
@@ -84,10 +88,17 @@ export class ArchivesChain implements IArchives {
84
88
  private initRetryTimer: ReturnType<typeof setTimeout> | undefined;
85
89
  private pollTimer: ReturnType<typeof setInterval> | undefined;
86
90
  private disposed = false;
91
+ private unsubscribeRoutingPush: (() => void) | undefined;
87
92
 
88
93
  constructor(config: RemoteConfig | RemoteConfigBase) {
89
94
  this.configured = normalizeRemoteConfig(config);
90
95
  this.activeConfig = this.configured;
96
+ // Servers broadcast to their connected clients the moment a routing config changes; the
97
+ // push runs the exact same refresh the poll does, just immediately
98
+ this.unsubscribeRoutingPush = onServerRoutingChanged(() => {
99
+ console.log(`A storage server broadcast a routing config change; refreshing config for ${this.getDebugName()}`);
100
+ void this.refreshActiveConfig().catch((e: Error) => console.error(`Config refresh failed for ${this.getDebugName()}: ${e.stack ?? e}`));
101
+ });
91
102
  }
92
103
 
93
104
  public getDebugName() {
@@ -220,10 +231,17 @@ export class ArchivesChain implements IArchives {
220
231
  let routingData = serializeRemoteConfig(this.configured);
221
232
  let routingWriteTime = Date.now();
222
233
  let writtenUrls = new Set<string>();
234
+ for (let source of sources) {
235
+ if (writtenUrls.has(source.config.url)) continue;
236
+ writtenUrls.add(source.config.url);
237
+ }
238
+ console.log(`Storage routing config for ${this.getDebugName()} is out of date (stored version ${existing && getConfigVersion(existing) || "none"}, ours ${getConfigVersion(this.configured)}): writing ours to all ${writtenUrls.size} nodes (write time ${new Date(routingWriteTime).toISOString()}): ${[...writtenUrls].join(", ")}`);
239
+ writtenUrls.clear();
223
240
  for (let source of sources) {
224
241
  if (writtenUrls.has(source.config.url)) continue;
225
242
  writtenUrls.add(source.config.url);
226
243
  await source.write(archives => archives.set(ROUTING_FILE, routingData, { lastModified: routingWriteTime }));
244
+ console.log(`Wrote storage routing config version ${getConfigVersion(this.configured)} to ${source.config.url}`);
227
245
  }
228
246
  } catch (e) {
229
247
  for (let source of sources) {
@@ -698,12 +716,47 @@ export class ArchivesChain implements IArchives {
698
716
  * are automatically materialized (a shard value is picked and embedded, see setVariableShard)
699
717
  * and the caller needs the returned key to ever read the value back. */
700
718
  public async set(fileName: string, data: Buffer, config?: { lastModified?: number }): Promise<string> {
719
+ if (fileName === ROUTING_FILE) {
720
+ return await this.setRoutingConfig(data, config);
721
+ }
701
722
  if (fileName.includes(VARIABLE_SHARD) && parseVariableRoute(fileName) === undefined) {
702
723
  return await this.setVariableShard(fileName, data, config);
703
724
  }
704
725
  await this.request({ write: true, route: getRoute(fileName) }, archives => archives.set(fileName, data, config));
705
726
  return fileName;
706
727
  }
728
+
729
+ // The routing config is NEVER synchronized between nodes, so a chain-level write of it goes
730
+ // directly to EVERY node - first-valid-source routing would leave every other node on the old
731
+ // config forever. One shared write time, so each node resolves latest-write-time-wins
732
+ // identically. Afterwards we immediately run the same refresh the config poll uses: the
733
+ // update must apply to us now, not whenever the poll would have noticed.
734
+ private async setRoutingConfig(data: Buffer, config?: { lastModified?: number }): Promise<string> {
735
+ let state = await this.getState();
736
+ // Parse first, so a malformed config fails before any node stores it
737
+ let incoming = parseRoutingData(data);
738
+ let writeTime = config?.lastModified || Date.now();
739
+ let written: string[] = [];
740
+ let errors: string[] = [];
741
+ let seen = new Set<string>();
742
+ console.log(`Writing storage routing config version ${getConfigVersion(incoming)} for ${this.getDebugName()} to every node (write time ${new Date(writeTime).toISOString()})`);
743
+ for (let source of state.sources) {
744
+ if (seen.has(source.config.url)) continue;
745
+ seen.add(source.config.url);
746
+ try {
747
+ await source.write(archives => archives.set(ROUTING_FILE, data, { lastModified: writeTime }));
748
+ written.push(source.config.url);
749
+ console.log(`Wrote storage routing config version ${getConfigVersion(incoming)} to ${source.config.url}`);
750
+ } catch (e) {
751
+ errors.push(`${source.config.url}: ${(e as Error).stack ?? e}`);
752
+ }
753
+ }
754
+ await this.refreshActiveConfig();
755
+ if (errors.length) {
756
+ throw new Error(`Storage routing config write for ${this.getDebugName()} failed on ${errors.length} of ${seen.size} nodes (succeeded on: ${written.join(", ") || "none"}): ${errors.join(" | ")}`);
757
+ }
758
+ return ROUTING_FILE;
759
+ }
707
760
  public async del(fileName: string): Promise<void> {
708
761
  await this.request({ write: true, route: getRoute(fileName) }, archives => archives.del(fileName));
709
762
  }
@@ -799,6 +852,7 @@ export class ArchivesChain implements IArchives {
799
852
 
800
853
  public dispose(): void {
801
854
  this.disposed = true;
855
+ this.unsubscribeRoutingPush?.();
802
856
  if (this.pollTimer) {
803
857
  clearInterval(this.pollTimer);
804
858
  }