@dcl/sdk 7.29.1-36637355925.commit-d261ba1 → 7.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/explorer-ui/index.d.ts +118 -0
  2. package/explorer-ui/index.js +235 -0
  3. package/internal/transports/rendererTransport.js +2 -5
  4. package/network/binary-message-bus.d.ts +3 -6
  5. package/network/binary-message-bus.js +5 -9
  6. package/network/index.d.ts +2 -8
  7. package/network/index.js +3 -16
  8. package/network/message-bus-sync.d.ts +3 -16
  9. package/network/message-bus-sync.js +103 -164
  10. package/network/state.js +5 -3
  11. package/package.json +6 -6
  12. package/src/explorer-ui/index.ts +398 -0
  13. package/src/internal/transports/rendererTransport.ts +1 -4
  14. package/src/network/binary-message-bus.ts +4 -9
  15. package/src/network/index.ts +3 -40
  16. package/src/network/message-bus-sync.ts +112 -180
  17. package/src/network/state.ts +4 -3
  18. package/atom.d.ts +0 -19
  19. package/atom.js +0 -83
  20. package/future.d.ts +0 -8
  21. package/future.js +0 -26
  22. package/network/chunking.d.ts +0 -5
  23. package/network/chunking.js +0 -38
  24. package/network/events/implementation.d.ts +0 -93
  25. package/network/events/implementation.js +0 -221
  26. package/network/events/index.d.ts +0 -42
  27. package/network/events/index.js +0 -43
  28. package/network/events/protocol.d.ts +0 -27
  29. package/network/events/protocol.js +0 -66
  30. package/network/events/registry.d.ts +0 -8
  31. package/network/events/registry.js +0 -3
  32. package/network/server/index.d.ts +0 -14
  33. package/network/server/index.js +0 -256
  34. package/network/server/utils.d.ts +0 -18
  35. package/network/server/utils.js +0 -135
  36. package/server/env-var.d.ts +0 -15
  37. package/server/env-var.js +0 -31
  38. package/server/index.d.ts +0 -2
  39. package/server/index.js +0 -3
  40. package/server/storage/constants.d.ts +0 -80
  41. package/server/storage/constants.js +0 -19
  42. package/server/storage/error-message.d.ts +0 -1
  43. package/server/storage/error-message.js +0 -9
  44. package/server/storage/index.d.ts +0 -54
  45. package/server/storage/index.js +0 -60
  46. package/server/storage/player.d.ts +0 -60
  47. package/server/storage/player.js +0 -244
  48. package/server/storage/scene.d.ts +0 -55
  49. package/server/storage/scene.js +0 -232
  50. package/server/storage/serialize.d.ts +0 -1
  51. package/server/storage/serialize.js +0 -135
  52. package/server/storage/value-cache.d.ts +0 -1
  53. package/server/storage/value-cache.js +0 -67
  54. package/server/storage/write-queue.d.ts +0 -1
  55. package/server/storage/write-queue.js +0 -113
  56. package/server/storage-url.d.ts +0 -13
  57. package/server/storage-url.js +0 -42
  58. package/server/utils.d.ts +0 -35
  59. package/server/utils.js +0 -69
  60. package/src/atom.ts +0 -98
  61. package/src/future.ts +0 -38
  62. package/src/network/chunking.ts +0 -45
  63. package/src/network/events/implementation.ts +0 -271
  64. package/src/network/events/index.ts +0 -48
  65. package/src/network/events/protocol.ts +0 -94
  66. package/src/network/events/registry.ts +0 -18
  67. package/src/network/server/index.ts +0 -341
  68. package/src/network/server/utils.ts +0 -189
  69. package/src/server/env-var.ts +0 -36
  70. package/src/server/index.ts +0 -12
  71. package/src/server/storage/constants.ts +0 -112
  72. package/src/server/storage/error-message.ts +0 -8
  73. package/src/server/storage/index.ts +0 -83
  74. package/src/server/storage/player.ts +0 -345
  75. package/src/server/storage/scene.ts +0 -324
  76. package/src/server/storage/serialize.ts +0 -132
  77. package/src/server/storage/value-cache.ts +0 -119
  78. package/src/server/storage/write-queue.ts +0 -174
  79. package/src/server/storage-url.ts +0 -49
  80. package/src/server/utils.ts +0 -83
@@ -1,80 +0,0 @@
1
- export declare const MODULE_NAME = "Storage";
2
- /**
3
- * Options for getValues pagination and filtering.
4
- */
5
- export interface GetValuesOptions {
6
- prefix?: string;
7
- limit?: number;
8
- offset?: number;
9
- }
10
- /**
11
- * Result of getValues with pagination metadata.
12
- */
13
- export interface GetValuesResult {
14
- /** Key-value entries for the current page. */
15
- data: Array<{
16
- key: string;
17
- value: unknown;
18
- }>;
19
- pagination: {
20
- offset: number;
21
- total: number;
22
- };
23
- }
24
- /**
25
- * Per-call options for Storage get().
26
- */
27
- export interface GetOptions {
28
- /**
29
- * When true, bypasses the read cache and forces a network read (the result
30
- * still refreshes the cache). Concurrent gets for the same key still share
31
- * one in-flight request. Default: false.
32
- */
33
- fresh?: boolean;
34
- }
35
- /**
36
- * Per-call options for Storage set().
37
- */
38
- export interface SetOptions {
39
- /**
40
- * When true, skips the network write if the serialized value matches the
41
- * last value known to be stored for this key (from a previous successful
42
- * set(), get() or getValues()). Overrides the configured default. Default: true.
43
- */
44
- skipIfUnchanged?: boolean;
45
- }
46
- /**
47
- * Module-wide configuration for Storage, applied to both scene-scoped and
48
- * player-scoped storage via Storage.configure().
49
- */
50
- export interface StorageOptions {
51
- /**
52
- * Default for set()'s skipIfUnchanged when not passed per call. A skip only
53
- * suppresses a proven no-op: the exact serialized value was confirmed stored
54
- * by a previous network round-trip within cacheMaxAgeMs. Default: true.
55
- */
56
- skipIfUnchanged?: boolean;
57
- /**
58
- * When true, get() serves values from the local cache within cacheMaxAgeMs,
59
- * including confirmed "not found" results, without a network request.
60
- * Out-of-band writers (e.g. CLI storage commands) may not be visible for up
61
- * to cacheMaxAgeMs; pass { fresh: true } per call to force a network read.
62
- * Default: true.
63
- */
64
- cacheReads?: boolean;
65
- /**
66
- * Max cache entries per scope (scene / player) before oldest entries are
67
- * evicted. Entries hold the serialized value body, so memory scales with
68
- * value size — this bounds entry count, not bytes. Infinity removes the bound.
69
- * Default: 512.
70
- */
71
- cacheMaxEntries?: number;
72
- /**
73
- * Max age of a cache entry in milliseconds, bounding both read-cache
74
- * staleness and write-dedup trust; older entries are treated as unknown.
75
- * The default is deliberately short so out-of-band writers (CLI storage
76
- * commands, dashboards) become visible within a minute, while still
77
- * absorbing hot per-frame reads. Infinity never expires. Default: 60000 (1 minute).
78
- */
79
- cacheMaxAgeMs?: number;
80
- }
@@ -1,19 +0,0 @@
1
- export const MODULE_NAME = 'Storage';
2
- /**
3
- * Default values for the resolved storage configuration.
4
- * @internal
5
- */
6
- export const DEFAULT_STORAGE_CONFIG = {
7
- skipIfUnchanged: true,
8
- cacheReads: true,
9
- cacheMaxEntries: 512,
10
- cacheMaxAgeMs: 60 * 1000
11
- };
12
- /**
13
- * Creates a mutable config object, later mutated by Storage.configure().
14
- * @internal
15
- */
16
- export function createStorageConfig(overrides) {
17
- return { ...DEFAULT_STORAGE_CONFIG, ...overrides };
18
- }
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZlci9zdG9yYWdlL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFBO0FBOEZwQzs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxzQkFBc0IsR0FBaUM7SUFDbEUsZUFBZSxFQUFFLElBQUk7SUFDckIsVUFBVSxFQUFFLElBQUk7SUFDaEIsZUFBZSxFQUFFLEdBQUc7SUFDcEIsYUFBYSxFQUFFLEVBQUUsR0FBRyxJQUFJO0NBQ3pCLENBQUE7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBQUMsU0FBMEI7SUFDNUQsT0FBTyxFQUFFLEdBQUcsc0JBQXNCLEVBQUUsR0FBRyxTQUFTLEVBQUUsQ0FBQTtBQUNwRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGNvbnN0IE1PRFVMRV9OQU1FID0gJ1N0b3JhZ2UnXG5cbi8qKlxuICogT3B0aW9ucyBmb3IgZ2V0VmFsdWVzIHBhZ2luYXRpb24gYW5kIGZpbHRlcmluZy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHZXRWYWx1ZXNPcHRpb25zIHtcbiAgcHJlZml4Pzogc3RyaW5nXG4gIGxpbWl0PzogbnVtYmVyXG4gIG9mZnNldD86IG51bWJlclxufVxuXG4vKipcbiAqIFJlc3VsdCBvZiBnZXRWYWx1ZXMgd2l0aCBwYWdpbmF0aW9uIG1ldGFkYXRhLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEdldFZhbHVlc1Jlc3VsdCB7XG4gIC8qKiBLZXktdmFsdWUgZW50cmllcyBmb3IgdGhlIGN1cnJlbnQgcGFnZS4gKi9cbiAgZGF0YTogQXJyYXk8eyBrZXk6IHN0cmluZzsgdmFsdWU6IHVua25vd24gfT5cbiAgcGFnaW5hdGlvbjoge1xuICAgIG9mZnNldDogbnVtYmVyXG4gICAgdG90YWw6IG51bWJlclxuICB9XG59XG5cbi8qKlxuICogUGVyLWNhbGwgb3B0aW9ucyBmb3IgU3RvcmFnZSBnZXQoKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBHZXRPcHRpb25zIHtcbiAgLyoqXG4gICAqIFdoZW4gdHJ1ZSwgYnlwYXNzZXMgdGhlIHJlYWQgY2FjaGUgYW5kIGZvcmNlcyBhIG5ldHdvcmsgcmVhZCAodGhlIHJlc3VsdFxuICAgKiBzdGlsbCByZWZyZXNoZXMgdGhlIGNhY2hlKS4gQ29uY3VycmVudCBnZXRzIGZvciB0aGUgc2FtZSBrZXkgc3RpbGwgc2hhcmVcbiAgICogb25lIGluLWZsaWdodCByZXF1ZXN0LiBEZWZhdWx0OiBmYWxzZS5cbiAgICovXG4gIGZyZXNoPzogYm9vbGVhblxufVxuXG4vKipcbiAqIFBlci1jYWxsIG9wdGlvbnMgZm9yIFN0b3JhZ2Ugc2V0KCkuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2V0T3B0aW9ucyB7XG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIHNraXBzIHRoZSBuZXR3b3JrIHdyaXRlIGlmIHRoZSBzZXJpYWxpemVkIHZhbHVlIG1hdGNoZXMgdGhlXG4gICAqIGxhc3QgdmFsdWUga25vd24gdG8gYmUgc3RvcmVkIGZvciB0aGlzIGtleSAoZnJvbSBhIHByZXZpb3VzIHN1Y2Nlc3NmdWxcbiAgICogc2V0KCksIGdldCgpIG9yIGdldFZhbHVlcygpKS4gT3ZlcnJpZGVzIHRoZSBjb25maWd1cmVkIGRlZmF1bHQuIERlZmF1bHQ6IHRydWUuXG4gICAqL1xuICBza2lwSWZVbmNoYW5nZWQ/OiBib29sZWFuXG59XG5cbi8qKlxuICogTW9kdWxlLXdpZGUgY29uZmlndXJhdGlvbiBmb3IgU3RvcmFnZSwgYXBwbGllZCB0byBib3RoIHNjZW5lLXNjb3BlZCBhbmRcbiAqIHBsYXllci1zY29wZWQgc3RvcmFnZSB2aWEgU3RvcmFnZS5jb25maWd1cmUoKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdG9yYWdlT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBEZWZhdWx0IGZvciBzZXQoKSdzIHNraXBJZlVuY2hhbmdlZCB3aGVuIG5vdCBwYXNzZWQgcGVyIGNhbGwuIEEgc2tpcCBvbmx5XG4gICAqIHN1cHByZXNzZXMgYSBwcm92ZW4gbm8tb3A6IHRoZSBleGFjdCBzZXJpYWxpemVkIHZhbHVlIHdhcyBjb25maXJtZWQgc3RvcmVkXG4gICAqIGJ5IGEgcHJldmlvdXMgbmV0d29yayByb3VuZC10cmlwIHdpdGhpbiBjYWNoZU1heEFnZU1zLiBEZWZhdWx0OiB0cnVlLlxuICAgKi9cbiAgc2tpcElmVW5jaGFuZ2VkPzogYm9vbGVhblxuICAvKipcbiAgICogV2hlbiB0cnVlLCBnZXQoKSBzZXJ2ZXMgdmFsdWVzIGZyb20gdGhlIGxvY2FsIGNhY2hlIHdpdGhpbiBjYWNoZU1heEFnZU1zLFxuICAgKiBpbmNsdWRpbmcgY29uZmlybWVkIFwibm90IGZvdW5kXCIgcmVzdWx0cywgd2l0aG91dCBhIG5ldHdvcmsgcmVxdWVzdC5cbiAgICogT3V0LW9mLWJhbmQgd3JpdGVycyAoZS5nLiBDTEkgc3RvcmFnZSBjb21tYW5kcykgbWF5IG5vdCBiZSB2aXNpYmxlIGZvciB1cFxuICAgKiB0byBjYWNoZU1heEFnZU1zOyBwYXNzIHsgZnJlc2g6IHRydWUgfSBwZXIgY2FsbCB0byBmb3JjZSBhIG5ldHdvcmsgcmVhZC5cbiAgICogRGVmYXVsdDogdHJ1ZS5cbiAgICovXG4gIGNhY2hlUmVhZHM/OiBib29sZWFuXG4gIC8qKlxuICAgKiBNYXggY2FjaGUgZW50cmllcyBwZXIgc2NvcGUgKHNjZW5lIC8gcGxheWVyKSBiZWZvcmUgb2xkZXN0IGVudHJpZXMgYXJlXG4gICAqIGV2aWN0ZWQuIEVudHJpZXMgaG9sZCB0aGUgc2VyaWFsaXplZCB2YWx1ZSBib2R5LCBzbyBtZW1vcnkgc2NhbGVzIHdpdGhcbiAgICogdmFsdWUgc2l6ZSDigJQgdGhpcyBib3VuZHMgZW50cnkgY291bnQsIG5vdCBieXRlcy4gSW5maW5pdHkgcmVtb3ZlcyB0aGUgYm91bmQuXG4gICAqIERlZmF1bHQ6IDUxMi5cbiAgICovXG4gIGNhY2hlTWF4RW50cmllcz86IG51bWJlclxuICAvKipcbiAgICogTWF4IGFnZSBvZiBhIGNhY2hlIGVudHJ5IGluIG1pbGxpc2Vjb25kcywgYm91bmRpbmcgYm90aCByZWFkLWNhY2hlXG4gICAqIHN0YWxlbmVzcyBhbmQgd3JpdGUtZGVkdXAgdHJ1c3Q7IG9sZGVyIGVudHJpZXMgYXJlIHRyZWF0ZWQgYXMgdW5rbm93bi5cbiAgICogVGhlIGRlZmF1bHQgaXMgZGVsaWJlcmF0ZWx5IHNob3J0IHNvIG91dC1vZi1iYW5kIHdyaXRlcnMgKENMSSBzdG9yYWdlXG4gICAqIGNvbW1hbmRzLCBkYXNoYm9hcmRzKSBiZWNvbWUgdmlzaWJsZSB3aXRoaW4gYSBtaW51dGUsIHdoaWxlIHN0aWxsXG4gICAqIGFic29yYmluZyBob3QgcGVyLWZyYW1lIHJlYWRzLiBJbmZpbml0eSBuZXZlciBleHBpcmVzLiBEZWZhdWx0OiA2MDAwMCAoMSBtaW51dGUpLlxuICAgKi9cbiAgY2FjaGVNYXhBZ2VNcz86IG51bWJlclxufVxuXG4vKipcbiAqIEZ1bGx5LXJlc29sdmVkIHN0b3JhZ2UgY29uZmlndXJhdGlvbiBzaGFyZWQgYnkgc2NlbmUgYW5kIHBsYXllciBzdG9yYWdlLlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RvcmFnZUNvbmZpZ1N0YXRlIHtcbiAgc2tpcElmVW5jaGFuZ2VkOiBib29sZWFuXG4gIGNhY2hlUmVhZHM6IGJvb2xlYW5cbiAgY2FjaGVNYXhFbnRyaWVzOiBudW1iZXJcbiAgY2FjaGVNYXhBZ2VNczogbnVtYmVyXG59XG5cbi8qKlxuICogRGVmYXVsdCB2YWx1ZXMgZm9yIHRoZSByZXNvbHZlZCBzdG9yYWdlIGNvbmZpZ3VyYXRpb24uXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfU1RPUkFHRV9DT05GSUc6IFJlYWRvbmx5PFN0b3JhZ2VDb25maWdTdGF0ZT4gPSB7XG4gIHNraXBJZlVuY2hhbmdlZDogdHJ1ZSxcbiAgY2FjaGVSZWFkczogdHJ1ZSxcbiAgY2FjaGVNYXhFbnRyaWVzOiA1MTIsXG4gIGNhY2hlTWF4QWdlTXM6IDYwICogMTAwMFxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBtdXRhYmxlIGNvbmZpZyBvYmplY3QsIGxhdGVyIG11dGF0ZWQgYnkgU3RvcmFnZS5jb25maWd1cmUoKS5cbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU3RvcmFnZUNvbmZpZyhvdmVycmlkZXM/OiBTdG9yYWdlT3B0aW9ucyk6IFN0b3JhZ2VDb25maWdTdGF0ZSB7XG4gIHJldHVybiB7IC4uLkRFRkFVTFRfU1RPUkFHRV9DT05GSUcsIC4uLm92ZXJyaWRlcyB9XG59XG4iXX0=
@@ -1 +0,0 @@
1
- export {};
@@ -1,9 +0,0 @@
1
- /**
2
- * The message of a thrown value, or `fallback` when it carries none.
3
- * @internal
4
- */
5
- export function errorMessage(error, fallback = 'unknown error') {
6
- const message = error instanceof Error ? error.message : typeof error === 'string' ? error : '';
7
- return message || fallback;
8
- }
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3ItbWVzc2FnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2ZXIvc3RvcmFnZS9lcnJvci1tZXNzYWdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUNILE1BQU0sVUFBVSxZQUFZLENBQUMsS0FBYyxFQUFFLFFBQVEsR0FBRyxlQUFlO0lBQ3JFLE1BQU0sT0FBTyxHQUFHLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUE7SUFDL0YsT0FBTyxPQUFPLElBQUksUUFBUSxDQUFBO0FBQzVCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFRoZSBtZXNzYWdlIG9mIGEgdGhyb3duIHZhbHVlLCBvciBgZmFsbGJhY2tgIHdoZW4gaXQgY2FycmllcyBub25lLlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlcnJvck1lc3NhZ2UoZXJyb3I6IHVua25vd24sIGZhbGxiYWNrID0gJ3Vua25vd24gZXJyb3InKTogc3RyaW5nIHtcbiAgY29uc3QgbWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogdHlwZW9mIGVycm9yID09PSAnc3RyaW5nJyA/IGVycm9yIDogJydcbiAgcmV0dXJuIG1lc3NhZ2UgfHwgZmFsbGJhY2tcbn1cbiJdfQ==
@@ -1,54 +0,0 @@
1
- import { StorageOptions } from './constants';
2
- import { ISceneStorage } from './scene';
3
- import { IPlayerStorage } from './player';
4
- export { GetOptions, GetValuesOptions, GetValuesResult, SetOptions, StorageOptions } from './constants';
5
- export { ISceneStorage } from './scene';
6
- export { IPlayerStorage } from './player';
7
- /**
8
- * Storage interface with methods for scene-scoped and player-scoped storage.
9
- */
10
- export interface IStorage extends ISceneStorage {
11
- /** Player-scoped storage for key-value pairs */
12
- player: IPlayerStorage;
13
- /**
14
- * Sets module-wide defaults for both scene-scoped and player-scoped storage.
15
- * Merges the given partial options into the current configuration.
16
- * Note: cacheMaxEntries applies per scope (scene and player each keep their
17
- * own cache) and bounds entry count, not bytes (entries hold serialized
18
- * value bodies).
19
- */
20
- configure(options: StorageOptions): void;
21
- }
22
- /**
23
- * Storage provides methods to store and retrieve key-value data from the
24
- * Server Side Storage service.
25
- *
26
- * - Use Storage.get/set/delete/getValues for scene-scoped storage
27
- * - Use Storage.player.get/set/delete/getValues for player-scoped storage
28
- * - Use Storage.configure to change module-wide defaults (e.g. skipIfUnchanged, cacheReads)
29
- *
30
- * A failure is never reported as absence. A read that fails throws; get() resolves
31
- * null only for a confirmed absence (a 404, or a delete that landed) or a stored
32
- * JSON null, and getValues() returns an empty page only for a genuinely empty
33
- * listing. A delete that fails throws, and resolves false only for a confirmed 404.
34
- * set() reports failure through its boolean result, and throws for a value that
35
- * cannot be JSON-serialized. A get() issued while writes to its key are pending
36
- * waits for them, so it reflects them; getValues() does not wait. Nothing is
37
- * retried — that is the caller's to decide.
38
- *
39
- * Reads are cached by default: get() serves values known from a network
40
- * round-trip within the last cacheMaxAgeMs (including confirmed "not found"
41
- * results) without hitting the service, and concurrent gets for the same key
42
- * share one request. Overlapping writes to the same key are serialized so the
43
- * service commits them in issue order, and rapid writes coalesce to the
44
- * latest value; a set()/delete() settles once its own write, or the newer
45
- * write that replaced it, lands or fails. By default (skipIfUnchanged)
46
- * an unchanged set is skipped, but only when a previous confirmed round-trip
47
- * proved the exact value is already stored. Out-of-band writers (e.g. CLI storage commands)
48
- * may not be visible for up to cacheMaxAgeMs; use get(key, { fresh: true })
49
- * for an authoritative read or Storage.configure({ cacheReads: false }) to
50
- * disable read caching.
51
- *
52
- * This module only works when running on server-side scenes.
53
- */
54
- export declare const Storage: IStorage;
@@ -1,60 +0,0 @@
1
- import { createStorageConfig } from './constants';
2
- import { createSceneStorage } from './scene';
3
- import { createPlayerStorage } from './player';
4
- /**
5
- * Creates the Storage module with scene-scoped and player-scoped storage.
6
- */
7
- const createStorage = () => {
8
- const config = createStorageConfig();
9
- const sceneStorage = createSceneStorage(config);
10
- const playerStorage = createPlayerStorage(config);
11
- return {
12
- // Spread scene storage methods at top level
13
- get: sceneStorage.get,
14
- set: sceneStorage.set,
15
- delete: sceneStorage.delete,
16
- getValues: sceneStorage.getValues,
17
- // Keep player as nested property
18
- player: playerStorage,
19
- configure(options) {
20
- for (const [key, value] of Object.entries(options)) {
21
- if (value !== undefined)
22
- config[key] = value;
23
- }
24
- }
25
- };
26
- };
27
- /**
28
- * Storage provides methods to store and retrieve key-value data from the
29
- * Server Side Storage service.
30
- *
31
- * - Use Storage.get/set/delete/getValues for scene-scoped storage
32
- * - Use Storage.player.get/set/delete/getValues for player-scoped storage
33
- * - Use Storage.configure to change module-wide defaults (e.g. skipIfUnchanged, cacheReads)
34
- *
35
- * A failure is never reported as absence. A read that fails throws; get() resolves
36
- * null only for a confirmed absence (a 404, or a delete that landed) or a stored
37
- * JSON null, and getValues() returns an empty page only for a genuinely empty
38
- * listing. A delete that fails throws, and resolves false only for a confirmed 404.
39
- * set() reports failure through its boolean result, and throws for a value that
40
- * cannot be JSON-serialized. A get() issued while writes to its key are pending
41
- * waits for them, so it reflects them; getValues() does not wait. Nothing is
42
- * retried — that is the caller's to decide.
43
- *
44
- * Reads are cached by default: get() serves values known from a network
45
- * round-trip within the last cacheMaxAgeMs (including confirmed "not found"
46
- * results) without hitting the service, and concurrent gets for the same key
47
- * share one request. Overlapping writes to the same key are serialized so the
48
- * service commits them in issue order, and rapid writes coalesce to the
49
- * latest value; a set()/delete() settles once its own write, or the newer
50
- * write that replaced it, lands or fails. By default (skipIfUnchanged)
51
- * an unchanged set is skipped, but only when a previous confirmed round-trip
52
- * proved the exact value is already stored. Out-of-band writers (e.g. CLI storage commands)
53
- * may not be visible for up to cacheMaxAgeMs; use get(key, { fresh: true })
54
- * for an authoritative read or Storage.configure({ cacheReads: false }) to
55
- * disable read caching.
56
- *
57
- * This module only works when running on server-side scenes.
58
- */
59
- export const Storage = createStorage();
60
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmVyL3N0b3JhZ2UvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLG1CQUFtQixFQUFrQixNQUFNLGFBQWEsQ0FBQTtBQUNqRSxPQUFPLEVBQUUsa0JBQWtCLEVBQWlCLE1BQU0sU0FBUyxDQUFBO0FBQzNELE9BQU8sRUFBRSxtQkFBbUIsRUFBa0IsTUFBTSxVQUFVLENBQUE7QUF3QjlEOztHQUVHO0FBQ0gsTUFBTSxhQUFhLEdBQUcsR0FBYSxFQUFFO0lBQ25DLE1BQU0sTUFBTSxHQUFHLG1CQUFtQixFQUFFLENBQUE7SUFDcEMsTUFBTSxZQUFZLEdBQUcsa0JBQWtCLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDL0MsTUFBTSxhQUFhLEdBQUcsbUJBQW1CLENBQUMsTUFBTSxDQUFDLENBQUE7SUFFakQsT0FBTztRQUNMLDRDQUE0QztRQUM1QyxHQUFHLEVBQUUsWUFBWSxDQUFDLEdBQUc7UUFDckIsR0FBRyxFQUFFLFlBQVksQ0FBQyxHQUFHO1FBQ3JCLE1BQU0sRUFBRSxZQUFZLENBQUMsTUFBTTtRQUMzQixTQUFTLEVBQUUsWUFBWSxDQUFDLFNBQVM7UUFDakMsaUNBQWlDO1FBQ2pDLE1BQU0sRUFBRSxhQUFhO1FBQ3JCLFNBQVMsQ0FBQyxPQUF1QjtZQUMvQixLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRTtnQkFDbEQsSUFBSSxLQUFLLEtBQUssU0FBUztvQkFBRyxNQUE2QyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQTthQUNyRjtRQUNILENBQUM7S0FDRixDQUFBO0FBQ0gsQ0FBQyxDQUFBO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0ErQkc7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQWEsYUFBYSxFQUFFLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjcmVhdGVTdG9yYWdlQ29uZmlnLCBTdG9yYWdlT3B0aW9ucyB9IGZyb20gJy4vY29uc3RhbnRzJ1xuaW1wb3J0IHsgY3JlYXRlU2NlbmVTdG9yYWdlLCBJU2NlbmVTdG9yYWdlIH0gZnJvbSAnLi9zY2VuZSdcbmltcG9ydCB7IGNyZWF0ZVBsYXllclN0b3JhZ2UsIElQbGF5ZXJTdG9yYWdlIH0gZnJvbSAnLi9wbGF5ZXInXG5cbi8vIFJlLWV4cG9ydCBpbnRlcmZhY2VzIGFuZCB0eXBlc1xuZXhwb3J0IHsgR2V0T3B0aW9ucywgR2V0VmFsdWVzT3B0aW9ucywgR2V0VmFsdWVzUmVzdWx0LCBTZXRPcHRpb25zLCBTdG9yYWdlT3B0aW9ucyB9IGZyb20gJy4vY29uc3RhbnRzJ1xuZXhwb3J0IHsgSVNjZW5lU3RvcmFnZSB9IGZyb20gJy4vc2NlbmUnXG5leHBvcnQgeyBJUGxheWVyU3RvcmFnZSB9IGZyb20gJy4vcGxheWVyJ1xuXG4vKipcbiAqIFN0b3JhZ2UgaW50ZXJmYWNlIHdpdGggbWV0aG9kcyBmb3Igc2NlbmUtc2NvcGVkIGFuZCBwbGF5ZXItc2NvcGVkIHN0b3JhZ2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSVN0b3JhZ2UgZXh0ZW5kcyBJU2NlbmVTdG9yYWdlIHtcbiAgLyoqIFBsYXllci1zY29wZWQgc3RvcmFnZSBmb3Iga2V5LXZhbHVlIHBhaXJzICovXG4gIHBsYXllcjogSVBsYXllclN0b3JhZ2VcblxuICAvKipcbiAgICogU2V0cyBtb2R1bGUtd2lkZSBkZWZhdWx0cyBmb3IgYm90aCBzY2VuZS1zY29wZWQgYW5kIHBsYXllci1zY29wZWQgc3RvcmFnZS5cbiAgICogTWVyZ2VzIHRoZSBnaXZlbiBwYXJ0aWFsIG9wdGlvbnMgaW50byB0aGUgY3VycmVudCBjb25maWd1cmF0aW9uLlxuICAgKiBOb3RlOiBjYWNoZU1heEVudHJpZXMgYXBwbGllcyBwZXIgc2NvcGUgKHNjZW5lIGFuZCBwbGF5ZXIgZWFjaCBrZWVwIHRoZWlyXG4gICAqIG93biBjYWNoZSkgYW5kIGJvdW5kcyBlbnRyeSBjb3VudCwgbm90IGJ5dGVzIChlbnRyaWVzIGhvbGQgc2VyaWFsaXplZFxuICAgKiB2YWx1ZSBib2RpZXMpLlxuICAgKi9cbiAgY29uZmlndXJlKG9wdGlvbnM6IFN0b3JhZ2VPcHRpb25zKTogdm9pZFxufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIFN0b3JhZ2UgbW9kdWxlIHdpdGggc2NlbmUtc2NvcGVkIGFuZCBwbGF5ZXItc2NvcGVkIHN0b3JhZ2UuXG4gKi9cbmNvbnN0IGNyZWF0ZVN0b3JhZ2UgPSAoKTogSVN0b3JhZ2UgPT4ge1xuICBjb25zdCBjb25maWcgPSBjcmVhdGVTdG9yYWdlQ29uZmlnKClcbiAgY29uc3Qgc2NlbmVTdG9yYWdlID0gY3JlYXRlU2NlbmVTdG9yYWdlKGNvbmZpZylcbiAgY29uc3QgcGxheWVyU3RvcmFnZSA9IGNyZWF0ZVBsYXllclN0b3JhZ2UoY29uZmlnKVxuXG4gIHJldHVybiB7XG4gICAgLy8gU3ByZWFkIHNjZW5lIHN0b3JhZ2UgbWV0aG9kcyBhdCB0b3AgbGV2ZWxcbiAgICBnZXQ6IHNjZW5lU3RvcmFnZS5nZXQsXG4gICAgc2V0OiBzY2VuZVN0b3JhZ2Uuc2V0LFxuICAgIGRlbGV0ZTogc2NlbmVTdG9yYWdlLmRlbGV0ZSxcbiAgICBnZXRWYWx1ZXM6IHNjZW5lU3RvcmFnZS5nZXRWYWx1ZXMsXG4gICAgLy8gS2VlcCBwbGF5ZXIgYXMgbmVzdGVkIHByb3BlcnR5XG4gICAgcGxheWVyOiBwbGF5ZXJTdG9yYWdlLFxuICAgIGNvbmZpZ3VyZShvcHRpb25zOiBTdG9yYWdlT3B0aW9ucyk6IHZvaWQge1xuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcbiAgICAgICAgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQpIChjb25maWcgYXMgdW5rbm93biBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilba2V5XSA9IHZhbHVlXG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogU3RvcmFnZSBwcm92aWRlcyBtZXRob2RzIHRvIHN0b3JlIGFuZCByZXRyaWV2ZSBrZXktdmFsdWUgZGF0YSBmcm9tIHRoZVxuICogU2VydmVyIFNpZGUgU3RvcmFnZSBzZXJ2aWNlLlxuICpcbiAqIC0gVXNlIFN0b3JhZ2UuZ2V0L3NldC9kZWxldGUvZ2V0VmFsdWVzIGZvciBzY2VuZS1zY29wZWQgc3RvcmFnZVxuICogLSBVc2UgU3RvcmFnZS5wbGF5ZXIuZ2V0L3NldC9kZWxldGUvZ2V0VmFsdWVzIGZvciBwbGF5ZXItc2NvcGVkIHN0b3JhZ2VcbiAqIC0gVXNlIFN0b3JhZ2UuY29uZmlndXJlIHRvIGNoYW5nZSBtb2R1bGUtd2lkZSBkZWZhdWx0cyAoZS5nLiBza2lwSWZVbmNoYW5nZWQsIGNhY2hlUmVhZHMpXG4gKlxuICogQSBmYWlsdXJlIGlzIG5ldmVyIHJlcG9ydGVkIGFzIGFic2VuY2UuIEEgcmVhZCB0aGF0IGZhaWxzIHRocm93czsgZ2V0KCkgcmVzb2x2ZXNcbiAqIG51bGwgb25seSBmb3IgYSBjb25maXJtZWQgYWJzZW5jZSAoYSA0MDQsIG9yIGEgZGVsZXRlIHRoYXQgbGFuZGVkKSBvciBhIHN0b3JlZFxuICogSlNPTiBudWxsLCBhbmQgZ2V0VmFsdWVzKCkgcmV0dXJucyBhbiBlbXB0eSBwYWdlIG9ubHkgZm9yIGEgZ2VudWluZWx5IGVtcHR5XG4gKiBsaXN0aW5nLiBBIGRlbGV0ZSB0aGF0IGZhaWxzIHRocm93cywgYW5kIHJlc29sdmVzIGZhbHNlIG9ubHkgZm9yIGEgY29uZmlybWVkIDQwNC5cbiAqIHNldCgpIHJlcG9ydHMgZmFpbHVyZSB0aHJvdWdoIGl0cyBib29sZWFuIHJlc3VsdCwgYW5kIHRocm93cyBmb3IgYSB2YWx1ZSB0aGF0XG4gKiBjYW5ub3QgYmUgSlNPTi1zZXJpYWxpemVkLiBBIGdldCgpIGlzc3VlZCB3aGlsZSB3cml0ZXMgdG8gaXRzIGtleSBhcmUgcGVuZGluZ1xuICogd2FpdHMgZm9yIHRoZW0sIHNvIGl0IHJlZmxlY3RzIHRoZW07IGdldFZhbHVlcygpIGRvZXMgbm90IHdhaXQuIE5vdGhpbmcgaXNcbiAqIHJldHJpZWQg4oCUIHRoYXQgaXMgdGhlIGNhbGxlcidzIHRvIGRlY2lkZS5cbiAqXG4gKiBSZWFkcyBhcmUgY2FjaGVkIGJ5IGRlZmF1bHQ6IGdldCgpIHNlcnZlcyB2YWx1ZXMga25vd24gZnJvbSBhIG5ldHdvcmtcbiAqIHJvdW5kLXRyaXAgd2l0aGluIHRoZSBsYXN0IGNhY2hlTWF4QWdlTXMgKGluY2x1ZGluZyBjb25maXJtZWQgXCJub3QgZm91bmRcIlxuICogcmVzdWx0cykgd2l0aG91dCBoaXR0aW5nIHRoZSBzZXJ2aWNlLCBhbmQgY29uY3VycmVudCBnZXRzIGZvciB0aGUgc2FtZSBrZXlcbiAqIHNoYXJlIG9uZSByZXF1ZXN0LiBPdmVybGFwcGluZyB3cml0ZXMgdG8gdGhlIHNhbWUga2V5IGFyZSBzZXJpYWxpemVkIHNvIHRoZVxuICogc2VydmljZSBjb21taXRzIHRoZW0gaW4gaXNzdWUgb3JkZXIsIGFuZCByYXBpZCB3cml0ZXMgY29hbGVzY2UgdG8gdGhlXG4gKiBsYXRlc3QgdmFsdWU7IGEgc2V0KCkvZGVsZXRlKCkgc2V0dGxlcyBvbmNlIGl0cyBvd24gd3JpdGUsIG9yIHRoZSBuZXdlclxuICogd3JpdGUgdGhhdCByZXBsYWNlZCBpdCwgbGFuZHMgb3IgZmFpbHMuIEJ5IGRlZmF1bHQgKHNraXBJZlVuY2hhbmdlZClcbiAqIGFuIHVuY2hhbmdlZCBzZXQgaXMgc2tpcHBlZCwgYnV0IG9ubHkgd2hlbiBhIHByZXZpb3VzIGNvbmZpcm1lZCByb3VuZC10cmlwXG4gKiBwcm92ZWQgdGhlIGV4YWN0IHZhbHVlIGlzIGFscmVhZHkgc3RvcmVkLiBPdXQtb2YtYmFuZCB3cml0ZXJzIChlLmcuIENMSSBzdG9yYWdlIGNvbW1hbmRzKVxuICogbWF5IG5vdCBiZSB2aXNpYmxlIGZvciB1cCB0byBjYWNoZU1heEFnZU1zOyB1c2UgZ2V0KGtleSwgeyBmcmVzaDogdHJ1ZSB9KVxuICogZm9yIGFuIGF1dGhvcml0YXRpdmUgcmVhZCBvciBTdG9yYWdlLmNvbmZpZ3VyZSh7IGNhY2hlUmVhZHM6IGZhbHNlIH0pIHRvXG4gKiBkaXNhYmxlIHJlYWQgY2FjaGluZy5cbiAqXG4gKiBUaGlzIG1vZHVsZSBvbmx5IHdvcmtzIHdoZW4gcnVubmluZyBvbiBzZXJ2ZXItc2lkZSBzY2VuZXMuXG4gKi9cbmV4cG9ydCBjb25zdCBTdG9yYWdlOiBJU3RvcmFnZSA9IGNyZWF0ZVN0b3JhZ2UoKVxuIl19
@@ -1,60 +0,0 @@
1
- import { GetOptions, GetValuesOptions, GetValuesResult, SetOptions } from './constants';
2
- /**
3
- * Player-scoped storage interface for key-value pairs from the Server Side Storage service.
4
- * This is NOT filesystem storage - data is stored in the remote storage service.
5
- */
6
- export interface IPlayerStorage {
7
- /**
8
- * Retrieves a value from a player's storage by key from the Server Side Storage service.
9
- *
10
- * By default (cacheReads), values read or written during the last cacheMaxAgeMs
11
- * are served from a local cache without a network request, including confirmed
12
- * "not found" results. Concurrent gets for the same player and key share one
13
- * request. Out-of-band writers (e.g. CLI storage commands) may not be visible
14
- * for up to cacheMaxAgeMs — pass { fresh: true } to force a network read.
15
- * A read first waits for writes to the same key that were pending when it was
16
- * issued, so it reflects every write issued before it.
17
- * @param address - The player's wallet address
18
- * @param key - The key to retrieve
19
- * @param options - Optional { fresh } to bypass the read cache
20
- * @returns A promise that resolves to the parsed JSON value, or null if not found
21
- * @throws Error if the read fails, so a failure is never mistaken for a missing key
22
- * @throws TypeError if the address or key is invalid
23
- */
24
- get<T = unknown>(address: string, key: string, options?: GetOptions): Promise<T | null>;
25
- /**
26
- * Stores a value in a player's storage in the Server Side Storage service.
27
- * @param address - The player's wallet address
28
- * @param key - The key to store the value under
29
- * @param value - The value to store (will be JSON serialized)
30
- * @param options - Optional { skipIfUnchanged } to skip the network write when the value is already stored
31
- * @returns true once stored, or once a newer write that replaced this one lands
32
- * (rapid writes coalesce); false if the write, or its replacement, fails
33
- * @throws TypeError if the address or key is invalid, or the value cannot be stored unchanged: undefined,
34
- * circular, a BigInt, or containing a function, a symbol, a non-finite number, an undefined array
35
- * element or hole, a Map, Set, typed array or other built-in with no JSON form, or text with an
36
- * unpaired surrogate or a NUL character. An undefined object property is dropped; a Date is
37
- * stored as its ISO string and a class instance as its own enumerable data.
38
- */
39
- set<T = unknown>(address: string, key: string, value: T, options?: SetOptions): Promise<boolean>;
40
- /**
41
- * Deletes a value from a player's storage in the Server Side Storage service.
42
- * @param address - The player's wallet address
43
- * @param key - The key to delete
44
- * @returns true once applied, or once a newer write that replaced it lands;
45
- * false only for a confirmed 404
46
- * @throws Error if the delete, or its replacement, fails; a failure is never reported as absence
47
- * @throws TypeError if the address or key is invalid
48
- */
49
- delete(address: string, key: string): Promise<boolean>;
50
- /**
51
- * Returns key-value entries from a player's storage, optionally filtered by prefix.
52
- * Supports pagination via limit and offset.
53
- * @param address - The player's wallet address
54
- * @param options - Optional { prefix, limit, offset } for filtering and pagination.
55
- * @returns A promise that resolves to { data, pagination: { offset, total } } for pagination UI
56
- * @throws Error if the read fails, so a failure is never mistaken for an empty page
57
- * @throws TypeError if the address is invalid
58
- */
59
- getValues(address: string, options?: GetValuesOptions): Promise<GetValuesResult>;
60
- }
@@ -1,244 +0,0 @@
1
- import { getStorageServerUrl } from '../storage-url';
2
- import { assertIsServer, wrapSignedFetch } from '../utils';
3
- import { createStorageConfig, MODULE_NAME } from './constants';
4
- import { createValueCache } from './value-cache';
5
- import { errorMessage } from './error-message';
6
- import { assertPlayerAddress, assertStorageKey, serializeStorageValue } from './serialize';
7
- import { createWriteQueue, SupersedingWriteFailed } from './write-queue';
8
- /**
9
- * Creates player-scoped storage that provides methods to interact with
10
- * player-specific key-value pairs from the Server Side Storage service.
11
- * This module only works when running on server-side scenes.
12
- * @internal
13
- */
14
- export const createPlayerStorage = (config = createStorageConfig()) => {
15
- const cache = createValueCache(config);
16
- // Each in-flight GET is tracked by a wrapper object whose identity marks
17
- // ownership: a landed write detaches the pending GET so its stale response is not cached.
18
- const inflightGets = new Map();
19
- // Lowercased so checksummed callers share an entry.
20
- const cacheKey = (address, key) => JSON.stringify([address.toLowerCase(), key]);
21
- // Writes to the same player key are serialized (and rapid ones coalesced to
22
- // the latest value) so the service commits them in issue order — overlapping
23
- // PUTs would otherwise leave both the kept value and the cached value to
24
- // response-order chance. Keyed by the same case-insensitive cache key.
25
- const writes = createWriteQueue();
26
- async function executeSet(address, key, ck, body) {
27
- let baseUrl;
28
- try {
29
- baseUrl = await getStorageServerUrl();
30
- }
31
- catch (error) {
32
- cache.delete(ck);
33
- console.error(`Failed to set player storage value '${key}' for '${address}': ${errorMessage(error)}`);
34
- return false;
35
- }
36
- const url = `${baseUrl}/players/${encodeURIComponent(address)}/values/${encodeURIComponent(key)}`;
37
- const [error] = await wrapSignedFetch({
38
- url,
39
- init: {
40
- method: 'PUT',
41
- headers: {
42
- 'content-type': 'application/json'
43
- },
44
- body
45
- }
46
- });
47
- // Either way the entry changed server-side (or may have): detach any
48
- // overlapping in-flight GET so its stale response is not cached.
49
- inflightGets.delete(ck);
50
- if (error) {
51
- // The PUT may have reached the server, so the cached body is no
52
- // longer reliable.
53
- cache.delete(ck);
54
- return false;
55
- }
56
- cache.set(ck, { body });
57
- return true;
58
- }
59
- async function executeDelete(address, key, ck) {
60
- let baseUrl;
61
- try {
62
- baseUrl = await getStorageServerUrl();
63
- }
64
- catch (error) {
65
- cache.delete(ck);
66
- throw new Error(`Failed to delete player storage value '${key}' for '${address}': ${errorMessage(error)}`);
67
- }
68
- const url = `${baseUrl}/players/${encodeURIComponent(address)}/values/${encodeURIComponent(key)}`;
69
- const [error, , status] = await wrapSignedFetch({
70
- url,
71
- init: {
72
- method: 'DELETE',
73
- headers: {}
74
- }
75
- });
76
- // Detach again: a GET may have started while the DELETE was in flight.
77
- inflightGets.delete(ck);
78
- if (error) {
79
- // A 404 still confirms the key is absent server-side.
80
- if (status === 404) {
81
- cache.setAbsent(ck);
82
- return false;
83
- }
84
- // The DELETE may have reached the server, so the cached body is unreliable.
85
- cache.delete(ck);
86
- throw new Error(`Failed to delete player storage value '${key}' for '${address}': ${errorMessage(error)}`);
87
- }
88
- cache.setAbsent(ck);
89
- return true;
90
- }
91
- return {
92
- async get(address, key, options) {
93
- assertIsServer(MODULE_NAME);
94
- assertPlayerAddress(address, 'Storage.player.get()');
95
- assertStorageKey(key, 'Storage.player.get()');
96
- const ck = cacheKey(address, key);
97
- if (writes.isPending(ck))
98
- await writes.settled(ck);
99
- if (config.cacheReads && !options?.fresh) {
100
- const entry = cache.get(ck);
101
- if (entry?.absent)
102
- return null;
103
- // Parse per hit so each caller gets a fresh object (no shared mutation).
104
- if (entry?.body !== undefined)
105
- return JSON.parse(entry.body).value;
106
- }
107
- // Coalesce concurrent gets (even fresh ones: an in-flight response is
108
- // milliseconds old, not TTL-stale) into a single network request.
109
- const joined = inflightGets.get(ck);
110
- if (joined)
111
- return joined.promise;
112
- const inflight = {};
113
- inflight.promise = (async () => {
114
- try {
115
- let baseUrl;
116
- try {
117
- baseUrl = await getStorageServerUrl();
118
- }
119
- catch (error) {
120
- throw new Error(`Failed to get player storage value '${key}' for '${address}': ${errorMessage(error)}`);
121
- }
122
- const url = `${baseUrl}/players/${encodeURIComponent(address)}/values/${encodeURIComponent(key)}`;
123
- const [error, data, status] = await wrapSignedFetch({ url });
124
- const isOwner = inflightGets.get(ck) === inflight;
125
- if (error) {
126
- // A confirmed 404 is a first-class "absent" outcome, not a failure.
127
- if (status === 404) {
128
- if (isOwner)
129
- cache.setAbsent(ck);
130
- return null;
131
- }
132
- throw new Error(`Failed to get player storage value '${key}' for '${address}': ${errorMessage(error)}`);
133
- }
134
- // wrapSignedFetch parses an empty 2xx body as {}: a missing value is a fault, not an absence.
135
- if (!data || data.value === undefined) {
136
- throw new Error(`Failed to get player storage value '${key}' for '${address}': response carried no value`);
137
- }
138
- // Same serialization shape as set()'s PUT body, so a read followed by
139
- // an unchanged write can be skipped.
140
- const body = JSON.stringify({ value: data.value });
141
- if (isOwner)
142
- cache.set(ck, { body });
143
- return data.value;
144
- }
145
- finally {
146
- if (inflightGets.get(ck) === inflight)
147
- inflightGets.delete(ck);
148
- }
149
- })();
150
- inflightGets.set(ck, inflight);
151
- return inflight.promise;
152
- },
153
- async set(address, key, value, options) {
154
- assertIsServer(MODULE_NAME);
155
- assertPlayerAddress(address, 'Storage.player.set()');
156
- assertStorageKey(key, 'Storage.player.set()');
157
- const ck = cacheKey(address, key);
158
- const body = serializeStorageValue(value, `Storage.player.set('${address}', '${key}')`);
159
- const skipIfUnchanged = options?.skipIfUnchanged ?? config.skipIfUnchanged;
160
- // Dedup against confirmed state only while no write is pending — a
161
- // pending write makes the cache momentarily stale; enqueue() coalesces
162
- // against pending writes instead.
163
- if (skipIfUnchanged && writes.pending(ck) === undefined && cache.get(ck)?.body === body) {
164
- return true;
165
- }
166
- return writes.enqueue(ck, body, (b) => executeSet(address, key, ck, b), skipIfUnchanged);
167
- },
168
- async delete(address, key) {
169
- assertIsServer(MODULE_NAME);
170
- assertPlayerAddress(address, 'Storage.player.delete()');
171
- assertStorageKey(key, 'Storage.player.delete()');
172
- const ck = cacheKey(address, key);
173
- return writes
174
- .enqueue(ck, null, () => executeDelete(address, key, ck), true)
175
- .catch((error) => {
176
- if (error instanceof SupersedingWriteFailed) {
177
- throw new Error(`Failed to delete player storage value '${key}' for '${address}': ${error.message}`);
178
- }
179
- throw error;
180
- });
181
- },
182
- async getValues(address, options) {
183
- assertIsServer(MODULE_NAME);
184
- assertPlayerAddress(address, 'Storage.player.getValues()');
185
- const { prefix, limit, offset } = options ?? {};
186
- let baseUrl;
187
- try {
188
- baseUrl = await getStorageServerUrl();
189
- }
190
- catch (error) {
191
- throw new Error(`Failed to get player storage values for '${address}': ${errorMessage(error)}`);
192
- }
193
- const parts = [];
194
- if (!!prefix) {
195
- parts.push(`prefix=${encodeURIComponent(prefix)}`);
196
- }
197
- if (!!limit) {
198
- parts.push(`limit=${limit}`);
199
- }
200
- if (!!offset) {
201
- parts.push(`offset=${offset}`);
202
- }
203
- const query = parts.join('&');
204
- const url = query
205
- ? `${baseUrl}/players/${encodeURIComponent(address)}/values?${query}`
206
- : `${baseUrl}/players/${encodeURIComponent(address)}/values`;
207
- const watcher = cache.watch();
208
- const [error, response] = await wrapSignedFetch({ url });
209
- watcher.stop();
210
- if (error) {
211
- throw new Error(`Failed to get player storage values for '${address}': ${errorMessage(error)}`);
212
- }
213
- const data = response?.data;
214
- if (!Array.isArray(data)) {
215
- throw new Error(`Failed to get player storage values for '${address}': response carried no data array`);
216
- }
217
- if (data.some((entry) => typeof entry?.key !== 'string' || entry.value === undefined)) {
218
- throw new Error(`Failed to get player storage values for '${address}': response carried a malformed entry`);
219
- }
220
- // Seed the per-key cache so subsequent get()/set() on returned keys can
221
- // skip the network. Only keys with no live entry and no pending write
222
- // are seeded: existing per-key state comes from a confirmed operation
223
- // that this page snapshot — whose request started earlier — must not
224
- // clobber with stale data. Absence is never seeded (prefix/pagination
225
- // make it non-authoritative). A page larger than cacheMaxEntries churns
226
- // the cache; entries repopulate lazily.
227
- for (const entry of data) {
228
- const ck = cacheKey(address, entry.key);
229
- if (watcher.mutated(ck) || writes.isPending(ck) || inflightGets.has(ck))
230
- continue;
231
- if (cache.get(ck) === undefined) {
232
- cache.set(ck, { body: JSON.stringify({ value: entry.value }) });
233
- }
234
- }
235
- const requestedOffset = offset ?? 0;
236
- const pagination = {
237
- offset: response.pagination?.offset ?? requestedOffset,
238
- total: response.pagination?.total ?? data.length
239
- };
240
- return { data, pagination };
241
- }
242
- };
243
- };
244
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxheWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZlci9zdG9yYWdlL3BsYXllci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUMxRCxPQUFPLEVBQ0wsbUJBQW1CLEVBSW5CLFdBQVcsRUFHWixNQUFNLGFBQWEsQ0FBQTtBQUNwQixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFDaEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQzlDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxnQkFBZ0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUMxRixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFpRXhFOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxTQUE2QixtQkFBbUIsRUFBRSxFQUFrQixFQUFFO0lBQ3hHLE1BQU0sS0FBSyxHQUFHLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQ3RDLHlFQUF5RTtJQUN6RSwwRkFBMEY7SUFDMUYsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQXlDLENBQUE7SUFFckUsb0RBQW9EO0lBQ3BELE1BQU0sUUFBUSxHQUFHLENBQUMsT0FBZSxFQUFFLEdBQVcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBRS9GLDRFQUE0RTtJQUM1RSw2RUFBNkU7SUFDN0UseUVBQXlFO0lBQ3pFLHVFQUF1RTtJQUN2RSxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFBO0lBRWpDLEtBQUssVUFBVSxVQUFVLENBQUMsT0FBZSxFQUFFLEdBQVcsRUFBRSxFQUFVLEVBQUUsSUFBWTtRQUM5RSxJQUFJLE9BQWUsQ0FBQTtRQUNuQixJQUFJO1lBQ0YsT0FBTyxHQUFHLE1BQU0sbUJBQW1CLEVBQUUsQ0FBQTtTQUN0QztRQUFDLE9BQU8sS0FBSyxFQUFFO1lBQ2QsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUNoQixPQUFPLENBQUMsS0FBSyxDQUFDLHVDQUF1QyxHQUFHLFVBQVUsT0FBTyxNQUFNLFlBQVksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7WUFDckcsT0FBTyxLQUFLLENBQUE7U0FDYjtRQUNELE1BQU0sR0FBRyxHQUFHLEdBQUcsT0FBTyxZQUFZLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxXQUFXLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUE7UUFFakcsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLE1BQU0sZUFBZSxDQUFDO1lBQ3BDLEdBQUc7WUFDSCxJQUFJLEVBQUU7Z0JBQ0osTUFBTSxFQUFFLEtBQUs7Z0JBQ2IsT0FBTyxFQUFFO29CQUNQLGNBQWMsRUFBRSxrQkFBa0I7aUJBQ25DO2dCQUNELElBQUk7YUFDTDtTQUNGLENBQUMsQ0FBQTtRQUVGLHFFQUFxRTtRQUNyRSxpRUFBaUU7UUFDakUsWUFBWSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUV2QixJQUFJLEtBQUssRUFBRTtZQUNULGdFQUFnRTtZQUNoRSxtQkFBbUI7WUFDbkIsS0FBSyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUNoQixPQUFPLEtBQUssQ0FBQTtTQUNiO1FBRUQsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO1FBQ3ZCLE9BQU8sSUFBSSxDQUFBO0lBQ2IsQ0FBQztJQUVELEtBQUssVUFBVSxhQUFhLENBQUMsT0FBZSxFQUFFLEdBQVcsRUFBRSxFQUFVO1FBQ25FLElBQUksT0FBZSxDQUFBO1FBQ25CLElBQUk7WUFDRixPQUFPLEdBQUcsTUFBTSxtQkFBbUIsRUFBRSxDQUFBO1NBQ3RDO1FBQUMsT0FBTyxLQUFLLEVBQUU7WUFDZCxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ2hCLE1BQU0sSUFBSSxLQUFLLENBQUMsMENBQTBDLEdBQUcsVUFBVSxPQUFPLE1BQU0sWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQTtTQUMzRztRQUNELE1BQU0sR0FBRyxHQUFHLEdBQUcsT0FBTyxZQUFZLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxXQUFXLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUE7UUFFakcsTUFBTSxDQUFDLEtBQUssRUFBRSxBQUFELEVBQUcsTUFBTSxDQUFDLEdBQUcsTUFBTSxlQUFlLENBQUM7WUFDOUMsR0FBRztZQUNILElBQUksRUFBRTtnQkFDSixNQUFNLEVBQUUsUUFBUTtnQkFDaEIsT0FBTyxFQUFFLEVBQUU7YUFDWjtTQUNGLENBQUMsQ0FBQTtRQUVGLHVFQUF1RTtRQUN2RSxZQUFZLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBRXZCLElBQUksS0FBSyxFQUFFO1lBQ1Qsc0RBQXNEO1lBQ3RELElBQUksTUFBTSxLQUFLLEdBQUcsRUFBRTtnQkFDbEIsS0FBSyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQTtnQkFDbkIsT0FBTyxLQUFLLENBQUE7YUFDYjtZQUNELDRFQUE0RTtZQUM1RSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ2hCLE1BQU0sSUFBSSxLQUFLLENBQUMsMENBQTBDLEdBQUcsVUFBVSxPQUFPLE1BQU0sWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQTtTQUMzRztRQUVELEtBQUssQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDbkIsT0FBTyxJQUFJLENBQUE7SUFDYixDQUFDO0lBRUQsT0FBTztRQUNMLEtBQUssQ0FBQyxHQUFHLENBQWMsT0FBZSxFQUFFLEdBQVcsRUFBRSxPQUFvQjtZQUN2RSxjQUFjLENBQUMsV0FBVyxDQUFDLENBQUE7WUFDM0IsbUJBQW1CLENBQUMsT0FBTyxFQUFFLHNCQUFzQixDQUFDLENBQUE7WUFDcEQsZ0JBQWdCLENBQUMsR0FBRyxFQUFFLHNCQUFzQixDQUFDLENBQUE7WUFFN0MsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQTtZQUVqQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO2dCQUFFLE1BQU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUVsRCxJQUFJLE1BQU0sQ0FBQyxVQUFVLElBQUksQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFO2dCQUN4QyxNQUFNLEtBQUssR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFBO2dCQUMzQixJQUFJLEtBQUssRUFBRSxNQUFNO29CQUFFLE9BQU8sSUFBSSxDQUFBO2dCQUM5Qix5RUFBeUU7Z0JBQ3pFLElBQUksS0FBSyxFQUFFLElBQUksS0FBSyxTQUFTO29CQUFFLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBVSxDQUFBO2FBQ3hFO1lBRUQsc0VBQXNFO1lBQ3RFLGtFQUFrRTtZQUNsRSxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1lBQ25DLElBQUksTUFBTTtnQkFBRSxPQUFPLE1BQU0sQ0FBQyxPQUE0QixDQUFBO1lBRXRELE1BQU0sUUFBUSxHQUFHLEVBQW9DLENBQUE7WUFDckQsUUFBUSxDQUFDLE9BQU8sR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFO2dCQUM3QixJQUFJO29CQUNGLElBQUksT0FBZSxDQUFBO29CQUNuQixJQUFJO3dCQUNGLE9BQU8sR0FBRyxNQUFNLG1CQUFtQixFQUFFLENBQUE7cUJBQ3RDO29CQUFDLE9BQU8sS0FBSyxFQUFFO3dCQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLEdBQUcsVUFBVSxPQUFPLE1BQU0sWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQTtxQkFDeEc7b0JBQ0QsTUFBTSxHQUFHLEdBQUcsR0FBRyxPQUFPLFlBQVksa0JBQWtCLENBQUMsT0FBTyxDQUFDLFdBQVcsa0JBQWtCLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQTtvQkFFakcsTUFBTSxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLEdBQUcsTUFBTSxlQUFlLENBQWUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFBO29CQUUxRSxNQUFNLE9BQU8sR0FBRyxZQUFZLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLFFBQVEsQ0FBQTtvQkFFakQsSUFBSSxLQUFLLEVBQUU7d0JBQ1Qsb0VBQW9FO3dCQUNwRSxJQUFJLE1BQU0sS0FBSyxHQUFHLEVBQUU7NEJBQ2xCLElBQUksT0FBTztnQ0FBRSxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFBOzRCQUNoQyxPQUFPLElBQUksQ0FBQTt5QkFDWjt3QkFDRCxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxHQUFHLFVBQVUsT0FBTyxNQUFNLFlBQVksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7cUJBQ3hHO29CQUVELDhGQUE4RjtvQkFDOUYsSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVMsRUFBRTt3QkFDckMsTUFBTSxJQUFJLEtBQUssQ0FBQyx1Q0FBdUMsR0FBRyxVQUFVLE9BQU8sOEJBQThCLENBQUMsQ0FBQTtxQkFDM0c7b0JBRUQsc0VBQXNFO29CQUN0RSxxQ0FBcUM7b0JBQ3JDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUE7b0JBQ2xELElBQUksT0FBTzt3QkFBRSxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7b0JBQ3BDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQTtpQkFDbEI7d0JBQVM7b0JBQ1IsSUFBSSxZQUFZLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLFFBQVE7d0JBQUUsWUFBWSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQTtpQkFDL0Q7WUFDSCxDQUFDLENBQUMsRUFBRSxDQUFBO1lBRUosWUFBWSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsUUFBUSxDQUFDLENBQUE7WUFDOUIsT0FBTyxRQUFRLENBQUMsT0FBTyxDQUFBO1FBQ3pCLENBQUM7UUFFRCxLQUFLLENBQUMsR0FBRyxDQUFjLE9BQWUsRUFBRSxHQUFXLEVBQUUsS0FBUSxFQUFFLE9BQW9CO1lBQ2pGLGNBQWMsQ0FBQyxXQUFXLENBQUMsQ0FBQTtZQUMzQixtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsc0JBQXNCLENBQUMsQ0FBQTtZQUNwRCxnQkFBZ0IsQ0FBQyxHQUFHLEVBQUUsc0JBQXNCLENBQUMsQ0FBQTtZQUU3QyxNQUFNLEVBQUUsR0FBRyxRQUFRLENBQUMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxDQUFBO1lBQ2pDLE1BQU0sSUFBSSxHQUFHLHFCQUFxQixDQUFDLEtBQUssRUFBRSx1QkFBdUIsT0FBTyxPQUFPLEdBQUcsSUFBSSxDQUFDLENBQUE7WUFDdkYsTUFBTSxlQUFlLEdBQUcsT0FBTyxFQUFFLGVBQWUsSUFBSSxNQUFNLENBQUMsZUFBZSxDQUFBO1lBRTFFLG1FQUFtRTtZQUNuRSx1RUFBdUU7WUFDdkUsa0NBQWtDO1lBQ2xDLElBQUksZUFBZSxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLEtBQUssU0FBUyxJQUFJLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsSUFBSSxLQUFLLElBQUksRUFBRTtnQkFDdkYsT0FBTyxJQUFJLENBQUE7YUFDWjtZQUVELE9BQU8sTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxFQUFFLEVBQUUsQ0FBVyxDQUFDLEVBQUUsZUFBZSxDQUFDLENBQUE7UUFDcEcsQ0FBQztRQUVELEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBZSxFQUFFLEdBQVc7WUFDdkMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxDQUFBO1lBQzNCLG1CQUFtQixDQUFDLE9BQU8sRUFBRSx5QkFBeUIsQ0FBQyxDQUFBO1lBQ3ZELGdCQUFnQixDQUFDLEdBQUcsRUFBRSx5QkFBeUIsQ0FBQyxDQUFBO1lBRWhELE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLENBQUE7WUFFakMsT0FBTyxNQUFNO2lCQUNWLE9BQU8sQ0FBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxDQUFDLGFBQWEsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQztpQkFDOUQsS0FBSyxDQUFDLENBQUMsS0FBYyxFQUFFLEVBQUU7Z0JBQ3hCLElBQUksS0FBSyxZQUFZLHNCQUFzQixFQUFFO29CQUMzQyxNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxHQUFHLFVBQVUsT0FBTyxNQUFNLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO2lCQUNyRztnQkFDRCxNQUFNLEtBQUssQ0FBQTtZQUNiLENBQUMsQ0FBQyxDQUFBO1FBQ04sQ0FBQztRQUVELEtBQUssQ0FBQyxTQUFTLENBQUMsT0FBZSxFQUFFLE9BQTBCO1lBQ3pELGNBQWMsQ0FBQyxXQUFXLENBQUMsQ0FBQTtZQUMzQixtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsNEJBQTRCLENBQUMsQ0FBQTtZQUUxRCxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPLElBQUksRUFBRSxDQUFBO1lBQy9DLElBQUksT0FBZSxDQUFBO1lBQ25CLElBQUk7Z0JBQ0YsT0FBTyxHQUFHLE1BQU0sbUJBQW1CLEVBQUUsQ0FBQTthQUN0QztZQUFDLE9BQU8sS0FBSyxFQUFFO2dCQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsNENBQTRDLE9BQU8sTUFBTSxZQUFZLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFBO2FBQ2hHO1lBQ0QsTUFBTSxLQUFLLEdBQWEsRUFBRSxDQUFBO1lBRTFCLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRTtnQkFDWixLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsa0JBQWtCLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFBO2FBQ25EO1lBRUQsSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFO2dCQUNYLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxLQUFLLEVBQUUsQ0FBQyxDQUFBO2FBQzdCO1lBRUQsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFO2dCQUNaLEtBQUssQ0FBQyxJQUFJLENBQUMsVUFBVSxNQUFNLEVBQUUsQ0FBQyxDQUFBO2FBQy9CO1lBRUQsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUM3QixNQUFNLEdBQUcsR0FBRyxLQUFLO2dCQUNmLENBQUMsQ0FBQyxHQUFHLE9BQU8sWUFBWSxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsV0FBVyxLQUFLLEVBQUU7Z0JBQ3JFLENBQUMsQ0FBQyxHQUFHLE9BQU8sWUFBWSxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFBO1lBRTlELE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQTtZQUM3QixNQUFNLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQyxHQUFHLE1BQU0sZUFBZSxDQUFrQixFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUE7WUFDekUsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFBO1lBRWQsSUFBSSxLQUFLLEVBQUU7Z0JBQ1QsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsT0FBTyxNQUFNLFlBQVksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUE7YUFDaEc7WUFFRCxNQUFNLElBQUksR0FBRyxRQUFRLEVBQUUsSUFBSSxDQUFBO1lBQzNCLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO2dCQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxPQUFPLG1DQUFtQyxDQUFDLENBQUE7YUFDeEc7WUFDRCxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLE9BQU8sS0FBSyxFQUFFLEdBQUcsS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLEtBQUssS0FBSyxTQUFTLENBQUMsRUFBRTtnQkFDckYsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsT0FBTyx1Q0FBdUMsQ0FBQyxDQUFBO2FBQzVHO1lBRUQsd0VBQXdFO1lBQ3hFLHNFQUFzRTtZQUN0RSxzRUFBc0U7WUFDdEUscUVBQXFFO1lBQ3JFLHNFQUFzRTtZQUN0RSx3RUFBd0U7WUFDeEUsd0NBQXdDO1lBQ3hDLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxFQUFFO2dCQUN4QixNQUFNLEVBQUUsR0FBRyxRQUFRLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQTtnQkFDdkMsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLElBQUksWUFBWSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7b0JBQUUsU0FBUTtnQkFDakYsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxLQUFLLFNBQVMsRUFBRTtvQkFDL0IsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUE7aUJBQ2hFO2FBQ0Y7WUFFRCxNQUFNLGVBQWUsR0FBRyxNQUFNLElBQUksQ0FBQyxDQUFBO1lBQ25DLE1BQU0sVUFBVSxHQUFHO2dCQUNqQixNQUFNLEVBQUUsUUFBUyxDQUFDLFVBQVUsRUFBRSxNQUFNLElBQUksZUFBZTtnQkFDdkQsS0FBSyxFQUFFLFFBQVMsQ0FBQyxVQUFVLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNO2FBQ2xELENBQUE7WUFFRCxPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFBO1FBQzdCLENBQUM7S0FDRixDQUFBO0FBQ0gsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZ2V0U3RvcmFnZVNlcnZlclVybCB9IGZyb20gJy4uL3N0b3JhZ2UtdXJsJ1xuaW1wb3J0IHsgYXNzZXJ0SXNTZXJ2ZXIsIHdyYXBTaWduZWRGZXRjaCB9IGZyb20gJy4uL3V0aWxzJ1xuaW1wb3J0IHtcbiAgY3JlYXRlU3RvcmFnZUNvbmZpZyxcbiAgR2V0T3B0aW9ucyxcbiAgR2V0VmFsdWVzT3B0aW9ucyxcbiAgR2V0VmFsdWVzUmVzdWx0LFxuICBNT0RVTEVfTkFNRSxcbiAgU2V0T3B0aW9ucyxcbiAgU3RvcmFnZUNvbmZpZ1N0YXRlXG59IGZyb20gJy4vY29uc3RhbnRzJ1xuaW1wb3J0IHsgY3JlYXRlVmFsdWVDYWNoZSB9IGZyb20gJy4vdmFsdWUtY2FjaGUnXG5pbXBvcnQgeyBlcnJvck1lc3NhZ2UgfSBmcm9tICcuL2Vycm9yLW1lc3NhZ2UnXG5pbXBvcnQgeyBhc3NlcnRQbGF5ZXJBZGRyZXNzLCBhc3NlcnRTdG9yYWdlS2V5LCBzZXJpYWxpemVTdG9yYWdlVmFsdWUgfSBmcm9tICcuL3NlcmlhbGl6ZSdcbmltcG9ydCB7IGNyZWF0ZVdyaXRlUXVldWUsIFN1cGVyc2VkaW5nV3JpdGVGYWlsZWQgfSBmcm9tICcuL3dyaXRlLXF1ZXVlJ1xuXG4vKipcbiAqIFBsYXllci1zY29wZWQgc3RvcmFnZSBpbnRlcmZhY2UgZm9yIGtleS12YWx1ZSBwYWlycyBmcm9tIHRoZSBTZXJ2ZXIgU2lkZSBTdG9yYWdlIHNlcnZpY2UuXG4gKiBUaGlzIGlzIE5PVCBmaWxlc3lzdGVtIHN0b3JhZ2UgLSBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgcmVtb3RlIHN0b3JhZ2Ugc2VydmljZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJUGxheWVyU3RvcmFnZSB7XG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgYSB2YWx1ZSBmcm9tIGEgcGxheWVyJ3Mgc3RvcmFnZSBieSBrZXkgZnJvbSB0aGUgU2VydmVyIFNpZGUgU3RvcmFnZSBzZXJ2aWNlLlxuICAgKlxuICAgKiBCeSBkZWZhdWx0IChjYWNoZVJlYWRzKSwgdmFsdWVzIHJlYWQgb3Igd3JpdHRlbiBkdXJpbmcgdGhlIGxhc3QgY2FjaGVNYXhBZ2VNc1xuICAgKiBhcmUgc2VydmVkIGZyb20gYSBsb2NhbCBjYWNoZSB3aXRob3V0IGEgbmV0d29yayByZXF1ZXN0LCBpbmNsdWRpbmcgY29uZmlybWVkXG4gICAqIFwibm90IGZvdW5kXCIgcmVzdWx0cy4gQ29uY3VycmVudCBnZXRzIGZvciB0aGUgc2FtZSBwbGF5ZXIgYW5kIGtleSBzaGFyZSBvbmVcbiAgICogcmVxdWVzdC4gT3V0LW9mLWJhbmQgd3JpdGVycyAoZS5nLiBDTEkgc3RvcmFnZSBjb21tYW5kcykgbWF5IG5vdCBiZSB2aXNpYmxlXG4gICAqIGZvciB1cCB0byBjYWNoZU1heEFnZU1zIOKAlCBwYXNzIHsgZnJlc2g6IHRydWUgfSB0byBmb3JjZSBhIG5ldHdvcmsgcmVhZC5cbiAgICogQSByZWFkIGZpcnN0IHdhaXRzIGZvciB3cml0ZXMgdG8gdGhlIHNhbWUga2V5IHRoYXQgd2VyZSBwZW5kaW5nIHdoZW4gaXQgd2FzXG4gICAqIGlzc3VlZCwgc28gaXQgcmVmbGVjdHMgZXZlcnkgd3JpdGUgaXNzdWVkIGJlZm9yZSBpdC5cbiAgICogQHBhcmFtIGFkZHJlc3MgLSBUaGUgcGxheWVyJ3Mgd2FsbGV0IGFkZHJlc3NcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gcmV0cmlldmVcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25hbCB7IGZyZXNoIH0gdG8gYnlwYXNzIHRoZSByZWFkIGNhY2hlXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBwYXJzZWQgSlNPTiB2YWx1ZSwgb3IgbnVsbCBpZiBub3QgZm91bmRcbiAgICogQHRocm93cyBFcnJvciBpZiB0aGUgcmVhZCBmYWlscywgc28gYSBmYWlsdXJlIGlzIG5ldmVyIG1pc3Rha2VuIGZvciBhIG1pc3Npbmcga2V5XG4gICAqIEB0aHJvd3MgVHlwZUVycm9yIGlmIHRoZSBhZGRyZXNzIG9yIGtleSBpcyBpbnZhbGlkXG4gICAqL1xuICBnZXQ8VCA9IHVua25vd24+KGFkZHJlc3M6IHN0cmluZywga2V5OiBzdHJpbmcsIG9wdGlvbnM/OiBHZXRPcHRpb25zKTogUHJvbWlzZTxUIHwgbnVsbD5cblxuICAvKipcbiAgICogU3RvcmVzIGEgdmFsdWUgaW4gYSBwbGF5ZXIncyBzdG9yYWdlIGluIHRoZSBTZXJ2ZXIgU2lkZSBTdG9yYWdlIHNlcnZpY2UuXG4gICAqIEBwYXJhbSBhZGRyZXNzIC0gVGhlIHBsYXllcidzIHdhbGxldCBhZGRyZXNzXG4gICAqIEBwYXJhbSBrZXkgLSBUaGUga2V5IHRvIHN0b3JlIHRoZSB2YWx1ZSB1bmRlclxuICAgKiBAcGFyYW0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gc3RvcmUgKHdpbGwgYmUgSlNPTiBzZXJpYWxpemVkKVxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIHsgc2tpcElmVW5jaGFuZ2VkIH0gdG8gc2tpcCB0aGUgbmV0d29yayB3cml0ZSB3aGVuIHRoZSB2YWx1ZSBpcyBhbHJlYWR5IHN0b3JlZFxuICAgKiBAcmV0dXJucyB0cnVlIG9uY2Ugc3RvcmVkLCBvciBvbmNlIGEgbmV3ZXIgd3JpdGUgdGhhdCByZXBsYWNlZCB0aGlzIG9uZSBsYW5kc1xuICAgKiAocmFwaWQgd3JpdGVzIGNvYWxlc2NlKTsgZmFsc2UgaWYgdGhlIHdyaXRlLCBvciBpdHMgcmVwbGFjZW1lbnQsIGZhaWxzXG4gICAqIEB0aHJvd3MgVHlwZUVycm9yIGlmIHRoZSBhZGRyZXNzIG9yIGtleSBpcyBpbnZhbGlkLCBvciB0aGUgdmFsdWUgY2Fubm90IGJlIHN0b3JlZCB1bmNoYW5nZWQ6IHVuZGVmaW5lZCxcbiAgICogY2lyY3VsYXIsIGEgQmlnSW50LCBvciBjb250YWluaW5nIGEgZnVuY3Rpb24sIGEgc3ltYm9sLCBhIG5vbi1maW5pdGUgbnVtYmVyLCBhbiB1bmRlZmluZWQgYXJyYXlcbiAgICogZWxlbWVudCBvciBob2xlLCBhIE1hcCwgU2V0LCB0eXBlZCBhcnJheSBvciBvdGhlciBidWlsdC1pbiB3aXRoIG5vIEpTT04gZm9ybSwgb3IgdGV4dCB3aXRoIGFuXG4gICAqIHVucGFpcmVkIHN1cnJvZ2F0ZSBvciBhIE5VTCBjaGFyYWN0ZXIuIEFuIHVuZGVmaW5lZCBvYmplY3QgcHJvcGVydHkgaXMgZHJvcHBlZDsgYSBEYXRlIGlzXG4gICAqIHN0b3JlZCBhcyBpdHMgSVNPIHN0cmluZyBhbmQgYSBjbGFzcyBpbnN0YW5jZSBhcyBpdHMgb3duIGVudW1lcmFibGUgZGF0YS5cbiAgICovXG4gIHNldDxUID0gdW5rbm93bj4oYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZywgdmFsdWU6IFQsIG9wdGlvbnM/OiBTZXRPcHRpb25zKTogUHJvbWlzZTxib29sZWFuPlxuXG4gIC8qKlxuICAgKiBEZWxldGVzIGEgdmFsdWUgZnJvbSBhIHBsYXllcidzIHN0b3JhZ2UgaW4gdGhlIFNlcnZlciBTaWRlIFN0b3JhZ2Ugc2VydmljZS5cbiAgICogQHBhcmFtIGFkZHJlc3MgLSBUaGUgcGxheWVyJ3Mgd2FsbGV0IGFkZHJlc3NcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gZGVsZXRlXG4gICAqIEByZXR1cm5zIHRydWUgb25jZSBhcHBsaWVkLCBvciBvbmNlIGEgbmV3ZXIgd3JpdGUgdGhhdCByZXBsYWNlZCBpdCBsYW5kcztcbiAgICogZmFsc2Ugb25seSBmb3IgYSBjb25maXJtZWQgNDA0XG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIGRlbGV0ZSwgb3IgaXRzIHJlcGxhY2VtZW50LCBmYWlsczsgYSBmYWlsdXJlIGlzIG5ldmVyIHJlcG9ydGVkIGFzIGFic2VuY2VcbiAgICogQHRocm93cyBUeXBlRXJyb3IgaWYgdGhlIGFkZHJlc3Mgb3Iga2V5IGlzIGludmFsaWRcbiAgICovXG4gIGRlbGV0ZShhZGRyZXNzOiBzdHJpbmcsIGtleTogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPlxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGtleS12YWx1ZSBlbnRyaWVzIGZyb20gYSBwbGF5ZXIncyBzdG9yYWdlLCBvcHRpb25hbGx5IGZpbHRlcmVkIGJ5IHByZWZpeC5cbiAgICogU3VwcG9ydHMgcGFnaW5hdGlvbiB2aWEgbGltaXQgYW5kIG9mZnNldC5cbiAgICogQHBhcmFtIGFkZHJlc3MgLSBUaGUgcGxheWVyJ3Mgd2FsbGV0IGFkZHJlc3NcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25hbCB7IHByZWZpeCwgbGltaXQsIG9mZnNldCB9IGZvciBmaWx0ZXJpbmcgYW5kIHBhZ2luYXRpb24uXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHsgZGF0YSwgcGFnaW5hdGlvbjogeyBvZmZzZXQsIHRvdGFsIH0gfSBmb3IgcGFnaW5hdGlvbiBVSVxuICAgKiBAdGhyb3dzIEVycm9yIGlmIHRoZSByZWFkIGZhaWxzLCBzbyBhIGZhaWx1cmUgaXMgbmV2ZXIgbWlzdGFrZW4gZm9yIGFuIGVtcHR5IHBhZ2VcbiAgICogQHRocm93cyBUeXBlRXJyb3IgaWYgdGhlIGFkZHJlc3MgaXMgaW52YWxpZFxuICAgKi9cbiAgZ2V0VmFsdWVzKGFkZHJlc3M6IHN0cmluZywgb3B0aW9ucz86IEdldFZhbHVlc09wdGlvbnMpOiBQcm9taXNlPEdldFZhbHVlc1Jlc3VsdD5cbn1cblxuLyoqXG4gKiBDcmVhdGVzIHBsYXllci1zY29wZWQgc3RvcmFnZSB0aGF0IHByb3ZpZGVzIG1ldGhvZHMgdG8gaW50ZXJhY3Qgd2l0aFxuICogcGxheWVyLXNwZWNpZmljIGtleS12YWx1ZSBwYWlycyBmcm9tIHRoZSBTZXJ2ZXIgU2lkZSBTdG9yYWdlIHNlcnZpY2UuXG4gKiBUaGlzIG1vZHVsZSBvbmx5IHdvcmtzIHdoZW4gcnVubmluZyBvbiBzZXJ2ZXItc2lkZSBzY2VuZXMuXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGNvbnN0IGNyZWF0ZVBsYXllclN0b3JhZ2UgPSAoY29uZmlnOiBTdG9yYWdlQ29uZmlnU3RhdGUgPSBjcmVhdGVTdG9yYWdlQ29uZmlnKCkpOiBJUGxheWVyU3RvcmFnZSA9PiB7XG4gIGNvbnN0IGNhY2hlID0gY3JlYXRlVmFsdWVDYWNoZShjb25maWcpXG4gIC8vIEVhY2ggaW4tZmxpZ2h0IEdFVCBpcyB0cmFja2VkIGJ5IGEgd3JhcHBlciBvYmplY3Qgd2hvc2UgaWRlbnRpdHkgbWFya3NcbiAgLy8gb3duZXJzaGlwOiBhIGxhbmRlZCB3cml0ZSBkZXRhY2hlcyB0aGUgcGVuZGluZyBHRVQgc28gaXRzIHN0YWxlIHJlc3BvbnNlIGlzIG5vdCBjYWNoZWQuXG4gIGNvbnN0IGluZmxpZ2h0R2V0cyA9IG5ldyBNYXA8c3RyaW5nLCB7IHByb21pc2U6IFByb21pc2U8dW5rbm93bj4gfT4oKVxuXG4gIC8vIExvd2VyY2FzZWQgc28gY2hlY2tzdW1tZWQgY2FsbGVycyBzaGFyZSBhbiBlbnRyeS5cbiAgY29uc3QgY2FjaGVLZXkgPSAoYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZykgPT4gSlNPTi5zdHJpbmdpZnkoW2FkZHJlc3MudG9Mb3dlckNhc2UoKSwga2V5XSlcblxuICAvLyBXcml0ZXMgdG8gdGhlIHNhbWUgcGxheWVyIGtleSBhcmUgc2VyaWFsaXplZCAoYW5kIHJhcGlkIG9uZXMgY29hbGVzY2VkIHRvXG4gIC8vIHRoZSBsYXRlc3QgdmFsdWUpIHNvIHRoZSBzZXJ2aWNlIGNvbW1pdHMgdGhlbSBpbiBpc3N1ZSBvcmRlciDigJQgb3ZlcmxhcHBpbmdcbiAgLy8gUFVUcyB3b3VsZCBvdGhlcndpc2UgbGVhdmUgYm90aCB0aGUga2VwdCB2YWx1ZSBhbmQgdGhlIGNhY2hlZCB2YWx1ZSB0b1xuICAvLyByZXNwb25zZS1vcmRlciBjaGFuY2UuIEtleWVkIGJ5IHRoZSBzYW1lIGNhc2UtaW5zZW5zaXRpdmUgY2FjaGUga2V5LlxuICBjb25zdCB3cml0ZXMgPSBjcmVhdGVXcml0ZVF1ZXVlKClcblxuICBhc3luYyBmdW5jdGlvbiBleGVjdXRlU2V0KGFkZHJlc3M6IHN0cmluZywga2V5OiBzdHJpbmcsIGNrOiBzdHJpbmcsIGJvZHk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGxldCBiYXNlVXJsOiBzdHJpbmdcbiAgICB0cnkge1xuICAgICAgYmFzZVVybCA9IGF3YWl0IGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjYWNoZS5kZWxldGUoY2spXG4gICAgICBjb25zb2xlLmVycm9yKGBGYWlsZWQgdG8gc2V0IHBsYXllciBzdG9yYWdlIHZhbHVlICcke2tleX0nIGZvciAnJHthZGRyZXNzfSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIGNvbnN0IHVybCA9IGAke2Jhc2VVcmx9L3BsYXllcnMvJHtlbmNvZGVVUklDb21wb25lbnQoYWRkcmVzcyl9L3ZhbHVlcy8ke2VuY29kZVVSSUNvbXBvbmVudChrZXkpfWBcblxuICAgIGNvbnN0IFtlcnJvcl0gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2goe1xuICAgICAgdXJsLFxuICAgICAgaW5pdDoge1xuICAgICAgICBtZXRob2Q6ICdQVVQnLFxuICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgJ2NvbnRlbnQtdHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgICB9LFxuICAgICAgICBib2R5XG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIEVpdGhlciB3YXkgdGhlIGVudHJ5IGNoYW5nZWQgc2VydmVyLXNpZGUgKG9yIG1heSBoYXZlKTogZGV0YWNoIGFueVxuICAgIC8vIG92ZXJsYXBwaW5nIGluLWZsaWdodCBHRVQgc28gaXRzIHN0YWxlIHJlc3BvbnNlIGlzIG5vdCBjYWNoZWQuXG4gICAgaW5mbGlnaHRHZXRzLmRlbGV0ZShjaylcblxuICAgIGlmIChlcnJvcikge1xuICAgICAgLy8gVGhlIFBVVCBtYXkgaGF2ZSByZWFjaGVkIHRoZSBzZXJ2ZXIsIHNvIHRoZSBjYWNoZWQgYm9keSBpcyBub1xuICAgICAgLy8gbG9uZ2VyIHJlbGlhYmxlLlxuICAgICAgY2FjaGUuZGVsZXRlKGNrKVxuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgY2FjaGUuc2V0KGNrLCB7IGJvZHkgfSlcbiAgICByZXR1cm4gdHJ1ZVxuICB9XG5cbiAgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZURlbGV0ZShhZGRyZXNzOiBzdHJpbmcsIGtleTogc3RyaW5nLCBjazogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgbGV0IGJhc2VVcmw6IHN0cmluZ1xuICAgIHRyeSB7XG4gICAgICBiYXNlVXJsID0gYXdhaXQgZ2V0U3RvcmFnZVNlcnZlclVybCgpXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNhY2hlLmRlbGV0ZShjaylcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBwbGF5ZXIgc3RvcmFnZSB2YWx1ZSAnJHtrZXl9JyBmb3IgJyR7YWRkcmVzc30nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICB9XG4gICAgY29uc3QgdXJsID0gYCR7YmFzZVVybH0vcGxheWVycy8ke2VuY29kZVVSSUNvbXBvbmVudChhZGRyZXNzKX0vdmFsdWVzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KGtleSl9YFxuXG4gICAgY29uc3QgW2Vycm9yLCAsIHN0YXR1c10gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2goe1xuICAgICAgdXJsLFxuICAgICAgaW5pdDoge1xuICAgICAgICBtZXRob2Q6ICdERUxFVEUnLFxuICAgICAgICBoZWFkZXJzOiB7fVxuICAgICAgfVxuICAgIH0pXG5cbiAgICAvLyBEZXRhY2ggYWdhaW46IGEgR0VUIG1heSBoYXZlIHN0YXJ0ZWQgd2hpbGUgdGhlIERFTEVURSB3YXMgaW4gZmxpZ2h0LlxuICAgIGluZmxpZ2h0R2V0cy5kZWxldGUoY2spXG5cbiAgICBpZiAoZXJyb3IpIHtcbiAgICAgIC8vIEEgNDA0IHN0aWxsIGNvbmZpcm1zIHRoZSBrZXkgaXMgYWJzZW50IHNlcnZlci1zaWRlLlxuICAgICAgaWYgKHN0YXR1cyA9PT0gNDA0KSB7XG4gICAgICAgIGNhY2hlLnNldEFic2VudChjaylcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgICAvLyBUaGUgREVMRVRFIG1heSBoYXZlIHJlYWNoZWQgdGhlIHNlcnZlciwgc28gdGhlIGNhY2hlZCBib2R5IGlzIHVucmVsaWFibGUuXG4gICAgICBjYWNoZS5kZWxldGUoY2spXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBkZWxldGUgcGxheWVyIHN0b3JhZ2UgdmFsdWUgJyR7a2V5fScgZm9yICcke2FkZHJlc3N9JzogJHtlcnJvck1lc3NhZ2UoZXJyb3IpfWApXG4gICAgfVxuXG4gICAgY2FjaGUuc2V0QWJzZW50KGNrKVxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGFzeW5jIGdldDxUID0gdW5rbm93bj4oYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZywgb3B0aW9ucz86IEdldE9wdGlvbnMpOiBQcm9taXNlPFQgfCBudWxsPiB7XG4gICAgICBhc3NlcnRJc1NlcnZlcihNT0RVTEVfTkFNRSlcbiAgICAgIGFzc2VydFBsYXllckFkZHJlc3MoYWRkcmVzcywgJ1N0b3JhZ2UucGxheWVyLmdldCgpJylcbiAgICAgIGFzc2VydFN0b3JhZ2VLZXkoa2V5LCAnU3RvcmFnZS5wbGF5ZXIuZ2V0KCknKVxuXG4gICAgICBjb25zdCBjayA9IGNhY2hlS2V5KGFkZHJlc3MsIGtleSlcblxuICAgICAgaWYgKHdyaXRlcy5pc1BlbmRpbmcoY2spKSBhd2FpdCB3cml0ZXMuc2V0dGxlZChjaylcblxuICAgICAgaWYgKGNvbmZpZy5jYWNoZVJlYWRzICYmICFvcHRpb25zPy5mcmVzaCkge1xuICAgICAgICBjb25zdCBlbnRyeSA9IGNhY2hlLmdldChjaylcbiAgICAgICAgaWYgKGVudHJ5Py5hYnNlbnQpIHJldHVybiBudWxsXG4gICAgICAgIC8vIFBhcnNlIHBlciBoaXQgc28gZWFjaCBjYWxsZXIgZ2V0cyBhIGZyZXNoIG9iamVjdCAobm8gc2hhcmVkIG11dGF0aW9uKS5cbiAgICAgICAgaWYgKGVudHJ5Py5ib2R5ICE9PSB1bmRlZmluZWQpIHJldHVybiBKU09OLnBhcnNlKGVudHJ5LmJvZHkpLnZhbHVlIGFzIFRcbiAgICAgIH1cblxuICAgICAgLy8gQ29hbGVzY2UgY29uY3VycmVudCBnZXRzIChldmVuIGZyZXNoIG9uZXM6IGFuIGluLWZsaWdodCByZXNwb25zZSBpc1xuICAgICAgLy8gbWlsbGlzZWNvbmRzIG9sZCwgbm90IFRUTC1zdGFsZSkgaW50byBhIHNpbmdsZSBuZXR3b3JrIHJlcXVlc3QuXG4gICAgICBjb25zdCBqb2luZWQgPSBpbmZsaWdodEdldHMuZ2V0KGNrKVxuICAgICAgaWYgKGpvaW5lZCkgcmV0dXJuIGpvaW5lZC5wcm9taXNlIGFzIFByb21pc2U8VCB8IG51bGw+XG5cbiAgICAgIGNvbnN0IGluZmxpZ2h0ID0ge30gYXMgeyBwcm9taXNlOiBQcm9taXNlPFQgfCBudWxsPiB9XG4gICAgICBpbmZsaWdodC5wcm9taXNlID0gKGFzeW5jICgpID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsZXQgYmFzZVVybDogc3RyaW5nXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGJhc2VVcmwgPSBhd2FpdCBnZXRTdG9yYWdlU2VydmVyVXJsKClcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHBsYXllciBzdG9yYWdlIHZhbHVlICcke2tleX0nIGZvciAnJHthZGRyZXNzfSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB1cmwgPSBgJHtiYXNlVXJsfS9wbGF5ZXJzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KGFkZHJlc3MpfS92YWx1ZXMvJHtlbmNvZGVVUklDb21wb25lbnQoa2V5KX1gXG5cbiAgICAgICAgICBjb25zdCBbZXJyb3IsIGRhdGEsIHN0YXR1c10gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2g8eyB2YWx1ZTogVCB9Pih7IHVybCB9KVxuXG4gICAgICAgICAgY29uc3QgaXNPd25lciA9IGluZmxpZ2h0R2V0cy5nZXQoY2spID09PSBpbmZsaWdodFxuXG4gICAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICAvLyBBIGNvbmZpcm1lZCA0MDQgaXMgYSBmaXJzdC1jbGFzcyBcImFic2VudFwiIG91dGNvbWUsIG5vdCBhIGZhaWx1cmUuXG4gICAgICAgICAgICBpZiAoc3RhdHVzID09PSA0MDQpIHtcbiAgICAgICAgICAgICAgaWYgKGlzT3duZXIpIGNhY2hlLnNldEFic2VudChjaylcbiAgICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBwbGF5ZXIgc3RvcmFnZSB2YWx1ZSAnJHtrZXl9JyBmb3IgJyR7YWRkcmVzc30nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyB3cmFwU2lnbmVkRmV0Y2ggcGFyc2VzIGFuIGVtcHR5IDJ4eCBib2R5IGFzIHt9OiBhIG1pc3NpbmcgdmFsdWUgaXMgYSBmYXVsdCwgbm90IGFuIGFic2VuY2UuXG4gICAgICAgICAgaWYgKCFkYXRhIHx8IGRhdGEudmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHBsYXllciBzdG9yYWdlIHZhbHVlICcke2tleX0nIGZvciAnJHthZGRyZXNzfSc6IHJlc3BvbnNlIGNhcnJpZWQgbm8gdmFsdWVgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFNhbWUgc2VyaWFsaXphdGlvbiBzaGFwZSBhcyBzZXQoKSdzIFBVVCBib2R5LCBzbyBhIHJlYWQgZm9sbG93ZWQgYnlcbiAgICAgICAgICAvLyBhbiB1bmNoYW5nZWQgd3JpdGUgY2FuIGJlIHNraXBwZWQuXG4gICAgICAgICAgY29uc3QgYm9keSA9IEpTT04uc3RyaW5naWZ5KHsgdmFsdWU6IGRhdGEudmFsdWUgfSlcbiAgICAgICAgICBpZiAoaXNPd25lcikgY2FjaGUuc2V0KGNrLCB7IGJvZHkgfSlcbiAgICAgICAgICByZXR1cm4gZGF0YS52YWx1ZVxuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIGlmIChpbmZsaWdodEdldHMuZ2V0KGNrKSA9PT0gaW5mbGlnaHQpIGluZmxpZ2h0R2V0cy5kZWxldGUoY2spXG4gICAgICAgIH1cbiAgICAgIH0pKClcblxuICAgICAgaW5mbGlnaHRHZXRzLnNldChjaywgaW5mbGlnaHQpXG4gICAgICByZXR1cm4gaW5mbGlnaHQucHJvbWlzZVxuICAgIH0sXG5cbiAgICBhc3luYyBzZXQ8VCA9IHVua25vd24+KGFkZHJlc3M6IHN0cmluZywga2V5OiBzdHJpbmcsIHZhbHVlOiBULCBvcHRpb25zPzogU2V0T3B0aW9ucyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgYXNzZXJ0SXNTZXJ2ZXIoTU9EVUxFX05BTUUpXG4gICAgICBhc3NlcnRQbGF5ZXJBZGRyZXNzKGFkZHJlc3MsICdTdG9yYWdlLnBsYXllci5zZXQoKScpXG4gICAgICBhc3NlcnRTdG9yYWdlS2V5KGtleSwgJ1N0b3JhZ2UucGxheWVyLnNldCgpJylcblxuICAgICAgY29uc3QgY2sgPSBjYWNoZUtleShhZGRyZXNzLCBrZXkpXG4gICAgICBjb25zdCBib2R5ID0gc2VyaWFsaXplU3RvcmFnZVZhbHVlKHZhbHVlLCBgU3RvcmFnZS5wbGF5ZXIuc2V0KCcke2FkZHJlc3N9JywgJyR7a2V5fScpYClcbiAgICAgIGNvbnN0IHNraXBJZlVuY2hhbmdlZCA9IG9wdGlvbnM/LnNraXBJZlVuY2hhbmdlZCA/PyBjb25maWcuc2tpcElmVW5jaGFuZ2VkXG5cbiAgICAgIC8vIERlZHVwIGFnYWluc3QgY29uZmlybWVkIHN0YXRlIG9ubHkgd2hpbGUgbm8gd3JpdGUgaXMgcGVuZGluZyDigJQgYVxuICAgICAgLy8gcGVuZGluZyB3cml0ZSBtYWtlcyB0aGUgY2FjaGUgbW9tZW50YXJpbHkgc3RhbGU7IGVucXVldWUoKSBjb2FsZXNjZXNcbiAgICAgIC8vIGFnYWluc3QgcGVuZGluZyB3cml0ZXMgaW5zdGVhZC5cbiAgICAgIGlmIChza2lwSWZVbmNoYW5nZWQgJiYgd3JpdGVzLnBlbmRpbmcoY2spID09PSB1bmRlZmluZWQgJiYgY2FjaGUuZ2V0KGNrKT8uYm9keSA9PT0gYm9keSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gd3JpdGVzLmVucXVldWUoY2ssIGJvZHksIChiKSA9PiBleGVjdXRlU2V0KGFkZHJlc3MsIGtleSwgY2ssIGIgYXMgc3RyaW5nKSwgc2tpcElmVW5jaGFuZ2VkKVxuICAgIH0sXG5cbiAgICBhc3luYyBkZWxldGUoYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgYXNzZXJ0SXNTZXJ2ZXIoTU9EVUxFX05BTUUpXG4gICAgICBhc3NlcnRQbGF5ZXJBZGRyZXNzKGFkZHJlc3MsICdTdG9yYWdlLnBsYXllci5kZWxldGUoKScpXG4gICAgICBhc3NlcnRTdG9yYWdlS2V5KGtleSwgJ1N0b3JhZ2UucGxheWVyLmRlbGV0ZSgpJylcblxuICAgICAgY29uc3QgY2sgPSBjYWNoZUtleShhZGRyZXNzLCBrZXkpXG5cbiAgICAgIHJldHVybiB3cml0ZXNcbiAgICAgICAgLmVucXVldWUoY2ssIG51bGwsICgpID0+IGV4ZWN1dGVEZWxldGUoYWRkcmVzcywga2V5LCBjayksIHRydWUpXG4gICAgICAgIC5jYXRjaCgoZXJyb3I6IHVua25vd24pID0+IHtcbiAgICAgICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTdXBlcnNlZGluZ1dyaXRlRmFpbGVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBkZWxldGUgcGxheWVyIHN0b3JhZ2UgdmFsdWUgJyR7a2V5fScgZm9yICcke2FkZHJlc3N9JzogJHtlcnJvci5tZXNzYWdlfWApXG4gICAgICAgICAgfVxuICAgICAgICAgIHRocm93IGVycm9yXG4gICAgICAgIH0pXG4gICAgfSxcblxuICAgIGFzeW5jIGdldFZhbHVlcyhhZGRyZXNzOiBzdHJpbmcsIG9wdGlvbnM/OiBHZXRWYWx1ZXNPcHRpb25zKTogUHJvbWlzZTxHZXRWYWx1ZXNSZXN1bHQ+IHtcbiAgICAgIGFzc2VydElzU2VydmVyKE1PRFVMRV9OQU1FKVxuICAgICAgYXNzZXJ0UGxheWVyQWRkcmVzcyhhZGRyZXNzLCAnU3RvcmFnZS5wbGF5ZXIuZ2V0VmFsdWVzKCknKVxuXG4gICAgICBjb25zdCB7IHByZWZpeCwgbGltaXQsIG9mZnNldCB9ID0gb3B0aW9ucyA/PyB7fVxuICAgICAgbGV0IGJhc2VVcmw6IHN0cmluZ1xuICAgICAgdHJ5IHtcbiAgICAgICAgYmFzZVVybCA9IGF3YWl0IGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHBsYXllciBzdG9yYWdlIHZhbHVlcyBmb3IgJyR7YWRkcmVzc30nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdXG5cbiAgICAgIGlmICghIXByZWZpeCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBwcmVmaXg9JHtlbmNvZGVVUklDb21wb25lbnQocHJlZml4KX1gKVxuICAgICAgfVxuXG4gICAgICBpZiAoISFsaW1pdCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBsaW1pdD0ke2xpbWl0fWApXG4gICAgICB9XG5cbiAgICAgIGlmICghIW9mZnNldCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBvZmZzZXQ9JHtvZmZzZXR9YClcbiAgICAgIH1cblxuICAgICAgY29uc3QgcXVlcnkgPSBwYXJ0cy5qb2luKCcmJylcbiAgICAgIGNvbnN0IHVybCA9IHF1ZXJ5XG4gICAgICAgID8gYCR7YmFzZVVybH0vcGxheWVycy8ke2VuY29kZVVSSUNvbXBvbmVudChhZGRyZXNzKX0vdmFsdWVzPyR7cXVlcnl9YFxuICAgICAgICA6IGAke2Jhc2VVcmx9L3BsYXllcnMvJHtlbmNvZGVVUklDb21wb25lbnQoYWRkcmVzcyl9L3ZhbHVlc2BcblxuICAgICAgY29uc3Qgd2F0Y2hlciA9IGNhY2hlLndhdGNoKClcbiAgICAgIGNvbnN0IFtlcnJvciwgcmVzcG9uc2VdID0gYXdhaXQgd3JhcFNpZ25lZEZldGNoPEdldFZhbHVlc1Jlc3VsdD4oeyB1cmwgfSlcbiAgICAgIHdhdGNoZXIuc3RvcCgpXG5cbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgcGxheWVyIHN0b3JhZ2UgdmFsdWVzIGZvciAnJHthZGRyZXNzfSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBkYXRhID0gcmVzcG9uc2U/LmRhdGFcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgcGxheWVyIHN0b3JhZ2UgdmFsdWVzIGZvciAnJHthZGRyZXNzfSc6IHJlc3BvbnNlIGNhcnJpZWQgbm8gZGF0YSBhcnJheWApXG4gICAgICB9XG4gICAgICBpZiAoZGF0YS5zb21lKChlbnRyeSkgPT4gdHlwZW9mIGVudHJ5Py5rZXkgIT09ICdzdHJpbmcnIHx8IGVudHJ5LnZhbHVlID09PSB1bmRlZmluZWQpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBwbGF5ZXIgc3RvcmFnZSB2YWx1ZXMgZm9yICcke2FkZHJlc3N9JzogcmVzcG9uc2UgY2FycmllZCBhIG1hbGZvcm1lZCBlbnRyeWApXG4gICAgICB9XG5cbiAgICAgIC8vIFNlZWQgdGhlIHBlci1rZXkgY2FjaGUgc28gc3Vic2VxdWVudCBnZXQoKS9zZXQoKSBvbiByZXR1cm5lZCBrZXlzIGNhblxuICAgICAgLy8gc2tpcCB0aGUgbmV0d29yay4gT25seSBrZXlzIHdpdGggbm8gbGl2ZSBlbnRyeSBhbmQgbm8gcGVuZGluZyB3cml0ZVxuICAgICAgLy8gYXJlIHNlZWRlZDogZXhpc3RpbmcgcGVyLWtleSBzdGF0ZSBjb21lcyBmcm9tIGEgY29uZmlybWVkIG9wZXJhdGlvblxuICAgICAgLy8gdGhhdCB0aGlzIHBhZ2Ugc25hcHNob3Qg4oCUIHdob3NlIHJlcXVlc3Qgc3RhcnRlZCBlYXJsaWVyIOKAlCBtdXN0IG5vdFxuICAgICAgLy8gY2xvYmJlciB3aXRoIHN0YWxlIGRhdGEuIEFic2VuY2UgaXMgbmV2ZXIgc2VlZGVkIChwcmVmaXgvcGFnaW5hdGlvblxuICAgICAgLy8gbWFrZSBpdCBub24tYXV0aG9yaXRhdGl2ZSkuIEEgcGFnZSBsYXJnZXIgdGhhbiBjYWNoZU1heEVudHJpZXMgY2h1cm5zXG4gICAgICAvLyB0aGUgY2FjaGU7IGVudHJpZXMgcmVwb3B1bGF0ZSBsYXppbHkuXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGRhdGEpIHtcbiAgICAgICAgY29uc3QgY2sgPSBjYWNoZUtleShhZGRyZXNzLCBlbnRyeS5rZXkpXG4gICAgICAgIGlmICh3YXRjaGVyLm11dGF0ZWQoY2spIHx8IHdyaXRlcy5pc1BlbmRpbmcoY2spIHx8IGluZmxpZ2h0R2V0cy5oYXMoY2spKSBjb250aW51ZVxuICAgICAgICBpZiAoY2FjaGUuZ2V0KGNrKSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY2FjaGUuc2V0KGNrLCB7IGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgdmFsdWU6IGVudHJ5LnZhbHVlIH0pIH0pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVxdWVzdGVkT2Zmc2V0ID0gb2Zmc2V0ID8/IDBcbiAgICAgIGNvbnN0IHBhZ2luYXRpb24gPSB7XG4gICAgICAgIG9mZnNldDogcmVzcG9uc2UhLnBhZ2luYXRpb24/Lm9mZnNldCA/PyByZXF1ZXN0ZWRPZmZzZXQsXG4gICAgICAgIHRvdGFsOiByZXNwb25zZSEucGFnaW5hdGlvbj8udG90YWwgPz8gZGF0YS5sZW5ndGhcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHsgZGF0YSwgcGFnaW5hdGlvbiB9XG4gICAgfVxuICB9XG59XG4iXX0=