@dcl/sdk 7.30.2-37804519171.commit-60e3717 → 7.30.2-37807847179.commit-24c42ea
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/atom.d.ts +19 -0
- package/atom.js +83 -0
- package/future.d.ts +8 -0
- package/future.js +26 -0
- package/internal/transports/rendererTransport.js +5 -2
- package/network/binary-message-bus.d.ts +6 -3
- package/network/binary-message-bus.js +9 -5
- package/network/chunking.d.ts +5 -0
- package/network/chunking.js +38 -0
- package/network/events/implementation.d.ts +93 -0
- package/network/events/implementation.js +221 -0
- package/network/events/index.d.ts +42 -0
- package/network/events/index.js +43 -0
- package/network/events/protocol.d.ts +27 -0
- package/network/events/protocol.js +66 -0
- package/network/events/registry.d.ts +8 -0
- package/network/events/registry.js +3 -0
- package/network/index.d.ts +8 -2
- package/network/index.js +16 -3
- package/network/message-bus-sync.d.ts +16 -3
- package/network/message-bus-sync.js +164 -103
- package/network/server/index.d.ts +14 -0
- package/network/server/index.js +256 -0
- package/network/server/utils.d.ts +18 -0
- package/network/server/utils.js +135 -0
- package/network/state.js +3 -5
- package/package.json +6 -6
- package/server/env-var.d.ts +15 -0
- package/server/env-var.js +31 -0
- package/server/index.d.ts +2 -0
- package/server/index.js +3 -0
- package/server/storage/constants.d.ts +80 -0
- package/server/storage/constants.js +19 -0
- package/server/storage/error-message.d.ts +1 -0
- package/server/storage/error-message.js +9 -0
- package/server/storage/index.d.ts +54 -0
- package/server/storage/index.js +60 -0
- package/server/storage/player.d.ts +60 -0
- package/server/storage/player.js +244 -0
- package/server/storage/scene.d.ts +55 -0
- package/server/storage/scene.js +232 -0
- package/server/storage/serialize.d.ts +1 -0
- package/server/storage/serialize.js +135 -0
- package/server/storage/value-cache.d.ts +1 -0
- package/server/storage/value-cache.js +67 -0
- package/server/storage/write-queue.d.ts +1 -0
- package/server/storage/write-queue.js +113 -0
- package/server/storage-url.d.ts +13 -0
- package/server/storage-url.js +42 -0
- package/server/utils.d.ts +35 -0
- package/server/utils.js +69 -0
- package/src/atom.ts +98 -0
- package/src/future.ts +38 -0
- package/src/internal/transports/rendererTransport.ts +4 -1
- package/src/network/binary-message-bus.ts +9 -4
- package/src/network/chunking.ts +45 -0
- package/src/network/events/implementation.ts +271 -0
- package/src/network/events/index.ts +48 -0
- package/src/network/events/protocol.ts +94 -0
- package/src/network/events/registry.ts +18 -0
- package/src/network/index.ts +40 -3
- package/src/network/message-bus-sync.ts +180 -112
- package/src/network/server/index.ts +341 -0
- package/src/network/server/utils.ts +189 -0
- package/src/network/state.ts +3 -4
- package/src/server/env-var.ts +36 -0
- package/src/server/index.ts +12 -0
- package/src/server/storage/constants.ts +112 -0
- package/src/server/storage/error-message.ts +8 -0
- package/src/server/storage/index.ts +83 -0
- package/src/server/storage/player.ts +345 -0
- package/src/server/storage/scene.ts +324 -0
- package/src/server/storage/serialize.ts +132 -0
- package/src/server/storage/value-cache.ts +119 -0
- package/src/server/storage/write-queue.ts +174 -0
- package/src/server/storage-url.ts +49 -0
- package/src/server/utils.ts +83 -0
|
@@ -0,0 +1,244 @@
|
|
|
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxheWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZlci9zdG9yYWdlL3BsYXllci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUMxRCxPQUFPLEVBQ0wsbUJBQW1CLEVBSW5CLFdBQVcsRUFHWixNQUFNLGFBQWEsQ0FBQTtBQUNwQixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFDaEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQzlDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxnQkFBZ0IsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUMxRixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFpRXhFOzs7OztHQUtHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxTQUE2QixtQkFBbUIsRUFBRSxFQUFrQixFQUFFO0lBQ3hHLE1BQU0sS0FBSyxHQUFHLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQ3RDLHlFQUF5RTtJQUN6RSwwRkFBMEY7SUFDMUYsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQXlDLENBQUE7SUFFckUsb0RBQW9EO0lBQ3BELE1BQU0sUUFBUSxHQUFHLENBQUMsT0FBZSxFQUFFLEdBQVcsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBRS9GLDRFQUE0RTtJQUM1RSw2RUFBNkU7SUFDN0UseUVBQXlFO0lBQ3pFLHVFQUF1RTtJQUN2RSxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxD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QUFNLElBQUksQ0FBQyxDQUFBO1lBQ25DLE1BQU0sVUFBVSxHQUFHO2dCQUNqQixNQUFNLEVBQUUsUUFBUyxDQUFDLFVBQVUsRUFBRSxNQUFNLElBQUksZUFBZTtnQkFDdkQsS0FBSyxFQUFFLFFBQVMsQ0FBQyxVQUFVLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNO2FBQ2xELENBQUE7WUFFRCxPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFBO1FBQzdCLENBQUM7S0FDRixDQUFBO0FBQ0gsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZ2V0U3RvcmFnZVNlcnZlclVybCB9IGZyb20gJy4uL3N0b3JhZ2UtdXJsJ1xuaW1wb3J0IHsgYXNzZXJ0SXNTZXJ2ZXIsIHdyYXBTaWduZWRGZXRjaCB9IGZyb20gJy4uL3V0aWxzJ1xuaW1wb3J0IHtcbiAgY3JlYXRlU3RvcmFnZUNvbmZpZyxcbiAgR2V0T3B0aW9ucyxcbiAgR2V0VmFsdWVzT3B0aW9ucyxcbiAgR2V0VmFsdWVzUmVzdWx0LFxuICBNT0RVTEVfTkFNRSxcbiAgU2V0T3B0aW9ucyxcbiAgU3RvcmFnZUNvbmZpZ1N0YXRlXG59IGZyb20gJy4vY29uc3RhbnRzJ1xuaW1wb3J0IHsgY3JlYXRlVmFsdWVDYWNoZSB9IGZyb20gJy4vdmFsdWUtY2FjaGUnXG5pbXBvcnQgeyBlcnJvck1lc3NhZ2UgfSBmcm9tICcuL2Vycm9yLW1lc3NhZ2UnXG5pbXBvcnQgeyBhc3NlcnRQbGF5ZXJBZGRyZXNzLCBhc3NlcnRTdG9yYWdlS2V5LCBzZXJpYWxpemVTdG9yYWdlVmFsdWUgfSBmcm9tICcuL3NlcmlhbGl6ZSdcbmltcG9ydCB7IGNyZWF0ZVdyaXRlUXVldWUsIFN1cGVyc2VkaW5nV3JpdGVGYWlsZWQgfSBmcm9tICcuL3dyaXRlLXF1ZXVlJ1xuXG4vKipcbiAqIFBsYXllci1zY29wZWQgc3RvcmFnZSBpbnRlcmZhY2UgZm9yIGtleS12YWx1ZSBwYWlycyBmcm9tIHRoZSBTZXJ2ZXIgU2lkZSBTdG9yYWdlIHNlcnZpY2UuXG4gKiBUaGlzIGlzIE5PVCBmaWxlc3lzdGVtIHN0b3JhZ2UgLSBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgcmVtb3RlIHN0b3JhZ2Ugc2VydmljZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJUGxheWVyU3RvcmFnZSB7XG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgYSB2YWx1ZSBmcm9tIGEgcGxheWVyJ3Mgc3RvcmFnZSBieSBrZXkgZnJvbSB0aGUgU2VydmVyIFNpZGUgU3RvcmFnZSBzZXJ2aWNlLlxuICAgKlxuICAgKiBCeSBkZWZhdWx0IChjYWNoZVJlYWRzKSwgdmFsdWVzIHJlYWQgb3Igd3JpdHRlbiBkdXJpbmcgdGhlIGxhc3QgY2FjaGVNYXhBZ2VNc1xuICAgKiBhcmUgc2VydmVkIGZyb20gYSBsb2NhbCBjYWNoZSB3aXRob3V0IGEgbmV0d29yayByZXF1ZXN0LCBpbmNsdWRpbmcgY29uZmlybWVkXG4gICAqIFwibm90IGZvdW5kXCIgcmVzdWx0cy4gQ29uY3VycmVudCBnZXRzIGZvciB0aGUgc2FtZSBwbGF5ZXIgYW5kIGtleSBzaGFyZSBvbmVcbiAgICogcmVxdWVzdC4gT3V0LW9mLWJhbmQgd3JpdGVycyAoZS5nLiBDTEkgc3RvcmFnZSBjb21tYW5kcykgbWF5IG5vdCBiZSB2aXNpYmxlXG4gICAqIGZvciB1cCB0byBjYWNoZU1heEFnZU1zIOKAlCBwYXNzIHsgZnJlc2g6IHRydWUgfSB0byBmb3JjZSBhIG5ldHdvcmsgcmVhZC5cbiAgICogQSByZWFkIGZpcnN0IHdhaXRzIGZvciB3cml0ZXMgdG8gdGhlIHNhbWUga2V5IHRoYXQgd2VyZSBwZW5kaW5nIHdoZW4gaXQgd2FzXG4gICAqIGlzc3VlZCwgc28gaXQgcmVmbGVjdHMgZXZlcnkgd3JpdGUgaXNzdWVkIGJlZm9yZSBpdC5cbiAgICogQHBhcmFtIGFkZHJlc3MgLSBUaGUgcGxheWVyJ3Mgd2FsbGV0IGFkZHJlc3NcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gcmV0cmlldmVcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25hbCB7IGZyZXNoIH0gdG8gYnlwYXNzIHRoZSByZWFkIGNhY2hlXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBwYXJzZWQgSlNPTiB2YWx1ZSwgb3IgbnVsbCBpZiBub3QgZm91bmRcbiAgICogQHRocm93cyBFcnJvciBpZiB0aGUgcmVhZCBmYWlscywgc28gYSBmYWlsdXJlIGlzIG5ldmVyIG1pc3Rha2VuIGZvciBhIG1pc3Npbmcga2V5XG4gICAqIEB0aHJvd3MgVHlwZUVycm9yIGlmIHRoZSBhZGRyZXNzIG9yIGtleSBpcyBpbnZhbGlkXG4gICAqL1xuICBnZXQ8VCA9IHVua25vd24+KGFkZHJlc3M6IHN0cmluZywga2V5OiBzdHJpbmcsIG9wdGlvbnM/OiBHZXRPcHRpb25zKTogUHJvbWlzZTxUIHwgbnVsbD5cblxuICAvKipcbiAgICogU3RvcmVzIGEgdmFsdWUgaW4gYSBwbGF5ZXIncyBzdG9yYWdlIGluIHRoZSBTZXJ2ZXIgU2lkZSBTdG9yYWdlIHNlcnZpY2UuXG4gICAqIEBwYXJhbSBhZGRyZXNzIC0gVGhlIHBsYXllcidzIHdhbGxldCBhZGRyZXNzXG4gICAqIEBwYXJhbSBrZXkgLSBUaGUga2V5IHRvIHN0b3JlIHRoZSB2YWx1ZSB1bmRlclxuICAgKiBAcGFyYW0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gc3RvcmUgKHdpbGwgYmUgSlNPTiBzZXJpYWxpemVkKVxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIHsgc2tpcElmVW5jaGFuZ2VkIH0gdG8gc2tpcCB0aGUgbmV0d29yayB3cml0ZSB3aGVuIHRoZSB2YWx1ZSBpcyBhbHJlYWR5IHN0b3JlZFxuICAgKiBAcmV0dXJucyB0cnVlIG9uY2Ugc3RvcmVkLCBvciBvbmNlIGEgbmV3ZXIgd3JpdGUgdGhhdCByZXBsYWNlZCB0aGlzIG9uZSBsYW5kc1xuICAgKiAocmFwaWQgd3JpdGVzIGNvYWxlc2NlKTsgZmFsc2UgaWYgdGhlIHdyaXRlLCBvciBpdHMgcmVwbGFjZW1lbnQsIGZhaWxzXG4gICAqIEB0aHJvd3MgVHlwZUVycm9yIGlmIHRoZSBhZGRyZXNzIG9yIGtleSBpcyBpbnZhbGlkLCBvciB0aGUgdmFsdWUgY2Fubm90IGJlIHN0b3JlZCB1bmNoYW5nZWQ6IHVuZGVmaW5lZCxcbiAgICogY2lyY3VsYXIsIGEgQmlnSW50LCBvciBjb250YWluaW5nIGEgZnVuY3Rpb24sIGEgc3ltYm9sLCBhIG5vbi1maW5pdGUgbnVtYmVyLCBhbiB1bmRlZmluZWQgYXJyYXlcbiAgICogZWxlbWVudCBvciBob2xlLCBhIE1hcCwgU2V0LCB0eXBlZCBhcnJheSBvciBvdGhlciBidWlsdC1pbiB3aXRoIG5vIEpTT04gZm9ybSwgb3IgdGV4dCB3aXRoIGFuXG4gICAqIHVucGFpcmVkIHN1cnJvZ2F0ZSBvciBhIE5VTCBjaGFyYWN0ZXIuIEFuIHVuZGVmaW5lZCBvYmplY3QgcHJvcGVydHkgaXMgZHJvcHBlZDsgYSBEYXRlIGlzXG4gICAqIHN0b3JlZCBhcyBpdHMgSVNPIHN0cmluZyBhbmQgYSBjbGFzcyBpbnN0YW5jZSBhcyBpdHMgb3duIGVudW1lcmFibGUgZGF0YS5cbiAgICovXG4gIHNldDxUID0gdW5rbm93bj4oYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZywgdmFsdWU6IFQsIG9wdGlvbnM/OiBTZXRPcHRpb25zKTogUHJvbWlzZTxib29sZWFuPlxuXG4gIC8qKlxuICAgKiBEZWxldGVzIGEgdmFsdWUgZnJvbSBhIHBsYXllcidzIHN0b3JhZ2UgaW4gdGhlIFNlcnZlciBTaWRlIFN0b3JhZ2Ugc2VydmljZS5cbiAgICogQHBhcmFtIGFkZHJlc3MgLSBUaGUgcGxheWVyJ3Mgd2FsbGV0IGFkZHJlc3NcbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gZGVsZXRlXG4gICAqIEByZXR1cm5zIHRydWUgb25jZSBhcHBsaWVkLCBvciBvbmNlIGEgbmV3ZXIgd3JpdGUgdGhhdCByZXBsYWNlZCBpdCBsYW5kcztcbiAgICogZmFsc2Ugb25seSBmb3IgYSBjb25maXJtZWQgNDA0XG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIGRlbGV0ZSwgb3IgaXRzIHJlcGxhY2VtZW50LCBmYWlsczsgYSBmYWlsdXJlIGlzIG5ldmVyIHJlcG9ydGVkIGFzIGFic2VuY2VcbiAgICogQHRocm93cyBUeXBlRXJyb3IgaWYgdGhlIGFkZHJlc3Mgb3Iga2V5IGlzIGludmFsaWRcbiAgICovXG4gIGRlbGV0ZShhZGRyZXNzOiBzdHJpbmcsIGtleTogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPlxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGtleS12YWx1ZSBlbnRyaWVzIGZyb20gYSBwbGF5ZXIncyBzdG9yYWdlLCBvcHRpb25hbGx5IGZpbHRlcmVkIGJ5IHByZWZpeC5cbiAgICogU3VwcG9ydHMgcGFnaW5hdGlvbiB2aWEgbGltaXQgYW5kIG9mZnNldC5cbiAgICogQHBhcmFtIGFkZHJlc3MgLSBUaGUgcGxheWVyJ3Mgd2FsbGV0IGFkZHJlc3NcbiAgICogQHBhcmFtIG9wdGlvbnMgLSBPcHRpb25hbCB7IHByZWZpeCwgbGltaXQsIG9mZnNldCB9IGZvciBmaWx0ZXJpbmcgYW5kIHBhZ2luYXRpb24uXG4gICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHsgZGF0YSwgcGFnaW5hdGlvbjogeyBvZmZzZXQsIHRvdGFsIH0gfSBmb3IgcGFnaW5hdGlvbiBVSVxuICAgKiBAdGhyb3dzIEVycm9yIGlmIHRoZSByZWFkIGZhaWxzLCBzbyBhIGZhaWx1cmUgaXMgbmV2ZXIgbWlzdGFrZW4gZm9yIGFuIGVtcHR5IHBhZ2VcbiAgICogQHRocm93cyBUeXBlRXJyb3IgaWYgdGhlIGFkZHJlc3MgaXMgaW52YWxpZFxuICAgKi9cbiAgZ2V0VmFsdWVzKGFkZHJlc3M6IHN0cmluZywgb3B0aW9ucz86IEdldFZhbHVlc09wdGlvbnMpOiBQcm9taXNlPEdldFZhbHVlc1Jlc3VsdD5cbn1cblxuLyoqXG4gKiBDcmVhdGVzIHBsYXllci1zY29wZWQgc3RvcmFnZSB0aGF0IHByb3ZpZGVzIG1ldGhvZHMgdG8gaW50ZXJhY3Qgd2l0aFxuICogcGxheWVyLXNwZWNpZmljIGtleS12YWx1ZSBwYWlycyBmcm9tIHRoZSBTZXJ2ZXIgU2lkZSBTdG9yYWdlIHNlcnZpY2UuXG4gKiBUaGlzIG1vZHVsZSBvbmx5IHdvcmtzIHdoZW4gcnVubmluZyBvbiBzZXJ2ZXItc2lkZSBzY2VuZXMuXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGNvbnN0IGNyZWF0ZVBsYXllclN0b3JhZ2UgPSAoY29uZmlnOiBTdG9yYWdlQ29uZmlnU3RhdGUgPSBjcmVhdGVTdG9yYWdlQ29uZmlnKCkpOiBJUGxheWVyU3RvcmFnZSA9PiB7XG4gIGNvbnN0IGNhY2hlID0gY3JlYXRlVmFsdWVDYWNoZShjb25maWcpXG4gIC8vIEVhY2ggaW4tZmxpZ2h0IEdFVCBpcyB0cmFja2VkIGJ5IGEgd3JhcHBlciBvYmplY3Qgd2hvc2UgaWRlbnRpdHkgbWFya3NcbiAgLy8gb3duZXJzaGlwOiBhIGxhbmRlZCB3cml0ZSBkZXRhY2hlcyB0aGUgcGVuZGluZyBHRVQgc28gaXRzIHN0YWxlIHJlc3BvbnNlIGlzIG5vdCBjYWNoZWQuXG4gIGNvbnN0IGluZmxpZ2h0R2V0cyA9IG5ldyBNYXA8c3RyaW5nLCB7IHByb21pc2U6IFByb21pc2U8dW5rbm93bj4gfT4oKVxuXG4gIC8vIExvd2VyY2FzZWQgc28gY2hlY2tzdW1tZWQgY2FsbGVycyBzaGFyZSBhbiBlbnRyeS5cbiAgY29uc3QgY2FjaGVLZXkgPSAoYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZykgPT4gSlNPTi5zdHJpbmdpZnkoW2FkZHJlc3MudG9Mb3dlckNhc2UoKSwga2V5XSlcblxuICAvLyBXcml0ZXMgdG8gdGhlIHNhbWUgcGxheWVyIGtleSBhcmUgc2VyaWFsaXplZCAoYW5kIHJhcGlkIG9uZXMgY29hbGVzY2VkIHRvXG4gIC8vIHRoZSBsYXRlc3QgdmFsdWUpIHNvIHRoZSBzZXJ2aWNlIGNvbW1pdHMgdGhlbSBpbiBpc3N1ZSBvcmRlciDigJQgb3ZlcmxhcHBpbmdcbiAgLy8gUFVUcyB3b3VsZCBvdGhlcndpc2UgbGVhdmUgYm90aCB0aGUga2VwdCB2YWx1ZSBhbmQgdGhlIGNhY2hlZCB2YWx1ZSB0b1xuICAvLyByZXNwb25zZS1vcmRlciBjaGFuY2UuIEtleWVkIGJ5IHRoZSBzYW1lIGNhc2UtaW5zZW5zaXRpdmUgY2FjaGUga2V5LlxuICBjb25zdCB3cml0ZXMgPSBjcmVhdGVXcml0ZVF1ZXVlKClcblxuICBhc3luYyBmdW5jdGlvbiBleGVjdXRlU2V0KGFkZHJlc3M6IHN0cmluZywga2V5OiBzdHJpbmcsIGNrOiBzdHJpbmcsIGJvZHk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGxldCBiYXNlVXJsOiBzdHJpbmdcbiAgICB0cnkge1xuICAgICAgYmFzZVVybCA9IGF3YWl0IGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjYWNoZS5kZWxldGUoY2spXG4gICAgICBjb25zb2xlLmVycm9yKGBGYWlsZWQgdG8gc2V0IHBsYXllciBzdG9yYWdlIHZhbHVlICcke2tleX0nIGZvciAnJHthZGRyZXNzfSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIGNvbnN0IHVybCA9IGAke2Jhc2VVcmx9L3BsYXllcnMvJHtlbmNvZGVVUklDb21wb25lbnQoYWRkcmVzcyl9L3ZhbHVlcy8ke2VuY29kZVVSSUNvbXBvbmVudChrZXkpfWBcblxuICAgIGNvbnN0IFtlcnJvcl0gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2goe1xuICAgICAgdXJsLFxuICAgICAgaW5pdDoge1xuICAgICAgICBtZXRob2Q6ICdQVVQnLFxuICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgJ2NvbnRlbnQtdHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgICB9LFxuICAgICAgICBib2R5XG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIEVpdGhlciB3YXkgdGhlIGVudHJ5IGNoYW5nZWQgc2VydmVyLXNpZGUgKG9yIG1heSBoYXZlKTogZGV0YWNoIGFueVxuICAgIC8vIG92ZXJsYXBwaW5nIGluLWZsaWdodCBHRVQgc28gaXRzIHN0YWxlIHJlc3BvbnNlIGlzIG5vdCBjYWNoZWQuXG4gICAgaW5mbGlnaHRHZXRzLmRlbGV0ZShjaylcblxuICAgIGlmIChlcnJvcikge1xuICAgICAgLy8gVGhlIFBVVCBtYXkgaGF2ZSByZWFjaGVkIHRoZSBzZXJ2ZXIsIHNvIHRoZSBjYWNoZWQgYm9keSBpcyBub1xuICAgICAgLy8gbG9uZ2VyIHJlbGlhYmxlLlxuICAgICAgY2FjaGUuZGVsZXRlKGNrKVxuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgY2FjaGUuc2V0KGNrLCB7IGJvZHkgfSlcbiAgICByZXR1cm4gdHJ1ZVxuICB9XG5cbiAgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZURlbGV0ZShhZGRyZXNzOiBzdHJpbmcsIGtleTogc3RyaW5nLCBjazogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgbGV0IGJhc2VVcmw6IHN0cmluZ1xuICAgIHRyeSB7XG4gICAgICBiYXNlVXJsID0gYXdhaXQgZ2V0U3RvcmFnZVNlcnZlclVybCgpXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNhY2hlLmRlbGV0ZShjaylcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBwbGF5ZXIgc3RvcmFnZSB2YWx1ZSAnJHtrZXl9JyBmb3IgJyR7YWRkcmVzc30nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICB9XG4gICAgY29uc3QgdXJsID0gYCR7YmFzZVVybH0vcGxheWVycy8ke2VuY29kZVVSSUNvbXBvbmVudChhZGRyZXNzKX0vdmFsdWVzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KGtleSl9YFxuXG4gICAgY29uc3QgW2Vycm9yLCAsIHN0YXR1c10gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2goe1xuICAgICAgdXJsLFxuICAgICAgaW5pdDoge1xuICAgICAgICBtZXRob2Q6ICdERUxFVEUnLFxuICAgICAgICBoZWFkZXJzOiB7fVxuICAgICAgfVxuICAgIH0pXG5cbiAgICAvLyBEZXRhY2ggYWdhaW46IGEgR0VUIG1heSBoYXZlIHN0YXJ0ZWQgd2hpbGUgdGhlIERFTEVURSB3YXMgaW4gZmxpZ2h0LlxuICAgIGluZmxpZ2h0R2V0cy5kZWxldGUoY2spXG5cbiAgICBpZiAoZXJyb3IpIHtcbiAgICAgIC8vIEEgNDA0IHN0aWxsIGNvbmZpcm1zIHRoZSBrZXkgaXMgYWJzZW50IHNlcnZlci1zaWRlLlxuICAgICAgaWYgKHN0YXR1cyA9PT0gNDA0KSB7XG4gICAgICAgIGNhY2hlLnNldEFic2VudChjaylcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgICAvLyBUaGUgREVMRVRFIG1heSBoYXZlIHJlYWNoZWQgdGhlIHNlcnZlciwgc28gdGhlIGNhY2hlZCBib2R5IGlzIHVucmVsaWFibGUuXG4gICAgICBjYWNoZS5kZWxldGUoY2spXG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBkZWxldGUgcGxheWVyIHN0b3JhZ2UgdmFsdWUgJyR7a2V5fScgZm9yICcke2FkZHJlc3N9JzogJHtlcnJvck1lc3NhZ2UoZXJyb3IpfWApXG4gICAgfVxuXG4gICAgY2FjaGUuc2V0QWJzZW50KGNrKVxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGFzeW5jIGdldDxUID0gdW5rbm93bj4oYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZywgb3B0aW9ucz86IEdldE9wdGlvbnMpOiBQcm9taXNlPFQgfCBudWxsPiB7XG4gICAgICBhc3NlcnRJc1NlcnZlcihNT0RVTEVfTkFNRSlcbiAgICAgIGFzc2VydFBsYXllckFkZHJlc3MoYWRkcmVzcywgJ1N0b3JhZ2UucGxheWVyLmdldCgpJylcbiAgICAgIGFzc2VydFN0b3JhZ2VLZXkoa2V5LCAnU3RvcmFnZS5wbGF5ZXIuZ2V0KCknKVxuXG4gICAgICBjb25zdCBjayA9IGNhY2hlS2V5KGFkZHJlc3MsIGtleSlcblxuICAgICAgaWYgKHdyaXRlcy5pc1BlbmRpbmcoY2spKSBhd2FpdCB3cml0ZXMuc2V0dGxlZChjaylcblxuICAgICAgaWYgKGNvbmZpZy5jYWNoZVJlYWRzICYmICFvcHRpb25zPy5mcmVzaCkge1xuICAgICAgICBjb25zdCBlbnRyeSA9IGNhY2hlLmdldChjaylcbiAgICAgICAgaWYgKGVudHJ5Py5hYnNlbnQpIHJldHVybiBudWxsXG4gICAgICAgIC8vIFBhcnNlIHBlciBoaXQgc28gZWFjaCBjYWxsZXIgZ2V0cyBhIGZyZXNoIG9iamVjdCAobm8gc2hhcmVkIG11dGF0aW9uKS5cbiAgICAgICAgaWYgKGVudHJ5Py5ib2R5ICE9PSB1bmRlZmluZWQpIHJldHVybiBKU09OLnBhcnNlKGVudHJ5LmJvZHkpLnZhbHVlIGFzIFRcbiAgICAgIH1cblxuICAgICAgLy8gQ29hbGVzY2UgY29uY3VycmVudCBnZXRzIChldmVuIGZyZXNoIG9uZXM6IGFuIGluLWZsaWdodCByZXNwb25zZSBpc1xuICAgICAgLy8gbWlsbGlzZWNvbmRzIG9sZCwgbm90IFRUTC1zdGFsZSkgaW50byBhIHNpbmdsZSBuZXR3b3JrIHJlcXVlc3QuXG4gICAgICBjb25zdCBqb2luZWQgPSBpbmZsaWdodEdldHMuZ2V0KGNrKVxuICAgICAgaWYgKGpvaW5lZCkgcmV0dXJuIGpvaW5lZC5wcm9taXNlIGFzIFByb21pc2U8VCB8IG51bGw+XG5cbiAgICAgIGNvbnN0IGluZmxpZ2h0ID0ge30gYXMgeyBwcm9taXNlOiBQcm9taXNlPFQgfCBudWxsPiB9XG4gICAgICBpbmZsaWdodC5wcm9taXNlID0gKGFzeW5jICgpID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsZXQgYmFzZVVybDogc3RyaW5nXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGJhc2VVcmwgPSBhd2FpdCBnZXRTdG9yYWdlU2VydmVyVXJsKClcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHBsYXllciBzdG9yYWdlIHZhbHVlICcke2tleX0nIGZvciAnJHthZGRyZXNzfSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB1cmwgPSBgJHtiYXNlVXJsfS9wbGF5ZXJzLyR7ZW5jb2RlVVJJQ29tcG9uZW50KGFkZHJlc3MpfS92YWx1ZXMvJHtlbmNvZGVVUklDb21wb25lbnQoa2V5KX1gXG5cbiAgICAgICAgICBjb25zdCBbZXJyb3IsIGRhdGEsIHN0YXR1c10gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2g8eyB2YWx1ZTogVCB9Pih7IHVybCB9KVxuXG4gICAgICAgICAgY29uc3QgaXNPd25lciA9IGluZmxpZ2h0R2V0cy5nZXQoY2spID09PSBpbmZsaWdodFxuXG4gICAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICAvLyBBIGNvbmZpcm1lZCA0MDQgaXMgYSBmaXJzdC1jbGFzcyBcImFic2VudFwiIG91dGNvbWUsIG5vdCBhIGZhaWx1cmUuXG4gICAgICAgICAgICBpZiAoc3RhdHVzID09PSA0MDQpIHtcbiAgICAgICAgICAgICAgaWYgKGlzT3duZXIpIGNhY2hlLnNldEFic2VudChjaylcbiAgICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBwbGF5ZXIgc3RvcmFnZSB2YWx1ZSAnJHtrZXl9JyBmb3IgJyR7YWRkcmVzc30nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyB3cmFwU2lnbmVkRmV0Y2ggcGFyc2VzIGFuIGVtcHR5IDJ4eCBib2R5IGFzIHt9OiBhIG1pc3NpbmcgdmFsdWUgaXMgYSBmYXVsdCwgbm90IGFuIGFic2VuY2UuXG4gICAgICAgICAgaWYgKCFkYXRhIHx8IGRhdGEudmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHBsYXllciBzdG9yYWdlIHZhbHVlICcke2tleX0nIGZvciAnJHthZGRyZXNzfSc6IHJlc3BvbnNlIGNhcnJpZWQgbm8gdmFsdWVgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFNhbWUgc2VyaWFsaXphdGlvbiBzaGFwZSBhcyBzZXQoKSdzIFBVVCBib2R5LCBzbyBhIHJlYWQgZm9sbG93ZWQgYnlcbiAgICAgICAgICAvLyBhbiB1bmNoYW5nZWQgd3JpdGUgY2FuIGJlIHNraXBwZWQuXG4gICAgICAgICAgY29uc3QgYm9keSA9IEpTT04uc3RyaW5naWZ5KHsgdmFsdWU6IGRhdGEudmFsdWUgfSlcbiAgICAgICAgICBpZiAoaXNPd25lcikgY2FjaGUuc2V0KGNrLCB7IGJvZHkgfSlcbiAgICAgICAgICByZXR1cm4gZGF0YS52YWx1ZVxuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIGlmIChpbmZsaWdodEdldHMuZ2V0KGNrKSA9PT0gaW5mbGlnaHQpIGluZmxpZ2h0R2V0cy5kZWxldGUoY2spXG4gICAgICAgIH1cbiAgICAgIH0pKClcblxuICAgICAgaW5mbGlnaHRHZXRzLnNldChjaywgaW5mbGlnaHQpXG4gICAgICByZXR1cm4gaW5mbGlnaHQucHJvbWlzZVxuICAgIH0sXG5cbiAgICBhc3luYyBzZXQ8VCA9IHVua25vd24+KGFkZHJlc3M6IHN0cmluZywga2V5OiBzdHJpbmcsIHZhbHVlOiBULCBvcHRpb25zPzogU2V0T3B0aW9ucyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgYXNzZXJ0SXNTZXJ2ZXIoTU9EVUxFX05BTUUpXG4gICAgICBhc3NlcnRQbGF5ZXJBZGRyZXNzKGFkZHJlc3MsICdTdG9yYWdlLnBsYXllci5zZXQoKScpXG4gICAgICBhc3NlcnRTdG9yYWdlS2V5KGtleSwgJ1N0b3JhZ2UucGxheWVyLnNldCgpJylcblxuICAgICAgY29uc3QgY2sgPSBjYWNoZUtleShhZGRyZXNzLCBrZXkpXG4gICAgICBjb25zdCBib2R5ID0gc2VyaWFsaXplU3RvcmFnZVZhbHVlKHZhbHVlLCBgU3RvcmFnZS5wbGF5ZXIuc2V0KCcke2FkZHJlc3N9JywgJyR7a2V5fScpYClcbiAgICAgIGNvbnN0IHNraXBJZlVuY2hhbmdlZCA9IG9wdGlvbnM/LnNraXBJZlVuY2hhbmdlZCA/PyBjb25maWcuc2tpcElmVW5jaGFuZ2VkXG5cbiAgICAgIC8vIERlZHVwIGFnYWluc3QgY29uZmlybWVkIHN0YXRlIG9ubHkgd2hpbGUgbm8gd3JpdGUgaXMgcGVuZGluZyDigJQgYVxuICAgICAgLy8gcGVuZGluZyB3cml0ZSBtYWtlcyB0aGUgY2FjaGUgbW9tZW50YXJpbHkgc3RhbGU7IGVucXVldWUoKSBjb2FsZXNjZXNcbiAgICAgIC8vIGFnYWluc3QgcGVuZGluZyB3cml0ZXMgaW5zdGVhZC5cbiAgICAgIGlmIChza2lwSWZVbmNoYW5nZWQgJiYgd3JpdGVzLnBlbmRpbmcoY2spID09PSB1bmRlZmluZWQgJiYgY2FjaGUuZ2V0KGNrKT8uYm9keSA9PT0gYm9keSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gd3JpdGVzLmVucXVldWUoY2ssIGJvZHksIChiKSA9PiBleGVjdXRlU2V0KGFkZHJlc3MsIGtleSwgY2ssIGIgYXMgc3RyaW5nKSwgc2tpcElmVW5jaGFuZ2VkKVxuICAgIH0sXG5cbiAgICBhc3luYyBkZWxldGUoYWRkcmVzczogc3RyaW5nLCBrZXk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgYXNzZXJ0SXNTZXJ2ZXIoTU9EVUxFX05BTUUpXG4gICAgICBhc3NlcnRQbGF5ZXJBZGRyZXNzKGFkZHJlc3MsICdTdG9yYWdlLnBsYXllci5kZWxldGUoKScpXG4gICAgICBhc3NlcnRTdG9yYWdlS2V5KGtleSwgJ1N0b3JhZ2UucGxheWVyLmRlbGV0ZSgpJylcblxuICAgICAgY29uc3QgY2sgPSBjYWNoZUtleShhZGRyZXNzLCBrZXkpXG5cbiAgICAgIHJldHVybiB3cml0ZXNcbiAgICAgICAgLmVucXVldWUoY2ssIG51bGwsICgpID0+IGV4ZWN1dGVEZWxldGUoYWRkcmVzcywga2V5LCBjayksIHRydWUpXG4gICAgICAgIC5jYXRjaCgoZXJyb3I6IHVua25vd24pID0+IHtcbiAgICAgICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBTdXBlcnNlZGluZ1dyaXRlRmFpbGVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBkZWxldGUgcGxheWVyIHN0b3JhZ2UgdmFsdWUgJyR7a2V5fScgZm9yICcke2FkZHJlc3N9JzogJHtlcnJvci5tZXNzYWdlfWApXG4gICAgICAgICAgfVxuICAgICAgICAgIHRocm93IGVycm9yXG4gICAgICAgIH0pXG4gICAgfSxcblxuICAgIGFzeW5jIGdldFZhbHVlcyhhZGRyZXNzOiBzdHJpbmcsIG9wdGlvbnM/OiBHZXRWYWx1ZXNPcHRpb25zKTogUHJvbWlzZTxHZXRWYWx1ZXNSZXN1bHQ+IHtcbiAgICAgIGFzc2VydElzU2VydmVyKE1PRFVMRV9OQU1FKVxuICAgICAgYXNzZXJ0UGxheWVyQWRkcmVzcyhhZGRyZXNzLCAnU3RvcmFnZS5wbGF5ZXIuZ2V0VmFsdWVzKCknKVxuXG4gICAgICBjb25zdCB7IHByZWZpeCwgbGltaXQsIG9mZnNldCB9ID0gb3B0aW9ucyA/PyB7fVxuICAgICAgbGV0IGJhc2VVcmw6IHN0cmluZ1xuICAgICAgdHJ5IHtcbiAgICAgICAgYmFzZVVybCA9IGF3YWl0IGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHBsYXllciBzdG9yYWdlIHZhbHVlcyBmb3IgJyR7YWRkcmVzc30nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdXG5cbiAgICAgIGlmICghIXByZWZpeCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBwcmVmaXg9JHtlbmNvZGVVUklDb21wb25lbnQocHJlZml4KX1gKVxuICAgICAgfVxuXG4gICAgICBpZiAoISFsaW1pdCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBsaW1pdD0ke2xpbWl0fWApXG4gICAgICB9XG5cbiAgICAgIGlmICghIW9mZnNldCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBvZmZzZXQ9JHtvZmZzZXR9YClcbiAgICAgIH1cblxuICAgICAgY29uc3QgcXVlcnkgPSBwYXJ0cy5qb2luKCcmJylcbiAgICAgIGNvbnN0IHVybCA9IHF1ZXJ5XG4gICAgICAgID8gYCR7YmFzZVVybH0vcGxheWVycy8ke2VuY29kZVVSSUNvbXBvbmVudChhZGRyZXNzKX0vdmFsdWVzPyR7cXVlcnl9YFxuICAgICAgICA6IGAke2Jhc2VVcmx9L3BsYXllcnMvJHtlbmNvZGVVUklDb21wb25lbnQoYWRkcmVzcyl9L3ZhbHVlc2BcblxuICAgICAgY29uc3Qgd2F0Y2hlciA9IGNhY2hlLndhdGNoKClcbiAgICAgIGNvbnN0IFtlcnJvciwgcmVzcG9uc2VdID0gYXdhaXQgd3JhcFNpZ25lZEZldGNoPEdldFZhbHVlc1Jlc3VsdD4oeyB1cmwgfSlcbiAgICAgIHdhdGNoZXIuc3RvcCgpXG5cbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgcGxheWVyIHN0b3JhZ2UgdmFsdWVzIGZvciAnJHthZGRyZXNzfSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBkYXRhID0gcmVzcG9uc2U/LmRhdGFcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgcGxheWVyIHN0b3JhZ2UgdmFsdWVzIGZvciAnJHthZGRyZXNzfSc6IHJlc3BvbnNlIGNhcnJpZWQgbm8gZGF0YSBhcnJheWApXG4gICAgICB9XG4gICAgICBpZiAoZGF0YS5zb21lKChlbnRyeSkgPT4gdHlwZW9mIGVudHJ5Py5rZXkgIT09ICdzdHJpbmcnIHx8IGVudHJ5LnZhbHVlID09PSB1bmRlZmluZWQpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGdldCBwbGF5ZXIgc3RvcmFnZSB2YWx1ZXMgZm9yICcke2FkZHJlc3N9JzogcmVzcG9uc2UgY2FycmllZCBhIG1hbGZvcm1lZCBlbnRyeWApXG4gICAgICB9XG5cbiAgICAgIC8vIFNlZWQgdGhlIHBlci1rZXkgY2FjaGUgc28gc3Vic2VxdWVudCBnZXQoKS9zZXQoKSBvbiByZXR1cm5lZCBrZXlzIGNhblxuICAgICAgLy8gc2tpcCB0aGUgbmV0d29yay4gT25seSBrZXlzIHdpdGggbm8gbGl2ZSBlbnRyeSBhbmQgbm8gcGVuZGluZyB3cml0ZVxuICAgICAgLy8gYXJlIHNlZWRlZDogZXhpc3RpbmcgcGVyLWtleSBzdGF0ZSBjb21lcyBmcm9tIGEgY29uZmlybWVkIG9wZXJhdGlvblxuICAgICAgLy8gdGhhdCB0aGlzIHBhZ2Ugc25hcHNob3Qg4oCUIHdob3NlIHJlcXVlc3Qgc3RhcnRlZCBlYXJsaWVyIOKAlCBtdXN0IG5vdFxuICAgICAgLy8gY2xvYmJlciB3aXRoIHN0YWxlIGRhdGEuIEFic2VuY2UgaXMgbmV2ZXIgc2VlZGVkIChwcmVmaXgvcGFnaW5hdGlvblxuICAgICAgLy8gbWFrZSBpdCBub24tYXV0aG9yaXRhdGl2ZSkuIEEgcGFnZSBsYXJnZXIgdGhhbiBjYWNoZU1heEVudHJpZXMgY2h1cm5zXG4gICAgICAvLyB0aGUgY2FjaGU7IGVudHJpZXMgcmVwb3B1bGF0ZSBsYXppbHkuXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGRhdGEpIHtcbiAgICAgICAgY29uc3QgY2sgPSBjYWNoZUtleShhZGRyZXNzLCBlbnRyeS5rZXkpXG4gICAgICAgIGlmICh3YXRjaGVyLm11dGF0ZWQoY2spIHx8IHdyaXRlcy5pc1BlbmRpbmcoY2spIHx8IGluZmxpZ2h0R2V0cy5oYXMoY2spKSBjb250aW51ZVxuICAgICAgICBpZiAoY2FjaGUuZ2V0KGNrKSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY2FjaGUuc2V0KGNrLCB7IGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgdmFsdWU6IGVudHJ5LnZhbHVlIH0pIH0pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVxdWVzdGVkT2Zmc2V0ID0gb2Zmc2V0ID8/IDBcbiAgICAgIGNvbnN0IHBhZ2luYXRpb24gPSB7XG4gICAgICAgIG9mZnNldDogcmVzcG9uc2UhLnBhZ2luYXRpb24/Lm9mZnNldCA/PyByZXF1ZXN0ZWRPZmZzZXQsXG4gICAgICAgIHRvdGFsOiByZXNwb25zZSEucGFnaW5hdGlvbj8udG90YWwgPz8gZGF0YS5sZW5ndGhcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHsgZGF0YSwgcGFnaW5hdGlvbiB9XG4gICAgfVxuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { GetOptions, GetValuesOptions, GetValuesResult, SetOptions } from './constants';
|
|
2
|
+
/**
|
|
3
|
+
* Scene-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 ISceneStorage {
|
|
7
|
+
/**
|
|
8
|
+
* Retrieves a value from scene 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 key share one request.
|
|
13
|
+
* Out-of-band writers (e.g. CLI storage commands) may not be visible for up to
|
|
14
|
+
* 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 key - The key to retrieve
|
|
18
|
+
* @param options - Optional { fresh } to bypass the read cache
|
|
19
|
+
* @returns A promise that resolves to the parsed JSON value, or null if not found
|
|
20
|
+
* @throws Error if the read fails, so a failure is never mistaken for a missing key
|
|
21
|
+
* @throws TypeError if the key is invalid
|
|
22
|
+
*/
|
|
23
|
+
get<T = unknown>(key: string, options?: GetOptions): Promise<T | null>;
|
|
24
|
+
/**
|
|
25
|
+
* Stores a value in scene storage in the Server Side Storage service.
|
|
26
|
+
* @param key - The key to store the value under
|
|
27
|
+
* @param value - The value to store (will be JSON serialized)
|
|
28
|
+
* @param options - Optional { skipIfUnchanged } to skip the network write when the value is already stored
|
|
29
|
+
* @returns true once stored, or once a newer write that replaced this one lands
|
|
30
|
+
* (rapid writes coalesce); false if the write, or its replacement, fails
|
|
31
|
+
* @throws TypeError if the key is invalid, or the value cannot be stored unchanged: undefined,
|
|
32
|
+
* circular, a BigInt, or containing a function, a symbol, a non-finite number, an undefined array
|
|
33
|
+
* element or hole, a Map, Set, typed array or other built-in with no JSON form, or text with an
|
|
34
|
+
* unpaired surrogate or a NUL character. An undefined object property is dropped; a Date is
|
|
35
|
+
* stored as its ISO string and a class instance as its own enumerable data.
|
|
36
|
+
*/
|
|
37
|
+
set<T = unknown>(key: string, value: T, options?: SetOptions): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Deletes a value from scene storage in the Server Side Storage service.
|
|
40
|
+
* @param key - The key to delete
|
|
41
|
+
* @returns true once applied, or once a newer write that replaced it lands;
|
|
42
|
+
* false only for a confirmed 404
|
|
43
|
+
* @throws Error if the delete, or its replacement, fails; a failure is never reported as absence
|
|
44
|
+
* @throws TypeError if the key is invalid
|
|
45
|
+
*/
|
|
46
|
+
delete(key: string): Promise<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Returns key-value entries from scene storage, optionally filtered by prefix.
|
|
49
|
+
* Supports pagination via limit and offset.
|
|
50
|
+
* @param options - Optional { prefix, limit, offset } for filtering and pagination.
|
|
51
|
+
* @returns A promise that resolves to { data, pagination: { offset, total } } for pagination UI
|
|
52
|
+
* @throws Error if the read fails, so a failure is never mistaken for an empty page
|
|
53
|
+
*/
|
|
54
|
+
getValues(options?: GetValuesOptions): Promise<GetValuesResult>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
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 { assertStorageKey, serializeStorageValue } from './serialize';
|
|
7
|
+
import { createWriteQueue, SupersedingWriteFailed } from './write-queue';
|
|
8
|
+
/**
|
|
9
|
+
* Creates scene-scoped storage that provides methods to interact with
|
|
10
|
+
* scene-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 createSceneStorage = (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
|
+
// Writes to the same key are serialized (and rapid ones coalesced to the
|
|
20
|
+
// latest value) so the service commits them in issue order — overlapping
|
|
21
|
+
// PUTs would otherwise leave both the kept value and the cached value to
|
|
22
|
+
// response-order chance.
|
|
23
|
+
const writes = createWriteQueue();
|
|
24
|
+
async function executeSet(key, body) {
|
|
25
|
+
let baseUrl;
|
|
26
|
+
try {
|
|
27
|
+
baseUrl = await getStorageServerUrl();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
cache.delete(key);
|
|
31
|
+
console.error(`Failed to set storage value '${key}': ${errorMessage(error)}`);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const url = `${baseUrl}/values/${encodeURIComponent(key)}`;
|
|
35
|
+
const [error] = await wrapSignedFetch({
|
|
36
|
+
url,
|
|
37
|
+
init: {
|
|
38
|
+
method: 'PUT',
|
|
39
|
+
headers: {
|
|
40
|
+
'content-type': 'application/json'
|
|
41
|
+
},
|
|
42
|
+
body
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// Either way the entry changed server-side (or may have): detach any
|
|
46
|
+
// overlapping in-flight GET so its stale response is not cached.
|
|
47
|
+
inflightGets.delete(key);
|
|
48
|
+
if (error) {
|
|
49
|
+
// The PUT may have reached the server, so the cached body is no
|
|
50
|
+
// longer reliable.
|
|
51
|
+
cache.delete(key);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
cache.set(key, { body });
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
async function executeDelete(key) {
|
|
58
|
+
let baseUrl;
|
|
59
|
+
try {
|
|
60
|
+
baseUrl = await getStorageServerUrl();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
cache.delete(key);
|
|
64
|
+
throw new Error(`Failed to delete storage value '${key}': ${errorMessage(error)}`);
|
|
65
|
+
}
|
|
66
|
+
const url = `${baseUrl}/values/${encodeURIComponent(key)}`;
|
|
67
|
+
const [error, , status] = await wrapSignedFetch({
|
|
68
|
+
url,
|
|
69
|
+
init: {
|
|
70
|
+
method: 'DELETE',
|
|
71
|
+
headers: {}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// Detach again: a GET may have started while the DELETE was in flight.
|
|
75
|
+
inflightGets.delete(key);
|
|
76
|
+
if (error) {
|
|
77
|
+
// A 404 still confirms the key is absent server-side.
|
|
78
|
+
if (status === 404) {
|
|
79
|
+
cache.setAbsent(key);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// The DELETE may have reached the server, so the cached body is unreliable.
|
|
83
|
+
cache.delete(key);
|
|
84
|
+
throw new Error(`Failed to delete storage value '${key}': ${errorMessage(error)}`);
|
|
85
|
+
}
|
|
86
|
+
cache.setAbsent(key);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
async get(key, options) {
|
|
91
|
+
assertIsServer(MODULE_NAME);
|
|
92
|
+
assertStorageKey(key, 'Storage.get()');
|
|
93
|
+
if (writes.isPending(key))
|
|
94
|
+
await writes.settled(key);
|
|
95
|
+
if (config.cacheReads && !options?.fresh) {
|
|
96
|
+
const entry = cache.get(key);
|
|
97
|
+
if (entry?.absent)
|
|
98
|
+
return null;
|
|
99
|
+
// Parse per hit so each caller gets a fresh object (no shared mutation).
|
|
100
|
+
if (entry?.body !== undefined)
|
|
101
|
+
return JSON.parse(entry.body).value;
|
|
102
|
+
}
|
|
103
|
+
// Coalesce concurrent gets (even fresh ones: an in-flight response is
|
|
104
|
+
// milliseconds old, not TTL-stale) into a single network request.
|
|
105
|
+
const joined = inflightGets.get(key);
|
|
106
|
+
if (joined)
|
|
107
|
+
return joined.promise;
|
|
108
|
+
const inflight = {};
|
|
109
|
+
inflight.promise = (async () => {
|
|
110
|
+
try {
|
|
111
|
+
let baseUrl;
|
|
112
|
+
try {
|
|
113
|
+
baseUrl = await getStorageServerUrl();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
throw new Error(`Failed to get storage value '${key}': ${errorMessage(error)}`);
|
|
117
|
+
}
|
|
118
|
+
const url = `${baseUrl}/values/${encodeURIComponent(key)}`;
|
|
119
|
+
const [error, data, status] = await wrapSignedFetch({ url });
|
|
120
|
+
const isOwner = inflightGets.get(key) === inflight;
|
|
121
|
+
if (error) {
|
|
122
|
+
// A confirmed 404 is a first-class "absent" outcome, not a failure.
|
|
123
|
+
if (status === 404) {
|
|
124
|
+
if (isOwner)
|
|
125
|
+
cache.setAbsent(key);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`Failed to get storage value '${key}': ${errorMessage(error)}`);
|
|
129
|
+
}
|
|
130
|
+
// wrapSignedFetch parses an empty 2xx body as {}: a missing value is a fault, not an absence.
|
|
131
|
+
if (!data || data.value === undefined) {
|
|
132
|
+
throw new Error(`Failed to get storage value '${key}': response carried no value`);
|
|
133
|
+
}
|
|
134
|
+
// Same serialization shape as set()'s PUT body, so a read followed by
|
|
135
|
+
// an unchanged write can be skipped.
|
|
136
|
+
const body = JSON.stringify({ value: data.value });
|
|
137
|
+
if (isOwner)
|
|
138
|
+
cache.set(key, { body });
|
|
139
|
+
return data.value;
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
if (inflightGets.get(key) === inflight)
|
|
143
|
+
inflightGets.delete(key);
|
|
144
|
+
}
|
|
145
|
+
})();
|
|
146
|
+
inflightGets.set(key, inflight);
|
|
147
|
+
return inflight.promise;
|
|
148
|
+
},
|
|
149
|
+
async set(key, value, options) {
|
|
150
|
+
assertIsServer(MODULE_NAME);
|
|
151
|
+
assertStorageKey(key, 'Storage.set()');
|
|
152
|
+
const body = serializeStorageValue(value, `Storage.set('${key}')`);
|
|
153
|
+
const skipIfUnchanged = options?.skipIfUnchanged ?? config.skipIfUnchanged;
|
|
154
|
+
// Dedup against confirmed state only while no write is pending — a
|
|
155
|
+
// pending write makes the cache momentarily stale; enqueue() coalesces
|
|
156
|
+
// against pending writes instead.
|
|
157
|
+
if (skipIfUnchanged && writes.pending(key) === undefined && cache.get(key)?.body === body) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return writes.enqueue(key, body, (b) => executeSet(key, b), skipIfUnchanged);
|
|
161
|
+
},
|
|
162
|
+
async delete(key) {
|
|
163
|
+
assertIsServer(MODULE_NAME);
|
|
164
|
+
assertStorageKey(key, 'Storage.delete()');
|
|
165
|
+
return writes
|
|
166
|
+
.enqueue(key, null, () => executeDelete(key), true)
|
|
167
|
+
.catch((error) => {
|
|
168
|
+
if (error instanceof SupersedingWriteFailed) {
|
|
169
|
+
throw new Error(`Failed to delete storage value '${key}': ${error.message}`);
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
async getValues(options) {
|
|
175
|
+
assertIsServer(MODULE_NAME);
|
|
176
|
+
const { prefix, limit, offset } = options ?? {};
|
|
177
|
+
let baseUrl;
|
|
178
|
+
try {
|
|
179
|
+
baseUrl = await getStorageServerUrl();
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
throw new Error(`Failed to get storage values: ${errorMessage(error)}`);
|
|
183
|
+
}
|
|
184
|
+
const parts = [];
|
|
185
|
+
if (!!prefix) {
|
|
186
|
+
parts.push(`prefix=${encodeURIComponent(prefix)}`);
|
|
187
|
+
}
|
|
188
|
+
if (!!limit) {
|
|
189
|
+
parts.push(`limit=${limit}`);
|
|
190
|
+
}
|
|
191
|
+
if (!!offset) {
|
|
192
|
+
parts.push(`offset=${offset}`);
|
|
193
|
+
}
|
|
194
|
+
const query = parts.join('&');
|
|
195
|
+
const url = query ? `${baseUrl}/values?${query}` : `${baseUrl}/values`;
|
|
196
|
+
const watcher = cache.watch();
|
|
197
|
+
const [error, response] = await wrapSignedFetch({ url });
|
|
198
|
+
watcher.stop();
|
|
199
|
+
if (error) {
|
|
200
|
+
throw new Error(`Failed to get storage values: ${errorMessage(error)}`);
|
|
201
|
+
}
|
|
202
|
+
const data = response?.data;
|
|
203
|
+
if (!Array.isArray(data)) {
|
|
204
|
+
throw new Error('Failed to get storage values: response carried no data array');
|
|
205
|
+
}
|
|
206
|
+
if (data.some((entry) => typeof entry?.key !== 'string' || entry.value === undefined)) {
|
|
207
|
+
throw new Error('Failed to get storage values: response carried a malformed entry');
|
|
208
|
+
}
|
|
209
|
+
// Seed the per-key cache so subsequent get()/set() on returned keys can
|
|
210
|
+
// skip the network. Only keys with no live entry and no pending write
|
|
211
|
+
// are seeded: existing per-key state comes from a confirmed operation
|
|
212
|
+
// that this page snapshot — whose request started earlier — must not
|
|
213
|
+
// clobber with stale data. Absence is never seeded (prefix/pagination
|
|
214
|
+
// make it non-authoritative). A page larger than cacheMaxEntries churns
|
|
215
|
+
// the cache; entries repopulate lazily.
|
|
216
|
+
for (const entry of data) {
|
|
217
|
+
if (watcher.mutated(entry.key) || writes.isPending(entry.key) || inflightGets.has(entry.key))
|
|
218
|
+
continue;
|
|
219
|
+
if (cache.get(entry.key) === undefined) {
|
|
220
|
+
cache.set(entry.key, { body: JSON.stringify({ value: entry.value }) });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const requestedOffset = offset ?? 0;
|
|
224
|
+
const pagination = {
|
|
225
|
+
offset: response.pagination?.offset ?? requestedOffset,
|
|
226
|
+
total: response.pagination?.total ?? data.length
|
|
227
|
+
};
|
|
228
|
+
return { data, pagination };
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NlbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmVyL3N0b3JhZ2Uvc2NlbmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDcEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxlQUFlLEVBQUUsTUFBTSxVQUFVLENBQUE7QUFDMUQsT0FBTyxFQUNMLG1CQUFtQixFQUluQixXQUFXLEVBR1osTUFBTSxhQUFhLENBQUE7QUFDcEIsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZUFBZSxDQUFBO0FBQ2hELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDckUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLHNCQUFzQixFQUFFLE1BQU0sZUFBZSxDQUFBO0FBNER4RTs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLENBQUMsU0FBNkIsbUJBQW1CLEVBQUUsRUFBaUIsRUFBRTtJQUN0RyxNQUFNLEtBQUssR0FBRyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUN0Qyx5RUFBeUU7SUFDekUsMEZBQTBGO0lBQzFGLE1BQU0sWUFBWSxHQUFHLElBQUksR0FBRyxFQUF5QyxDQUFBO0lBQ3JFLHlFQUF5RTtJQUN6RSx5RUFBeUU7SUFDekUseUVBQXlFO0lBQ3pFLHlCQUF5QjtJQUN6QixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFBO0lBRWpDLEtBQUssVUFBVSxVQUFVLENBQUMsR0FBVyxFQUFFLElBQVk7UUFDakQsSUFBSSxPQUFlLENBQUE7UUFDbkIsSUFBSTtZQUNGLE9BQU8sR0FBRyxNQUFNLG1CQUFtQixFQUFFLENBQUE7U0FDdEM7UUFBQyxPQUFPLEtBQUssRUFBRTtZQUNkLEtBQUssQ0FB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SUFBSSxDQUFDLENBQUE7WUFDbkMsTUFBTSxVQUFVLEdBQUc7Z0JBQ2pCLE1BQU0sRUFBRSxRQUFTLENBQUMsVUFBVSxFQUFFLE1BQU0sSUFBSSxlQUFlO2dCQUN2RCxLQUFLLEVBQUUsUUFBUyxDQUFDLFVBQVUsRUFBRSxLQUFLLElBQUksSUFBSSxDQUFDLE1BQU07YUFDbEQsQ0FBQTtZQUVELE9BQU8sRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLENBQUE7UUFDN0IsQ0FBQztLQUNGLENBQUE7QUFDSCxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRTdG9yYWdlU2VydmVyVXJsIH0gZnJvbSAnLi4vc3RvcmFnZS11cmwnXG5pbXBvcnQgeyBhc3NlcnRJc1NlcnZlciwgd3JhcFNpZ25lZEZldGNoIH0gZnJvbSAnLi4vdXRpbHMnXG5pbXBvcnQge1xuICBjcmVhdGVTdG9yYWdlQ29uZmlnLFxuICBHZXRPcHRpb25zLFxuICBHZXRWYWx1ZXNPcHRpb25zLFxuICBHZXRWYWx1ZXNSZXN1bHQsXG4gIE1PRFVMRV9OQU1FLFxuICBTZXRPcHRpb25zLFxuICBTdG9yYWdlQ29uZmlnU3RhdGVcbn0gZnJvbSAnLi9jb25zdGFudHMnXG5pbXBvcnQgeyBjcmVhdGVWYWx1ZUNhY2hlIH0gZnJvbSAnLi92YWx1ZS1jYWNoZSdcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4vZXJyb3ItbWVzc2FnZSdcbmltcG9ydCB7IGFzc2VydFN0b3JhZ2VLZXksIHNlcmlhbGl6ZVN0b3JhZ2VWYWx1ZSB9IGZyb20gJy4vc2VyaWFsaXplJ1xuaW1wb3J0IHsgY3JlYXRlV3JpdGVRdWV1ZSwgU3VwZXJzZWRpbmdXcml0ZUZhaWxlZCB9IGZyb20gJy4vd3JpdGUtcXVldWUnXG5cbi8qKlxuICogU2NlbmUtc2NvcGVkIHN0b3JhZ2UgaW50ZXJmYWNlIGZvciBrZXktdmFsdWUgcGFpcnMgZnJvbSB0aGUgU2VydmVyIFNpZGUgU3RvcmFnZSBzZXJ2aWNlLlxuICogVGhpcyBpcyBOT1QgZmlsZXN5c3RlbSBzdG9yYWdlIC0gZGF0YSBpcyBzdG9yZWQgaW4gdGhlIHJlbW90ZSBzdG9yYWdlIHNlcnZpY2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSVNjZW5lU3RvcmFnZSB7XG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgYSB2YWx1ZSBmcm9tIHNjZW5lIHN0b3JhZ2UgYnkga2V5IGZyb20gdGhlIFNlcnZlciBTaWRlIFN0b3JhZ2Ugc2VydmljZS5cbiAgICpcbiAgICogQnkgZGVmYXVsdCAoY2FjaGVSZWFkcyksIHZhbHVlcyByZWFkIG9yIHdyaXR0ZW4gZHVyaW5nIHRoZSBsYXN0IGNhY2hlTWF4QWdlTXNcbiAgICogYXJlIHNlcnZlZCBmcm9tIGEgbG9jYWwgY2FjaGUgd2l0aG91dCBhIG5ldHdvcmsgcmVxdWVzdCwgaW5jbHVkaW5nIGNvbmZpcm1lZFxuICAgKiBcIm5vdCBmb3VuZFwiIHJlc3VsdHMuIENvbmN1cnJlbnQgZ2V0cyBmb3IgdGhlIHNhbWUga2V5IHNoYXJlIG9uZSByZXF1ZXN0LlxuICAgKiBPdXQtb2YtYmFuZCB3cml0ZXJzIChlLmcuIENMSSBzdG9yYWdlIGNvbW1hbmRzKSBtYXkgbm90IGJlIHZpc2libGUgZm9yIHVwIHRvXG4gICAqIGNhY2hlTWF4QWdlTXMg4oCUIHBhc3MgeyBmcmVzaDogdHJ1ZSB9IHRvIGZvcmNlIGEgbmV0d29yayByZWFkLlxuICAgKiBBIHJlYWQgZmlyc3Qgd2FpdHMgZm9yIHdyaXRlcyB0byB0aGUgc2FtZSBrZXkgdGhhdCB3ZXJlIHBlbmRpbmcgd2hlbiBpdCB3YXNcbiAgICogaXNzdWVkLCBzbyBpdCByZWZsZWN0cyBldmVyeSB3cml0ZSBpc3N1ZWQgYmVmb3JlIGl0LlxuICAgKiBAcGFyYW0ga2V5IC0gVGhlIGtleSB0byByZXRyaWV2ZVxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIHsgZnJlc2ggfSB0byBieXBhc3MgdGhlIHJlYWQgY2FjaGVcbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIHBhcnNlZCBKU09OIHZhbHVlLCBvciBudWxsIGlmIG5vdCBmb3VuZFxuICAgKiBAdGhyb3dzIEVycm9yIGlmIHRoZSByZWFkIGZhaWxzLCBzbyBhIGZhaWx1cmUgaXMgbmV2ZXIgbWlzdGFrZW4gZm9yIGEgbWlzc2luZyBrZXlcbiAgICogQHRocm93cyBUeXBlRXJyb3IgaWYgdGhlIGtleSBpcyBpbnZhbGlkXG4gICAqL1xuICBnZXQ8VCA9IHVua25vd24+KGtleTogc3RyaW5nLCBvcHRpb25zPzogR2V0T3B0aW9ucyk6IFByb21pc2U8VCB8IG51bGw+XG5cbiAgLyoqXG4gICAqIFN0b3JlcyBhIHZhbHVlIGluIHNjZW5lIHN0b3JhZ2UgaW4gdGhlIFNlcnZlciBTaWRlIFN0b3JhZ2Ugc2VydmljZS5cbiAgICogQHBhcmFtIGtleSAtIFRoZSBrZXkgdG8gc3RvcmUgdGhlIHZhbHVlIHVuZGVyXG4gICAqIEBwYXJhbSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byBzdG9yZSAod2lsbCBiZSBKU09OIHNlcmlhbGl6ZWQpXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gT3B0aW9uYWwgeyBza2lwSWZVbmNoYW5nZWQgfSB0byBza2lwIHRoZSBuZXR3b3JrIHdyaXRlIHdoZW4gdGhlIHZhbHVlIGlzIGFscmVhZHkgc3RvcmVkXG4gICAqIEByZXR1cm5zIHRydWUgb25jZSBzdG9yZWQsIG9yIG9uY2UgYSBuZXdlciB3cml0ZSB0aGF0IHJlcGxhY2VkIHRoaXMgb25lIGxhbmRzXG4gICAqIChyYXBpZCB3cml0ZXMgY29hbGVzY2UpOyBmYWxzZSBpZiB0aGUgd3JpdGUsIG9yIGl0cyByZXBsYWNlbWVudCwgZmFpbHNcbiAgICogQHRocm93cyBUeXBlRXJyb3IgaWYgdGhlIGtleSBpcyBpbnZhbGlkLCBvciB0aGUgdmFsdWUgY2Fubm90IGJlIHN0b3JlZCB1bmNoYW5nZWQ6IHVuZGVmaW5lZCxcbiAgICogY2lyY3VsYXIsIGEgQmlnSW50LCBvciBjb250YWluaW5nIGEgZnVuY3Rpb24sIGEgc3ltYm9sLCBhIG5vbi1maW5pdGUgbnVtYmVyLCBhbiB1bmRlZmluZWQgYXJyYXlcbiAgICogZWxlbWVudCBvciBob2xlLCBhIE1hcCwgU2V0LCB0eXBlZCBhcnJheSBvciBvdGhlciBidWlsdC1pbiB3aXRoIG5vIEpTT04gZm9ybSwgb3IgdGV4dCB3aXRoIGFuXG4gICAqIHVucGFpcmVkIHN1cnJvZ2F0ZSBvciBhIE5VTCBjaGFyYWN0ZXIuIEFuIHVuZGVmaW5lZCBvYmplY3QgcHJvcGVydHkgaXMgZHJvcHBlZDsgYSBEYXRlIGlzXG4gICAqIHN0b3JlZCBhcyBpdHMgSVNPIHN0cmluZyBhbmQgYSBjbGFzcyBpbnN0YW5jZSBhcyBpdHMgb3duIGVudW1lcmFibGUgZGF0YS5cbiAgICovXG4gIHNldDxUID0gdW5rbm93bj4oa2V5OiBzdHJpbmcsIHZhbHVlOiBULCBvcHRpb25zPzogU2V0T3B0aW9ucyk6IFByb21pc2U8Ym9vbGVhbj5cblxuICAvKipcbiAgICogRGVsZXRlcyBhIHZhbHVlIGZyb20gc2NlbmUgc3RvcmFnZSBpbiB0aGUgU2VydmVyIFNpZGUgU3RvcmFnZSBzZXJ2aWNlLlxuICAgKiBAcGFyYW0ga2V5IC0gVGhlIGtleSB0byBkZWxldGVcbiAgICogQHJldHVybnMgdHJ1ZSBvbmNlIGFwcGxpZWQsIG9yIG9uY2UgYSBuZXdlciB3cml0ZSB0aGF0IHJlcGxhY2VkIGl0IGxhbmRzO1xuICAgKiBmYWxzZSBvbmx5IGZvciBhIGNvbmZpcm1lZCA0MDRcbiAgICogQHRocm93cyBFcnJvciBpZiB0aGUgZGVsZXRlLCBvciBpdHMgcmVwbGFjZW1lbnQsIGZhaWxzOyBhIGZhaWx1cmUgaXMgbmV2ZXIgcmVwb3J0ZWQgYXMgYWJzZW5jZVxuICAgKiBAdGhyb3dzIFR5cGVFcnJvciBpZiB0aGUga2V5IGlzIGludmFsaWRcbiAgICovXG4gIGRlbGV0ZShrZXk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj5cblxuICAvKipcbiAgICogUmV0dXJucyBrZXktdmFsdWUgZW50cmllcyBmcm9tIHNjZW5lIHN0b3JhZ2UsIG9wdGlvbmFsbHkgZmlsdGVyZWQgYnkgcHJlZml4LlxuICAgKiBTdXBwb3J0cyBwYWdpbmF0aW9uIHZpYSBsaW1pdCBhbmQgb2Zmc2V0LlxuICAgKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbmFsIHsgcHJlZml4LCBsaW1pdCwgb2Zmc2V0IH0gZm9yIGZpbHRlcmluZyBhbmQgcGFnaW5hdGlvbi5cbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8geyBkYXRhLCBwYWdpbmF0aW9uOiB7IG9mZnNldCwgdG90YWwgfSB9IGZvciBwYWdpbmF0aW9uIFVJXG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIHJlYWQgZmFpbHMsIHNvIGEgZmFpbHVyZSBpcyBuZXZlciBtaXN0YWtlbiBmb3IgYW4gZW1wdHkgcGFnZVxuICAgKi9cbiAgZ2V0VmFsdWVzKG9wdGlvbnM/OiBHZXRWYWx1ZXNPcHRpb25zKTogUHJvbWlzZTxHZXRWYWx1ZXNSZXN1bHQ+XG59XG5cbi8qKlxuICogQ3JlYXRlcyBzY2VuZS1zY29wZWQgc3RvcmFnZSB0aGF0IHByb3ZpZGVzIG1ldGhvZHMgdG8gaW50ZXJhY3Qgd2l0aFxuICogc2NlbmUtc3BlY2lmaWMga2V5LXZhbHVlIHBhaXJzIGZyb20gdGhlIFNlcnZlciBTaWRlIFN0b3JhZ2Ugc2VydmljZS5cbiAqIFRoaXMgbW9kdWxlIG9ubHkgd29ya3Mgd2hlbiBydW5uaW5nIG9uIHNlcnZlci1zaWRlIHNjZW5lcy5cbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgY29uc3QgY3JlYXRlU2NlbmVTdG9yYWdlID0gKGNvbmZpZzogU3RvcmFnZUNvbmZpZ1N0YXRlID0gY3JlYXRlU3RvcmFnZUNvbmZpZygpKTogSVNjZW5lU3RvcmFnZSA9PiB7XG4gIGNvbnN0IGNhY2hlID0gY3JlYXRlVmFsdWVDYWNoZShjb25maWcpXG4gIC8vIEVhY2ggaW4tZmxpZ2h0IEdFVCBpcyB0cmFja2VkIGJ5IGEgd3JhcHBlciBvYmplY3Qgd2hvc2UgaWRlbnRpdHkgbWFya3NcbiAgLy8gb3duZXJzaGlwOiBhIGxhbmRlZCB3cml0ZSBkZXRhY2hlcyB0aGUgcGVuZGluZyBHRVQgc28gaXRzIHN0YWxlIHJlc3BvbnNlIGlzIG5vdCBjYWNoZWQuXG4gIGNvbnN0IGluZmxpZ2h0R2V0cyA9IG5ldyBNYXA8c3RyaW5nLCB7IHByb21pc2U6IFByb21pc2U8dW5rbm93bj4gfT4oKVxuICAvLyBXcml0ZXMgdG8gdGhlIHNhbWUga2V5IGFyZSBzZXJpYWxpemVkIChhbmQgcmFwaWQgb25lcyBjb2FsZXNjZWQgdG8gdGhlXG4gIC8vIGxhdGVzdCB2YWx1ZSkgc28gdGhlIHNlcnZpY2UgY29tbWl0cyB0aGVtIGluIGlzc3VlIG9yZGVyIOKAlCBvdmVybGFwcGluZ1xuICAvLyBQVVRzIHdvdWxkIG90aGVyd2lzZSBsZWF2ZSBib3RoIHRoZSBrZXB0IHZhbHVlIGFuZCB0aGUgY2FjaGVkIHZhbHVlIHRvXG4gIC8vIHJlc3BvbnNlLW9yZGVyIGNoYW5jZS5cbiAgY29uc3Qgd3JpdGVzID0gY3JlYXRlV3JpdGVRdWV1ZSgpXG5cbiAgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZVNldChrZXk6IHN0cmluZywgYm9keTogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgbGV0IGJhc2VVcmw6IHN0cmluZ1xuICAgIHRyeSB7XG4gICAgICBiYXNlVXJsID0gYXdhaXQgZ2V0U3RvcmFnZVNlcnZlclVybCgpXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNhY2hlLmRlbGV0ZShrZXkpXG4gICAgICBjb25zb2xlLmVycm9yKGBGYWlsZWQgdG8gc2V0IHN0b3JhZ2UgdmFsdWUgJyR7a2V5fSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIGNvbnN0IHVybCA9IGAke2Jhc2VVcmx9L3ZhbHVlcy8ke2VuY29kZVVSSUNvbXBvbmVudChrZXkpfWBcblxuICAgIGNvbnN0IFtlcnJvcl0gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2goe1xuICAgICAgdXJsLFxuICAgICAgaW5pdDoge1xuICAgICAgICBtZXRob2Q6ICdQVVQnLFxuICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgJ2NvbnRlbnQtdHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xuICAgICAgICB9LFxuICAgICAgICBib2R5XG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIEVpdGhlciB3YXkgdGhlIGVudHJ5IGNoYW5nZWQgc2VydmVyLXNpZGUgKG9yIG1heSBoYXZlKTogZGV0YWNoIGFueVxuICAgIC8vIG92ZXJsYXBwaW5nIGluLWZsaWdodCBHRVQgc28gaXRzIHN0YWxlIHJlc3BvbnNlIGlzIG5vdCBjYWNoZWQuXG4gICAgaW5mbGlnaHRHZXRzLmRlbGV0ZShrZXkpXG5cbiAgICBpZiAoZXJyb3IpIHtcbiAgICAgIC8vIFRoZSBQVVQgbWF5IGhhdmUgcmVhY2hlZCB0aGUgc2VydmVyLCBzbyB0aGUgY2FjaGVkIGJvZHkgaXMgbm9cbiAgICAgIC8vIGxvbmdlciByZWxpYWJsZS5cbiAgICAgIGNhY2hlLmRlbGV0ZShrZXkpXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICBjYWNoZS5zZXQoa2V5LCB7IGJvZHkgfSlcbiAgICByZXR1cm4gdHJ1ZVxuICB9XG5cbiAgYXN5bmMgZnVuY3Rpb24gZXhlY3V0ZURlbGV0ZShrZXk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGxldCBiYXNlVXJsOiBzdHJpbmdcbiAgICB0cnkge1xuICAgICAgYmFzZVVybCA9IGF3YWl0IGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjYWNoZS5kZWxldGUoa2V5KVxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZGVsZXRlIHN0b3JhZ2UgdmFsdWUgJyR7a2V5fSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgIH1cbiAgICBjb25zdCB1cmwgPSBgJHtiYXNlVXJsfS92YWx1ZXMvJHtlbmNvZGVVUklDb21wb25lbnQoa2V5KX1gXG5cbiAgICBjb25zdCBbZXJyb3IsICwgc3RhdHVzXSA9IGF3YWl0IHdyYXBTaWduZWRGZXRjaCh7XG4gICAgICB1cmwsXG4gICAgICBpbml0OiB7XG4gICAgICAgIG1ldGhvZDogJ0RFTEVURScsXG4gICAgICAgIGhlYWRlcnM6IHt9XG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIERldGFjaCBhZ2FpbjogYSBHRVQgbWF5IGhhdmUgc3RhcnRlZCB3aGlsZSB0aGUgREVMRVRFIHdhcyBpbiBmbGlnaHQuXG4gICAgaW5mbGlnaHRHZXRzLmRlbGV0ZShrZXkpXG5cbiAgICBpZiAoZXJyb3IpIHtcbiAgICAgIC8vIEEgNDA0IHN0aWxsIGNvbmZpcm1zIHRoZSBrZXkgaXMgYWJzZW50IHNlcnZlci1zaWRlLlxuICAgICAgaWYgKHN0YXR1cyA9PT0gNDA0KSB7XG4gICAgICAgIGNhY2hlLnNldEFic2VudChrZXkpXG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuICAgICAgLy8gVGhlIERFTEVURSBtYXkgaGF2ZSByZWFjaGVkIHRoZSBzZXJ2ZXIsIHNvIHRoZSBjYWNoZWQgYm9keSBpcyB1bnJlbGlhYmxlLlxuICAgICAgY2FjaGUuZGVsZXRlKGtleSlcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBzdG9yYWdlIHZhbHVlICcke2tleX0nOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICB9XG5cbiAgICBjYWNoZS5zZXRBYnNlbnQoa2V5KVxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGFzeW5jIGdldDxUID0gdW5rbm93bj4oa2V5OiBzdHJpbmcsIG9wdGlvbnM/OiBHZXRPcHRpb25zKTogUHJvbWlzZTxUIHwgbnVsbD4ge1xuICAgICAgYXNzZXJ0SXNTZXJ2ZXIoTU9EVUxFX05BTUUpXG4gICAgICBhc3NlcnRTdG9yYWdlS2V5KGtleSwgJ1N0b3JhZ2UuZ2V0KCknKVxuXG4gICAgICBpZiAod3JpdGVzLmlzUGVuZGluZyhrZXkpKSBhd2FpdCB3cml0ZXMuc2V0dGxlZChrZXkpXG5cbiAgICAgIGlmIChjb25maWcuY2FjaGVSZWFkcyAmJiAhb3B0aW9ucz8uZnJlc2gpIHtcbiAgICAgICAgY29uc3QgZW50cnkgPSBjYWNoZS5nZXQoa2V5KVxuICAgICAgICBpZiAoZW50cnk/LmFic2VudCkgcmV0dXJuIG51bGxcbiAgICAgICAgLy8gUGFyc2UgcGVyIGhpdCBzbyBlYWNoIGNhbGxlciBnZXRzIGEgZnJlc2ggb2JqZWN0IChubyBzaGFyZWQgbXV0YXRpb24pLlxuICAgICAgICBpZiAoZW50cnk/LmJvZHkgIT09IHVuZGVmaW5lZCkgcmV0dXJuIEpTT04ucGFyc2UoZW50cnkuYm9keSkudmFsdWUgYXMgVFxuICAgICAgfVxuXG4gICAgICAvLyBDb2FsZXNjZSBjb25jdXJyZW50IGdldHMgKGV2ZW4gZnJlc2ggb25lczogYW4gaW4tZmxpZ2h0IHJlc3BvbnNlIGlzXG4gICAgICAvLyBtaWxsaXNlY29uZHMgb2xkLCBub3QgVFRMLXN0YWxlKSBpbnRvIGEgc2luZ2xlIG5ldHdvcmsgcmVxdWVzdC5cbiAgICAgIGNvbnN0IGpvaW5lZCA9IGluZmxpZ2h0R2V0cy5nZXQoa2V5KVxuICAgICAgaWYgKGpvaW5lZCkgcmV0dXJuIGpvaW5lZC5wcm9taXNlIGFzIFByb21pc2U8VCB8IG51bGw+XG5cbiAgICAgIGNvbnN0IGluZmxpZ2h0ID0ge30gYXMgeyBwcm9taXNlOiBQcm9taXNlPFQgfCBudWxsPiB9XG4gICAgICBpbmZsaWdodC5wcm9taXNlID0gKGFzeW5jICgpID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBsZXQgYmFzZVVybDogc3RyaW5nXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGJhc2VVcmwgPSBhd2FpdCBnZXRTdG9yYWdlU2VydmVyVXJsKClcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHN0b3JhZ2UgdmFsdWUgJyR7a2V5fSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCB1cmwgPSBgJHtiYXNlVXJsfS92YWx1ZXMvJHtlbmNvZGVVUklDb21wb25lbnQoa2V5KX1gXG5cbiAgICAgICAgICBjb25zdCBbZXJyb3IsIGRhdGEsIHN0YXR1c10gPSBhd2FpdCB3cmFwU2lnbmVkRmV0Y2g8eyB2YWx1ZTogVCB9Pih7IHVybCB9KVxuXG4gICAgICAgICAgY29uc3QgaXNPd25lciA9IGluZmxpZ2h0R2V0cy5nZXQoa2V5KSA9PT0gaW5mbGlnaHRcblxuICAgICAgICAgIGlmIChlcnJvcikge1xuICAgICAgICAgICAgLy8gQSBjb25maXJtZWQgNDA0IGlzIGEgZmlyc3QtY2xhc3MgXCJhYnNlbnRcIiBvdXRjb21lLCBub3QgYSBmYWlsdXJlLlxuICAgICAgICAgICAgaWYgKHN0YXR1cyA9PT0gNDA0KSB7XG4gICAgICAgICAgICAgIGlmIChpc093bmVyKSBjYWNoZS5zZXRBYnNlbnQoa2V5KVxuICAgICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHN0b3JhZ2UgdmFsdWUgJyR7a2V5fSc6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIHdyYXBTaWduZWRGZXRjaCBwYXJzZXMgYW4gZW1wdHkgMnh4IGJvZHkgYXMge306IGEgbWlzc2luZyB2YWx1ZSBpcyBhIGZhdWx0LCBub3QgYW4gYWJzZW5jZS5cbiAgICAgICAgICBpZiAoIWRhdGEgfHwgZGF0YS52YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgc3RvcmFnZSB2YWx1ZSAnJHtrZXl9JzogcmVzcG9uc2UgY2FycmllZCBubyB2YWx1ZWApXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gU2FtZSBzZXJpYWxpemF0aW9uIHNoYXBlIGFzIHNldCgpJ3MgUFVUIGJvZHksIHNvIGEgcmVhZCBmb2xsb3dlZCBieVxuICAgICAgICAgIC8vIGFuIHVuY2hhbmdlZCB3cml0ZSBjYW4gYmUgc2tpcHBlZC5cbiAgICAgICAgICBjb25zdCBib2R5ID0gSlNPTi5zdHJpbmdpZnkoeyB2YWx1ZTogZGF0YS52YWx1ZSB9KVxuICAgICAgICAgIGlmIChpc093bmVyKSBjYWNoZS5zZXQoa2V5LCB7IGJvZHkgfSlcbiAgICAgICAgICByZXR1cm4gZGF0YS52YWx1ZVxuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIGlmIChpbmZsaWdodEdldHMuZ2V0KGtleSkgPT09IGluZmxpZ2h0KSBpbmZsaWdodEdldHMuZGVsZXRlKGtleSlcbiAgICAgICAgfVxuICAgICAgfSkoKVxuXG4gICAgICBpbmZsaWdodEdldHMuc2V0KGtleSwgaW5mbGlnaHQpXG4gICAgICByZXR1cm4gaW5mbGlnaHQucHJvbWlzZVxuICAgIH0sXG5cbiAgICBhc3luYyBzZXQ8VCA9IHVua25vd24+KGtleTogc3RyaW5nLCB2YWx1ZTogVCwgb3B0aW9ucz86IFNldE9wdGlvbnMpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICAgIGFzc2VydElzU2VydmVyKE1PRFVMRV9OQU1FKVxuICAgICAgYXNzZXJ0U3RvcmFnZUtleShrZXksICdTdG9yYWdlLnNldCgpJylcblxuICAgICAgY29uc3QgYm9keSA9IHNlcmlhbGl6ZVN0b3JhZ2VWYWx1ZSh2YWx1ZSwgYFN0b3JhZ2Uuc2V0KCcke2tleX0nKWApXG4gICAgICBjb25zdCBza2lwSWZVbmNoYW5nZWQgPSBvcHRpb25zPy5za2lwSWZVbmNoYW5nZWQgPz8gY29uZmlnLnNraXBJZlVuY2hhbmdlZFxuXG4gICAgICAvLyBEZWR1cCBhZ2FpbnN0IGNvbmZpcm1lZCBzdGF0ZSBvbmx5IHdoaWxlIG5vIHdyaXRlIGlzIHBlbmRpbmcg4oCUIGFcbiAgICAgIC8vIHBlbmRpbmcgd3JpdGUgbWFrZXMgdGhlIGNhY2hlIG1vbWVudGFyaWx5IHN0YWxlOyBlbnF1ZXVlKCkgY29hbGVzY2VzXG4gICAgICAvLyBhZ2FpbnN0IHBlbmRpbmcgd3JpdGVzIGluc3RlYWQuXG4gICAgICBpZiAoc2tpcElmVW5jaGFuZ2VkICYmIHdyaXRlcy5wZW5kaW5nKGtleSkgPT09IHVuZGVmaW5lZCAmJiBjYWNoZS5nZXQoa2V5KT8uYm9keSA9PT0gYm9keSkge1xuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gd3JpdGVzLmVucXVldWUoa2V5LCBib2R5LCAoYikgPT4gZXhlY3V0ZVNldChrZXksIGIgYXMgc3RyaW5nKSwgc2tpcElmVW5jaGFuZ2VkKVxuICAgIH0sXG5cbiAgICBhc3luYyBkZWxldGUoa2V5OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICAgIGFzc2VydElzU2VydmVyKE1PRFVMRV9OQU1FKVxuICAgICAgYXNzZXJ0U3RvcmFnZUtleShrZXksICdTdG9yYWdlLmRlbGV0ZSgpJylcblxuICAgICAgcmV0dXJuIHdyaXRlc1xuICAgICAgICAuZW5xdWV1ZShrZXksIG51bGwsICgpID0+IGV4ZWN1dGVEZWxldGUoa2V5KSwgdHJ1ZSlcbiAgICAgICAgLmNhdGNoKChlcnJvcjogdW5rbm93bikgPT4ge1xuICAgICAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIFN1cGVyc2VkaW5nV3JpdGVGYWlsZWQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBzdG9yYWdlIHZhbHVlICcke2tleX0nOiAke2Vycm9yLm1lc3NhZ2V9YClcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhyb3cgZXJyb3JcbiAgICAgICAgfSlcbiAgICB9LFxuXG4gICAgYXN5bmMgZ2V0VmFsdWVzKG9wdGlvbnM/OiBHZXRWYWx1ZXNPcHRpb25zKTogUHJvbWlzZTxHZXRWYWx1ZXNSZXN1bHQ+IHtcbiAgICAgIGFzc2VydElzU2VydmVyKE1PRFVMRV9OQU1FKVxuXG4gICAgICBjb25zdCB7IHByZWZpeCwgbGltaXQsIG9mZnNldCB9ID0gb3B0aW9ucyA/PyB7fVxuICAgICAgbGV0IGJhc2VVcmw6IHN0cmluZ1xuICAgICAgdHJ5IHtcbiAgICAgICAgYmFzZVVybCA9IGF3YWl0IGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gZ2V0IHN0b3JhZ2UgdmFsdWVzOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdXG5cbiAgICAgIGlmICghIXByZWZpeCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBwcmVmaXg9JHtlbmNvZGVVUklDb21wb25lbnQocHJlZml4KX1gKVxuICAgICAgfVxuXG4gICAgICBpZiAoISFsaW1pdCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBsaW1pdD0ke2xpbWl0fWApXG4gICAgICB9XG5cbiAgICAgIGlmICghIW9mZnNldCkge1xuICAgICAgICBwYXJ0cy5wdXNoKGBvZmZzZXQ9JHtvZmZzZXR9YClcbiAgICAgIH1cblxuICAgICAgY29uc3QgcXVlcnkgPSBwYXJ0cy5qb2luKCcmJylcbiAgICAgIGNvbnN0IHVybCA9IHF1ZXJ5ID8gYCR7YmFzZVVybH0vdmFsdWVzPyR7cXVlcnl9YCA6IGAke2Jhc2VVcmx9L3ZhbHVlc2BcblxuICAgICAgY29uc3Qgd2F0Y2hlciA9IGNhY2hlLndhdGNoKClcbiAgICAgIGNvbnN0IFtlcnJvciwgcmVzcG9uc2VdID0gYXdhaXQgd3JhcFNpZ25lZEZldGNoPEdldFZhbHVlc1Jlc3VsdD4oeyB1cmwgfSlcbiAgICAgIHdhdGNoZXIuc3RvcCgpXG5cbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBnZXQgc3RvcmFnZSB2YWx1ZXM6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gKVxuICAgICAgfVxuXG4gICAgICBjb25zdCBkYXRhID0gcmVzcG9uc2U/LmRhdGFcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShkYXRhKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBnZXQgc3RvcmFnZSB2YWx1ZXM6IHJlc3BvbnNlIGNhcnJpZWQgbm8gZGF0YSBhcnJheScpXG4gICAgICB9XG4gICAgICBpZiAoZGF0YS5zb21lKChlbnRyeSkgPT4gdHlwZW9mIGVudHJ5Py5rZXkgIT09ICdzdHJpbmcnIHx8IGVudHJ5LnZhbHVlID09PSB1bmRlZmluZWQpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIGdldCBzdG9yYWdlIHZhbHVlczogcmVzcG9uc2UgY2FycmllZCBhIG1hbGZvcm1lZCBlbnRyeScpXG4gICAgICB9XG5cbiAgICAgIC8vIFNlZWQgdGhlIHBlci1rZXkgY2FjaGUgc28gc3Vic2VxdWVudCBnZXQoKS9zZXQoKSBvbiByZXR1cm5lZCBrZXlzIGNhblxuICAgICAgLy8gc2tpcCB0aGUgbmV0d29yay4gT25seSBrZXlzIHdpdGggbm8gbGl2ZSBlbnRyeSBhbmQgbm8gcGVuZGluZyB3cml0ZVxuICAgICAgLy8gYXJlIHNlZWRlZDogZXhpc3RpbmcgcGVyLWtleSBzdGF0ZSBjb21lcyBmcm9tIGEgY29uZmlybWVkIG9wZXJhdGlvblxuICAgICAgLy8gdGhhdCB0aGlzIHBhZ2Ugc25hcHNob3Qg4oCUIHdob3NlIHJlcXVlc3Qgc3RhcnRlZCBlYXJsaWVyIOKAlCBtdXN0IG5vdFxuICAgICAgLy8gY2xvYmJlciB3aXRoIHN0YWxlIGRhdGEuIEFic2VuY2UgaXMgbmV2ZXIgc2VlZGVkIChwcmVmaXgvcGFnaW5hdGlvblxuICAgICAgLy8gbWFrZSBpdCBub24tYXV0aG9yaXRhdGl2ZSkuIEEgcGFnZSBsYXJnZXIgdGhhbiBjYWNoZU1heEVudHJpZXMgY2h1cm5zXG4gICAgICAvLyB0aGUgY2FjaGU7IGVudHJpZXMgcmVwb3B1bGF0ZSBsYXppbHkuXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGRhdGEpIHtcbiAgICAgICAgaWYgKHdhdGNoZXIubXV0YXRlZChlbnRyeS5rZXkpIHx8IHdyaXRlcy5pc1BlbmRpbmcoZW50cnkua2V5KSB8fCBpbmZsaWdodEdldHMuaGFzKGVudHJ5LmtleSkpIGNvbnRpbnVlXG4gICAgICAgIGlmIChjYWNoZS5nZXQoZW50cnkua2V5KSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY2FjaGUuc2V0KGVudHJ5LmtleSwgeyBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHZhbHVlOiBlbnRyeS52YWx1ZSB9KSB9KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJlcXVlc3RlZE9mZnNldCA9IG9mZnNldCA/PyAwXG4gICAgICBjb25zdCBwYWdpbmF0aW9uID0ge1xuICAgICAgICBvZmZzZXQ6IHJlc3BvbnNlIS5wYWdpbmF0aW9uPy5vZmZzZXQgPz8gcmVxdWVzdGVkT2Zmc2V0LFxuICAgICAgICB0b3RhbDogcmVzcG9uc2UhLnBhZ2luYXRpb24/LnRvdGFsID8/IGRhdGEubGVuZ3RoXG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7IGRhdGEsIHBhZ2luYXRpb24gfVxuICAgIH1cbiAgfVxufVxuIl19
|