sliftutils 1.7.28 → 1.7.32

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
@@ -3207,6 +3207,7 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3207
3207
  private initRetryTimer;
3208
3208
  private pollTimer;
3209
3209
  private disposed;
3210
+ private unsubscribeRoutingPush;
3210
3211
  constructor(config: RemoteConfig | RemoteConfigBase);
3211
3212
  getDebugName(): string;
3212
3213
  private getState;
@@ -3275,6 +3276,7 @@ declare module "sliftutils/storage/remoteStorage/createArchives" {
3275
3276
  set(fileName: string, data: Buffer, config?: {
3276
3277
  lastModified?: number;
3277
3278
  }): Promise<string>;
3279
+ private setRoutingConfig;
3278
3280
  del(fileName: string): Promise<void>;
3279
3281
  private setVariableShard;
3280
3282
  setLargeFile(config: {
@@ -3437,6 +3439,15 @@ declare module "sliftutils/storage/remoteStorage/sourceWrapper" {
3437
3439
 
3438
3440
  }
3439
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
+
3440
3451
  declare module "sliftutils/storage/remoteStorage/storageController" {
3441
3452
  /// <reference types="node" />
3442
3453
  /// <reference types="node" />
@@ -3476,6 +3487,9 @@ declare module "sliftutils/storage/remoteStorage/storageController" {
3476
3487
  grantAccessCommand?: string;
3477
3488
  trustedMachines?: TrustRecord[];
3478
3489
  };
3490
+ /** Called by storageServerState the moment any routing config is applied - clients must never
3491
+ * have to wait for a poll to learn the topology changed. */
3492
+ export declare function broadcastRoutingChanged(): void;
3479
3493
  export declare const RemoteStorageController: import("socket-function/SocketFunctionTypes").SocketRegistered<{
3480
3494
  ping: () => Promise<{
3481
3495
  takeover?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.7.28",
3
+ "version": "1.7.32",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -1,4 +1,4 @@
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
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;
@@ -40,4 +40,4 @@ function windowAcceptsWrites(validWindow) {
40
40
  }
41
41
  exports.windowAcceptsWrites = windowAcceptsWrites;
42
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 = "14ead1a41fe7780fb0c9aee5b124012545ed8316a62a98f68655384d056005c1"; */
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: {
@@ -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";
@@ -87,10 +88,17 @@ export class ArchivesChain implements IArchives {
87
88
  private initRetryTimer: ReturnType<typeof setTimeout> | undefined;
88
89
  private pollTimer: ReturnType<typeof setInterval> | undefined;
89
90
  private disposed = false;
91
+ private unsubscribeRoutingPush: (() => void) | undefined;
90
92
 
91
93
  constructor(config: RemoteConfig | RemoteConfigBase) {
92
94
  this.configured = normalizeRemoteConfig(config);
93
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
+ });
94
102
  }
95
103
 
96
104
  public getDebugName() {
@@ -223,10 +231,17 @@ export class ArchivesChain implements IArchives {
223
231
  let routingData = serializeRemoteConfig(this.configured);
224
232
  let routingWriteTime = Date.now();
225
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();
226
240
  for (let source of sources) {
227
241
  if (writtenUrls.has(source.config.url)) continue;
228
242
  writtenUrls.add(source.config.url);
229
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}`);
230
245
  }
231
246
  } catch (e) {
232
247
  for (let source of sources) {
@@ -701,12 +716,47 @@ export class ArchivesChain implements IArchives {
701
716
  * are automatically materialized (a shard value is picked and embedded, see setVariableShard)
702
717
  * and the caller needs the returned key to ever read the value back. */
703
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
+ }
704
722
  if (fileName.includes(VARIABLE_SHARD) && parseVariableRoute(fileName) === undefined) {
705
723
  return await this.setVariableShard(fileName, data, config);
706
724
  }
707
725
  await this.request({ write: true, route: getRoute(fileName) }, archives => archives.set(fileName, data, config));
708
726
  return fileName;
709
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
+ }
710
760
  public async del(fileName: string): Promise<void> {
711
761
  await this.request({ write: true, route: getRoute(fileName) }, archives => archives.del(fileName));
712
762
  }
@@ -802,6 +852,7 @@ export class ArchivesChain implements IArchives {
802
852
 
803
853
  public dispose(): void {
804
854
  this.disposed = true;
855
+ this.unsubscribeRoutingPush?.();
805
856
  if (this.pollTimer) {
806
857
  clearInterval(this.pollTimer);
807
858
  }