@dbx-tools/appkit 0.9.118 → 0.9.120
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/README.md +27 -21
- package/index.ts +6 -1
- package/lib/index.d.ts +6 -1
- package/lib/index.js +5 -2
- package/lib/src/appkit.js +17 -20
- package/lib/src/{_cache-storage.d.ts → cache-storage.d.ts} +28 -16
- package/lib/src/cache-storage.js +276 -0
- package/lib/src/files-cache.d.ts +2 -4
- package/lib/src/files-cache.js +39 -38
- package/lib/src/identity.d.ts +12 -0
- package/lib/src/identity.js +69 -3
- package/lib/src/interceptor.js +6 -3
- package/lib/src/lakebase-resolver.d.ts +14 -0
- package/lib/src/lakebase-resolver.js +22 -3
- package/lib/src/migration.js +6 -7
- package/lib/src/plugin-execution.js +3 -3
- package/lib/src/provision.js +2 -2
- package/lib/src/tool-operations.d.ts +33 -0
- package/lib/src/tool-operations.js +43 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -9
- package/src/appkit.ts +16 -21
- package/src/{_cache-storage.ts → cache-storage.ts} +138 -71
- package/src/files-cache.ts +38 -44
- package/src/identity.ts +67 -2
- package/src/interceptor.ts +5 -2
- package/src/lakebase-resolver.ts +23 -1
- package/src/migration.ts +3 -5
- package/src/plugin-execution.ts +2 -2
- package/src/provision.ts +1 -1
- package/src/tool-operations.ts +80 -0
- package/lib/src/_cache-storage.js +0 -235
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Soft-fail Lakebase cache storage for AppKit's persistent cache.
|
|
3
|
-
*
|
|
4
|
-
* AppKit's `PersistentStorage.initialize()` runs DDL migrations and throws on
|
|
5
|
-
* any step failure. With `cache.strictPersistence: true` that throw makes
|
|
6
|
-
* `CacheManager` silently disable the cache. This wraps the same storage,
|
|
7
|
-
* still runs its migrations, but logs a failed step instead of throwing so a
|
|
8
|
-
* usable table keeps serving.
|
|
9
|
-
*
|
|
10
|
-
* `PersistentStorage` is not on AppKit's public export map. The deep load is
|
|
11
|
-
* lazy and best-effort: if the installed AppKit layout changes, this returns
|
|
12
|
-
* `undefined` and `createApp` leaves AppKit on its default cache path.
|
|
13
|
-
*
|
|
14
|
-
* @module
|
|
15
|
-
*/
|
|
16
|
-
import { createRequire } from "node:module";
|
|
17
|
-
import { dirname, join } from "node:path";
|
|
18
|
-
import { createLakebasePool, getWorkspaceClient } from "@databricks/appkit";
|
|
19
|
-
import { postgresConnectionOptions } from "@dbx-tools/postgres";
|
|
20
|
-
import { errorUtils, hash, log } from "@dbx-tools/shared-core";
|
|
21
|
-
import { LRUCache } from "lru-cache";
|
|
22
|
-
import { handleOwnershipMigrationError } from "./migration.js";
|
|
23
|
-
const logger = log.logger("cache-storage");
|
|
24
|
-
let persistentStorageCtor = null;
|
|
25
|
-
/** Process-local LRU that avoids a persistent cache query on every warm hit. */
|
|
26
|
-
export class L1CacheStorage {
|
|
27
|
-
storage;
|
|
28
|
-
entries;
|
|
29
|
-
constructor(storage, options = {}) {
|
|
30
|
-
this.storage = storage;
|
|
31
|
-
this.entries = new LRUCache({
|
|
32
|
-
max: positiveBound(options.maxEntries, 1_000, "maxEntries"),
|
|
33
|
-
maxSize: positiveBound(options.maxBytes, 64 * 1024 * 1024, "maxBytes"),
|
|
34
|
-
sizeCalculation: (entry, key) => retainedSize(key, entry),
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
async get(key) {
|
|
38
|
-
const local = this.entries.get(key);
|
|
39
|
-
if (local)
|
|
40
|
-
return local;
|
|
41
|
-
const persisted = await this.storage.get(key);
|
|
42
|
-
if (persisted && persisted.expiry > Date.now())
|
|
43
|
-
this.retain(key, persisted);
|
|
44
|
-
return persisted;
|
|
45
|
-
}
|
|
46
|
-
async set(key, entry) {
|
|
47
|
-
await this.storage.set(key, entry);
|
|
48
|
-
this.retain(key, entry);
|
|
49
|
-
}
|
|
50
|
-
async delete(key) {
|
|
51
|
-
await this.storage.delete(key);
|
|
52
|
-
this.entries.delete(key);
|
|
53
|
-
}
|
|
54
|
-
async clear() {
|
|
55
|
-
await this.storage.clear();
|
|
56
|
-
this.entries.clear();
|
|
57
|
-
}
|
|
58
|
-
async has(key) {
|
|
59
|
-
if (this.entries.has(key))
|
|
60
|
-
return true;
|
|
61
|
-
return this.storage.has(key);
|
|
62
|
-
}
|
|
63
|
-
size() {
|
|
64
|
-
return this.storage.size();
|
|
65
|
-
}
|
|
66
|
-
isPersistent() {
|
|
67
|
-
return this.storage.isPersistent();
|
|
68
|
-
}
|
|
69
|
-
healthCheck() {
|
|
70
|
-
return this.storage.healthCheck();
|
|
71
|
-
}
|
|
72
|
-
async close() {
|
|
73
|
-
this.entries.clear();
|
|
74
|
-
await this.storage.close();
|
|
75
|
-
}
|
|
76
|
-
/** Forward AppKit's runtime-only persistent cleanup hook when available. */
|
|
77
|
-
async cleanupExpired() {
|
|
78
|
-
const cleanup = this.storage.cleanupExpired;
|
|
79
|
-
return cleanup ? cleanup.call(this.storage) : 0;
|
|
80
|
-
}
|
|
81
|
-
retain(key, entry) {
|
|
82
|
-
const ttl = entry.expiry - Date.now();
|
|
83
|
-
if (ttl > 0)
|
|
84
|
-
this.entries.set(key, entry, { ttl });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function positiveBound(value, fallback, name) {
|
|
88
|
-
const resolved = value ?? fallback;
|
|
89
|
-
if (!Number.isFinite(resolved) || resolved <= 0) {
|
|
90
|
-
throw new RangeError(`${name} must be a finite positive number`);
|
|
91
|
-
}
|
|
92
|
-
return Math.floor(resolved);
|
|
93
|
-
}
|
|
94
|
-
function retainedSize(key, entry) {
|
|
95
|
-
try {
|
|
96
|
-
return Buffer.byteLength(key) + Buffer.byteLength(JSON.stringify(entry));
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
return Number.MAX_SAFE_INTEGER;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Lazily resolve AppKit's internal `PersistentStorage` constructor. `null`
|
|
104
|
-
* means not attempted yet; `undefined` means the lookup already failed.
|
|
105
|
-
*/
|
|
106
|
-
export function loadPersistentStorage() {
|
|
107
|
-
if (persistentStorageCtor !== null) {
|
|
108
|
-
return persistentStorageCtor;
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
const require = createRequire(import.meta.url);
|
|
112
|
-
const modulePath = join(dirname(require.resolve("@databricks/appkit")), "cache/storage/persistent.js");
|
|
113
|
-
const loaded = require(modulePath).PersistentStorage;
|
|
114
|
-
if (typeof loaded !== "function") {
|
|
115
|
-
logger.debug("soft persistent cache skipped (PersistentStorage missing)");
|
|
116
|
-
persistentStorageCtor = undefined;
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
persistentStorageCtor = loaded;
|
|
120
|
-
return loaded;
|
|
121
|
-
}
|
|
122
|
-
catch (err) {
|
|
123
|
-
logger.debug("soft persistent cache skipped (PersistentStorage unavailable)", {
|
|
124
|
-
error: errorUtils.errorMessage(err),
|
|
125
|
-
});
|
|
126
|
-
persistentStorageCtor = undefined;
|
|
127
|
-
return undefined;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Whether an existing cache table belongs to another role.
|
|
132
|
-
*
|
|
133
|
-
* PostgreSQL allows granted reads/writes but reserves index DDL for the table
|
|
134
|
-
* owner. Skip AppKit migrations in that case; {@link probeStorage} still proves
|
|
135
|
-
* the existing table can serve the cache before it is accepted.
|
|
136
|
-
*/
|
|
137
|
-
async function tableOwnedByAnotherRole(storage, pool) {
|
|
138
|
-
if (!storage.schemaName || !storage.tableName)
|
|
139
|
-
return false;
|
|
140
|
-
const result = await pool.query(`SELECT pg_get_userbyid(c.relowner) = current_user AS owned
|
|
141
|
-
FROM pg_class c
|
|
142
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
143
|
-
WHERE n.nspname = $1 AND c.relname = $2 AND c.relkind IN ('r', 'p')`, [storage.schemaName, storage.tableName]);
|
|
144
|
-
if (result.rows[0]?.owned !== false)
|
|
145
|
-
return false;
|
|
146
|
-
logger.warn("persistent cache migrations skipped (table owned by another role)", {
|
|
147
|
-
schema: storage.schemaName,
|
|
148
|
-
table: storage.tableName,
|
|
149
|
-
});
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
/** Soften `initialize()` so an ownership-only migration failure is not fatal. */
|
|
153
|
-
export function softenInitialize(storage, skipMigrations) {
|
|
154
|
-
const originalInitialize = storage.initialize.bind(storage);
|
|
155
|
-
let softInitPromise;
|
|
156
|
-
storage.initialize = async () => {
|
|
157
|
-
if (storage.initialized)
|
|
158
|
-
return;
|
|
159
|
-
softInitPromise ??= (async () => {
|
|
160
|
-
try {
|
|
161
|
-
if (!(await skipMigrations?.()))
|
|
162
|
-
await originalInitialize();
|
|
163
|
-
}
|
|
164
|
-
catch (err) {
|
|
165
|
-
if (!handleOwnershipMigrationError(err, {
|
|
166
|
-
scope: "cache-storage",
|
|
167
|
-
logger,
|
|
168
|
-
event: "persistent cache migration failed",
|
|
169
|
-
})) {
|
|
170
|
-
throw err;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
})();
|
|
174
|
-
try {
|
|
175
|
-
await softInitPromise;
|
|
176
|
-
storage.initialized = true;
|
|
177
|
-
}
|
|
178
|
-
catch (err) {
|
|
179
|
-
softInitPromise = undefined;
|
|
180
|
-
throw err;
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/** Verify the migrated cache table can serve the reads and writes AppKit needs. */
|
|
185
|
-
export async function probeStorage(storage) {
|
|
186
|
-
const key = `dbx-tools:cache-probe:${hash.id()}`;
|
|
187
|
-
try {
|
|
188
|
-
await storage.set(key, { value: key, expiry: Date.now() + 60_000 });
|
|
189
|
-
const result = await storage.get(key);
|
|
190
|
-
if (result?.value !== key) {
|
|
191
|
-
throw new Error("persistent cache probe returned an unexpected value");
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
await storage.delete(key).catch(() => undefined);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Build a soft-fail Lakebase cache storage when a pool can be created and
|
|
200
|
-
* AppKit's PersistentStorage can be loaded. Returns `undefined` otherwise so
|
|
201
|
-
* AppKit can fall through to its normal cache path.
|
|
202
|
-
*/
|
|
203
|
-
export async function createSoftPersistentStorage(cache) {
|
|
204
|
-
const PersistentStorage = loadPersistentStorage();
|
|
205
|
-
if (!PersistentStorage) {
|
|
206
|
-
return undefined;
|
|
207
|
-
}
|
|
208
|
-
let pool;
|
|
209
|
-
try {
|
|
210
|
-
const createdPool = createLakebasePool(postgresConnectionOptions({ workspaceClient: getWorkspaceClient({}) }));
|
|
211
|
-
pool = createdPool;
|
|
212
|
-
const storage = new PersistentStorage(cache ?? {}, createdPool);
|
|
213
|
-
softenInitialize(storage, () => tableOwnedByAnotherRole(storage, createdPool));
|
|
214
|
-
if (!(await storage.healthCheck())) {
|
|
215
|
-
await storage.close().catch(() => { });
|
|
216
|
-
return undefined;
|
|
217
|
-
}
|
|
218
|
-
await storage.initialize();
|
|
219
|
-
await probeStorage(storage);
|
|
220
|
-
return new L1CacheStorage(storage, {
|
|
221
|
-
maxBytes: cache?.maxBytes,
|
|
222
|
-
maxEntries: cache?.maxSize,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
catch (err) {
|
|
226
|
-
logger.debug("soft persistent cache unavailable", {
|
|
227
|
-
error: errorUtils.errorMessage(err),
|
|
228
|
-
});
|
|
229
|
-
if (pool) {
|
|
230
|
-
await pool.end().catch(() => { });
|
|
231
|
-
}
|
|
232
|
-
return undefined;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiX2NhY2hlLXN0b3JhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvX2NhY2hlLXN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFFSCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzVDLE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzFDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxrQkFBa0IsRUFBb0IsTUFBTSxvQkFBb0IsQ0FBQztBQUM5RixPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3JDLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRS9ELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7QUE4QjNDLElBQUkscUJBQXFCLEdBQW9ELElBQUksQ0FBQztBQUVsRixnRkFBZ0Y7QUFDaEYsTUFBTSxPQUFPLGNBQWM7SUFJTjtJQUhGLE9BQU8sQ0FBK0I7SUFFdkQsWUFDbUIsT0FBcUIsRUFDdEMsVUFBaUMsRUFBRTtRQURsQixZQUFPLEdBQVAsT0FBTyxDQUFjO1FBR3RDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxRQUFRLENBQUM7WUFDMUIsR0FBRyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEtBQUssRUFBRSxZQUFZLENBQUM7WUFDM0QsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxFQUFFLFVBQVUsQ0FBQztZQUN0RSxlQUFlLEVB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RyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7WUFDdEMsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELE1BQU0sT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQzNCLE1BQU0sWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzVCLE9BQU8sSUFBSSxjQUFjLENBQUMsT0FBTyxFQUFFO1lBQ2pDLFFBQVEsRUFBRSxLQUFLLEVBQUUsUUFBUTtZQUN6QixVQUFVLEVBQUUsS0FBSyxFQUFFLE9BQU87U0FDM0IsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsS0FBSyxDQUFDLG1DQUFtQyxFQUFFO1lBQ2hELEtBQUssRUFBRSxVQUFVLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQztTQUNwQyxDQUFDLENBQUM7UUFDSCxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ1QsTUFBTSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxHQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ25DLENBQUM7UUFDRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU29mdC1mYWlsIExha2ViYXNlIGNhY2hlIHN0b3JhZ2UgZm9yIEFwcEtpdCdzIHBlcnNpc3RlbnQgY2FjaGUuXG4gKlxuICogQXBwS2l0J3MgYFBlcnNpc3RlbnRTdG9yYWdlLmluaXRpYWxpemUoKWAgcnVucyBEREwgbWlncmF0aW9ucyBhbmQgdGhyb3dzIG9uXG4gKiBhbnkgc3RlcCBmYWlsdXJlLiBXaXRoIGBjYWNoZS5zdHJpY3RQZXJzaXN0ZW5jZTogdHJ1ZWAgdGhhdCB0aHJvdyBtYWtlc1xuICogYENhY2hlTWFuYWdlcmAgc2lsZW50bHkgZGlzYWJsZSB0aGUgY2FjaGUuIFRoaXMgd3JhcHMgdGhlIHNhbWUgc3RvcmFnZSxcbiAqIHN0aWxsIHJ1bnMgaXRzIG1pZ3JhdGlvbnMsIGJ1dCBsb2dzIGEgZmFpbGVkIHN0ZXAgaW5zdGVhZCBvZiB0aHJvd2luZyBzbyBhXG4gKiB1c2FibGUgdGFibGUga2VlcHMgc2VydmluZy5cbiAqXG4gKiBgUGVyc2lzdGVudFN0b3JhZ2VgIGlzIG5vdCBvbiBBcHBLaXQncyBwdWJsaWMgZXhwb3J0IG1hcC4gVGhlIGRlZXAgbG9hZCBpc1xuICogbGF6eSBhbmQgYmVzdC1lZmZvcnQ6IGlmIHRoZSBpbnN0YWxsZWQgQXBwS2l0IGxheW91dCBjaGFuZ2VzLCB0aGlzIHJldHVybnNcbiAqIGB1bmRlZmluZWRgIGFuZCBgY3JlYXRlQXBwYCBsZWF2ZXMgQXBwS2l0IG9uIGl0cyBkZWZhdWx0IGNhY2hlIHBhdGguXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgfSBmcm9tIFwibm9kZTptb2R1bGVcIjtcbmltcG9ydCB7IGRpcm5hbWUsIGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBjcmVhdGVMYWtlYmFzZVBvb2wsIGdldFdvcmtzcGFjZUNsaWVudCwgdHlwZSBDYWNoZUNvbmZpZyB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IHBvc3RncmVzQ29ubmVjdGlvbk9wdGlvbnMgfSBmcm9tIFwiQGRieC10b29scy9wb3N0Z3Jlc1wiO1xuaW1wb3J0IHsgZXJyb3JVdGlscywgaGFzaCwgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7IExSVUNhY2hlIH0gZnJvbSBcImxydS1jYWNoZVwiO1xuaW1wb3J0IHsgaGFuZGxlT3duZXJzaGlwTWlncmF0aW9uRXJyb3IgfSBmcm9tIFwiLi9taWdyYXRpb24udHNcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImNhY2hlLXN0b3JhZ2VcIik7XG5cbnR5cGUgTGFrZWJhc2VQb29sID0gUmV0dXJuVHlwZTx0eXBlb2YgY3JlYXRlTGFrZWJhc2VQb29sPjtcbnR5cGUgQ2FjaGVTdG9yYWdlID0gTm9uTnVsbGFibGU8Q2FjaGVDb25maWdbXCJzdG9yYWdlXCJdPjtcblxuaW50ZXJmYWNlIENhY2hlRW50cnk8VCA9IHVua25vd24+IHtcbiAgZXhwaXJ5OiBudW1iZXI7XG4gIHZhbHVlOiBUO1xufVxuXG4vKiogQm91bmRzIGZvciB0aGUgcHJvY2Vzcy1sb2NhbCBjYWNoZSBsYXllcmVkIG92ZXIgcGVyc2lzdGVudCBBcHBLaXQgc3RvcmFnZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTDFDYWNoZVN0b3JhZ2VPcHRpb25zIHtcbiAgbWF4Qnl0ZXM/OiBudW1iZXI7XG4gIG1heEVudHJpZXM/OiBudW1iZXI7XG59XG5cbi8qKiBBcHBLaXQncyBpbnRlcm5hbCBwZXJzaXN0ZW50IHN0b3JhZ2Ugc3VyZmFjZS4gKi9cbmV4cG9ydCB0eXBlIFBlcnNpc3RlbnRTdG9yYWdlQmFzZSA9IENhY2hlU3RvcmFnZSAmIHtcbiAgY2xlYW51cEV4cGlyZWQ/KCk6IFByb21pc2U8bnVtYmVyPjtcbiAgaW5pdGlhbGl6ZSgpOiBQcm9taXNlPHZvaWQ+O1xuICBpbml0aWFsaXplZDogYm9vbGVhbjtcbiAgc2NoZW1hTmFtZT86IHN0cmluZztcbiAgdGFibGVOYW1lPzogc3RyaW5nO1xufTtcblxudHlwZSBQZXJzaXN0ZW50U3RvcmFnZUNvbnN0cnVjdG9yID0gbmV3IChcbiAgY29uZmlnOiBDYWNoZUNvbmZpZyxcbiAgcG9vbDogTGFrZWJhc2VQb29sLFxuKSA9PiBQZXJzaXN0ZW50U3RvcmFnZUJhc2U7XG5cbmxldCBwZXJzaXN0ZW50U3RvcmFnZUN0b3I6IFBlcnNpc3RlbnRTdG9yYWdlQ29uc3RydWN0b3IgfCB1bmRlZmluZWQgfCBudWxsID0gbnVsbDtcblxuLyoqIFByb2Nlc3MtbG9jYWwgTFJVIHRoYXQgYXZvaWRzIGEgcGVyc2lzdGVudCBjYWNoZSBxdWVyeSBvbiBldmVyeSB3YXJtIGhpdC4gKi9cbmV4cG9ydCBjbGFzcyBMMUNhY2hlU3RvcmFnZSBpbXBsZW1lbnRzIENhY2hlU3RvcmFnZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgZW50cmllczogTFJVQ2FjaGU8c3RyaW5nLCBDYWNoZUVudHJ5PjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IHN0b3JhZ2U6IENhY2hlU3RvcmFnZSxcbiAgICBvcHRpb25zOiBMMUNhY2hlU3RvcmFnZU9wdGlvbnMgPSB7fSxcbiAgKSB7XG4gICAgdGhpcy5lbnRyaWVzID0gbmV3IExSVUNhY2hlKHtcbiAgICAgIG1heDogcG9zaXRpdmVCb3VuZChvcHRpb25zLm1heEVudHJpZXMsIDFfMDAwLCBcIm1heEVudHJpZXNcIiksXG4gICAgICBtYXhTaXplOiBwb3NpdGl2ZUJvdW5kKG9wdGlvbnMubWF4Qnl0ZXMsIDY0ICogMTAyNCAqIDEwMjQsIFwibWF4Qnl0ZXNcIiksXG4gICAgICBzaXplQ2FsY3VsYXRpb246IChlbnRyeSwga2V5KSA9PiByZXRhaW5lZFNpemUoa2V5LCBlbnRyeSksXG4gICAgfSk7XG4gIH1cblxuICBhc3luYyBnZXQ8VD4oa2V5OiBzdHJpbmcpOiBQcm9taXNlPENhY2hlRW50cnk8VD4gfCBudWxsPiB7XG4gICAgY29uc3QgbG9jYWwgPSB0aGlzLmVudHJpZXMuZ2V0KGtleSk7XG4gICAgaWYgKGxvY2FsKSByZXR1cm4gbG9jYWwgYXMgQ2FjaGVFbnRyeTxUPjtcbiAgICBjb25zdCBwZXJzaXN0ZWQgPSBhd2FpdCB0aGlzLnN0b3JhZ2UuZ2V0PFQ+KGtleSk7XG4gICAgaWYgKHBlcnNpc3RlZCAmJiBwZXJzaXN0ZWQuZXhwaXJ5ID4gRGF0ZS5ub3coKSkgdGhpcy5yZXRhaW4oa2V5LCBwZXJzaXN0ZWQpO1xuICAgIHJldHVybiBwZXJzaXN0ZWQ7XG4gIH1cblxuICBhc3luYyBzZXQ8VD4oa2V5OiBzdHJpbmcsIGVudHJ5OiBDYWNoZUVudHJ5PFQ+KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5zdG9yYWdlLnNldChrZXksIGVudHJ5KTtcbiAgICB0aGlzLnJldGFpbihrZXksIGVudHJ5KTtcbiAgfVxuXG4gIGFzeW5jIGRlbGV0ZShrZXk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc3RvcmFnZS5kZWxldGUoa2V5KTtcbiAgICB0aGlzLmVudHJpZXMuZGVsZXRlKGtleSk7XG4gIH1cblxuICBhc3luYyBjbGVhcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnN0b3JhZ2UuY2xlYXIoKTtcbiAgICB0aGlzLmVudHJpZXMuY2xlYXIoKTtcbiAgfVxuXG4gIGFzeW5jIGhhcyhrZXk6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGlmICh0aGlzLmVudHJpZXMuaGFzKGtleSkpIHJldHVybiB0cnVlO1xuICAgIHJldHVybiB0aGlzLnN0b3JhZ2UuaGFzKGtleSk7XG4gIH1cblxuICBzaXplKCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmFnZS5zaXplKCk7XG4gIH1cblxuICBpc1BlcnNpc3RlbnQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmFnZS5pc1BlcnNpc3RlbnQoKTtcbiAgfVxuXG4gIGhlYWx0aENoZWNrKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLnN0b3JhZ2UuaGVhbHRoQ2hlY2soKTtcbiAgfVxuXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuZW50cmllcy5jbGVhcigpO1xuICAgIGF3YWl0IHRoaXMuc3RvcmFnZS5jbG9zZSgpO1xuICB9XG5cbiAgLyoqIEZvcndhcmQgQXBwS2l0J3MgcnVudGltZS1vbmx5IHBlcnNpc3RlbnQgY2xlYW51cCBob29rIHdoZW4gYXZhaWxhYmxlLiAqL1xuICBhc3luYyBjbGVhbnVwRXhwaXJlZCgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGNvbnN0IGNsZWFudXAgPSAoXG4gICAgICB0aGlzLnN0b3JhZ2UgYXMgQ2FjaGVTdG9yYWdlICYge1xuICAgICAgICBjbGVhbnVwRXhwaXJlZD86ICgpID0+IFByb21pc2U8bnVtYmVyPjtcbiAgICAgIH1cbiAgICApLmNsZWFudXBFeHBpcmVkO1xuICAgIHJldHVybiBjbGVhbnVwID8gY2xlYW51cC5jYWxsKHRoaXMuc3RvcmFnZSkgOiAwO1xuICB9XG5cbiAgcHJpdmF0ZSByZXRhaW48VD4oa2V5OiBzdHJpbmcsIGVudHJ5OiBDYWNoZUVudHJ5PFQ+KTogdm9pZCB7XG4gICAgY29uc3QgdHRsID0gZW50cnkuZXhwaXJ5IC0gRGF0ZS5ub3coKTtcbiAgICBpZiAodHRsID4gMCkgdGhpcy5lbnRyaWVzLnNldChrZXksIGVudHJ5LCB7IHR0bCB9KTtcbiAgfVxufVxuXG5mdW5jdGlvbiBwb3NpdGl2ZUJvdW5kKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQsIGZhbGxiYWNrOiBudW1iZXIsIG5hbWU6IHN0cmluZyk6IG51bWJlciB7XG4gIGNvbnN0IHJlc29sdmVkID0gdmFsdWUgPz8gZmFsbGJhY2s7XG4gIGlmICghTnVtYmVyLmlzRmluaXRlKHJlc29sdmVkKSB8fCByZXNvbHZlZCA8PSAwKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoYCR7bmFtZX0gbXVzdCBiZSBhIGZpbml0ZSBwb3NpdGl2ZSBudW1iZXJgKTtcbiAgfVxuICByZXR1cm4gTWF0aC5mbG9vcihyZXNvbHZlZCk7XG59XG5cbmZ1bmN0aW9uIHJldGFpbmVkU2l6ZShrZXk6IHN0cmluZywgZW50cnk6IENhY2hlRW50cnkpOiBudW1iZXIge1xuICB0cnkge1xuICAgIHJldHVybiBCdWZmZXIuYnl0ZUxlbmd0aChrZXkpICsgQnVmZmVyLmJ5dGVMZW5ndGgoSlNPTi5zdHJpbmdpZnkoZW50cnkpKTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIE51bWJlci5NQVhfU0FGRV9JTlRFR0VSO1xuICB9XG59XG5cbi8qKlxuICogTGF6aWx5IHJlc29sdmUgQXBwS2l0J3MgaW50ZXJuYWwgYFBlcnNpc3RlbnRTdG9yYWdlYCBjb25zdHJ1Y3Rvci4gYG51bGxgXG4gKiBtZWFucyBub3QgYXR0ZW1wdGVkIHlldDsgYHVuZGVmaW5lZGAgbWVhbnMgdGhlIGxvb2t1cCBhbHJlYWR5IGZhaWxlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxvYWRQZXJzaXN0ZW50U3RvcmFnZSgpOiBQZXJzaXN0ZW50U3RvcmFnZUNvbnN0cnVjdG9yIHwgdW5kZWZpbmVkIHtcbiAgaWYgKHBlcnNpc3RlbnRTdG9yYWdlQ3RvciAhPT0gbnVsbCkge1xuICAgIHJldHVybiBwZXJzaXN0ZW50U3RvcmFnZUN0b3I7XG4gIH1cbiAgdHJ5IHtcbiAgICBjb25zdCByZXF1aXJlID0gY3JlYXRlUmVxdWlyZShpbXBvcnQubWV0YS51cmwpO1xuICAgIGNvbnN0IG1vZHVsZVBhdGggPSBqb2luKFxuICAgICAgZGlybmFtZShyZXF1aXJlLnJlc29sdmUoXCJAZGF0YWJyaWNrcy9hcHBraXRcIikpLFxuICAgICAgXCJjYWNoZS9zdG9yYWdlL3BlcnNpc3RlbnQuanNcIixcbiAgICApO1xuICAgIGNvbnN0IGxvYWRlZCA9IHJlcXVpcmUobW9kdWxlUGF0aCkuUGVyc2lzdGVudFN0b3JhZ2UgYXNcbiAgICAgIFBlcnNpc3RlbnRTdG9yYWdlQ29uc3RydWN0b3IgfCB1bmRlZmluZWQ7XG4gICAgaWYgKHR5cGVvZiBsb2FkZWQgIT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgbG9nZ2VyLmRlYnVnKFwic29mdCBwZXJzaXN0ZW50IGNhY2hlIHNraXBwZWQgKFBlcnNpc3RlbnRTdG9yYWdlIG1pc3NpbmcpXCIpO1xuICAgICAgcGVyc2lzdGVudFN0b3JhZ2VDdG9yID0gdW5kZWZpbmVkO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgcGVyc2lzdGVudFN0b3JhZ2VDdG9yID0gbG9hZGVkO1xuICAgIHJldHVybiBsb2FkZWQ7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGxvZ2dlci5kZWJ1ZyhcInNvZnQgcGVyc2lzdGVudCBjYWNoZSBza2lwcGVkIChQZXJzaXN0ZW50U3RvcmFnZSB1bmF2YWlsYWJsZSlcIiwge1xuICAgICAgZXJyb3I6IGVycm9yVXRpbHMuZXJyb3JNZXNzYWdlKGVyciksXG4gICAgfSk7XG4gICAgcGVyc2lzdGVudFN0b3JhZ2VDdG9yID0gdW5kZWZpbmVkO1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuLyoqXG4gKiBXaGV0aGVyIGFuIGV4aXN0aW5nIGNhY2hlIHRhYmxlIGJlbG9uZ3MgdG8gYW5vdGhlciByb2xlLlxuICpcbiAqIFBvc3RncmVTUUwgYWxsb3dzIGdyYW50ZWQgcmVhZHMvd3JpdGVzIGJ1dCByZXNlcnZlcyBpbmRleCBEREwgZm9yIHRoZSB0YWJsZVxuICogb3duZXIuIFNraXAgQXBwS2l0IG1pZ3JhdGlvbnMgaW4gdGhhdCBjYXNlOyB7QGxpbmsgcHJvYmVTdG9yYWdlfSBzdGlsbCBwcm92ZXNcbiAqIHRoZSBleGlzdGluZyB0YWJsZSBjYW4gc2VydmUgdGhlIGNhY2hlIGJlZm9yZSBpdCBpcyBhY2NlcHRlZC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdGFibGVPd25lZEJ5QW5vdGhlclJvbGUoXG4gIHN0b3JhZ2U6IFBlcnNpc3RlbnRTdG9yYWdlQmFzZSxcbiAgcG9vbDogTGFrZWJhc2VQb29sLFxuKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGlmICghc3RvcmFnZS5zY2hlbWFOYW1lIHx8ICFzdG9yYWdlLnRhYmxlTmFtZSkgcmV0dXJuIGZhbHNlO1xuICBjb25zdCByZXN1bHQgPSBhd2FpdCBwb29sLnF1ZXJ5KFxuICAgIGBTRUxFQ1QgcGdfZ2V0X3VzZXJieWlkKGMucmVsb3duZXIpID0gY3VycmVudF91c2VyIEFTIG93bmVkXG4gICAgIEZST00gcGdfY2xhc3MgY1xuICAgICBKT0lOIHBnX25hbWVzcGFjZSBuIE9OIG4ub2lkID0gYy5yZWxuYW1lc3BhY2VcbiAgICAgV0hFUkUgbi5uc3BuYW1lID0gJDEgQU5EIGMucmVsbmFtZSA9ICQyIEFORCBjLnJlbGtpbmQgSU4gKCdyJywgJ3AnKWAsXG4gICAgW3N0b3JhZ2Uuc2NoZW1hTmFtZSwgc3RvcmFnZS50YWJsZU5hbWVdLFxuICApO1xuICBpZiAocmVzdWx0LnJvd3NbMF0/Lm93bmVkICE9PSBmYWxzZSkgcmV0dXJuIGZhbHNlO1xuICBsb2dnZXIud2FybihcInBlcnNpc3RlbnQgY2FjaGUgbWlncmF0aW9ucyBza2lwcGVkICh0YWJsZSBvd25lZCBieSBhbm90aGVyIHJvbGUpXCIsIHtcbiAgICBzY2hlbWE6IHN0b3JhZ2Uuc2NoZW1hTmFtZSxcbiAgICB0YWJsZTogc3RvcmFnZS50YWJsZU5hbWUsXG4gIH0pO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqIFNvZnRlbiBgaW5pdGlhbGl6ZSgpYCBzbyBhbiBvd25lcnNoaXAtb25seSBtaWdyYXRpb24gZmFpbHVyZSBpcyBub3QgZmF0YWwuICovXG5leHBvcnQgZnVuY3Rpb24gc29mdGVuSW5pdGlhbGl6ZShcbiAgc3RvcmFnZTogUGVyc2lzdGVudFN0b3JhZ2VCYXNlLFxuICBza2lwTWlncmF0aW9ucz86ICgpID0+IFByb21pc2U8Ym9vbGVhbj4sXG4pOiB2b2lkIHtcbiAgY29uc3Qgb3JpZ2luYWxJbml0aWFsaXplID0gc3RvcmFnZS5pbml0aWFsaXplLmJpbmQoc3RvcmFnZSk7XG4gIGxldCBzb2Z0SW5pdFByb21pc2U6IFByb21pc2U8dm9pZD4gfCB1bmRlZmluZWQ7XG5cbiAgc3RvcmFnZS5pbml0aWFsaXplID0gYXN5bmMgKCkgPT4ge1xuICAgIGlmIChzdG9yYWdlLmluaXRpYWxpemVkKSByZXR1cm47XG4gICAgc29mdEluaXRQcm9taXNlID8/PSAoYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKCEoYXdhaXQgc2tpcE1pZ3JhdGlvbnM/LigpKSkgYXdhaXQgb3JpZ2luYWxJbml0aWFsaXplKCk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICFoYW5kbGVPd25lcnNoaXBNaWdyYXRpb25FcnJvcihlcnIsIHtcbiAgICAgICAgICAgIHNjb3BlOiBcImNhY2hlLXN0b3JhZ2VcIixcbiAgICAgICAgICAgIGxvZ2dlcixcbiAgICAgICAgICAgIGV2ZW50OiBcInBlcnNpc3RlbnQgY2FjaGUgbWlncmF0aW9uIGZhaWxlZFwiLFxuICAgICAgICAgIH0pXG4gICAgICAgICkge1xuICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pKCk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHNvZnRJbml0UHJvbWlzZTtcbiAgICAgIHN0b3JhZ2UuaW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgc29mdEluaXRQcm9taXNlID0gdW5kZWZpbmVkO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgfTtcbn1cblxuLyoqIFZlcmlmeSB0aGUgbWlncmF0ZWQgY2FjaGUgdGFibGUgY2FuIHNlcnZlIHRoZSByZWFkcyBhbmQgd3JpdGVzIEFwcEtpdCBuZWVkcy4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwcm9iZVN0b3JhZ2Uoc3RvcmFnZTogUGVyc2lzdGVudFN0b3JhZ2VCYXNlKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IGtleSA9IGBkYngtdG9vbHM6Y2FjaGUtcHJvYmU6JHtoYXNoLmlkKCl9YDtcbiAgdHJ5IHtcbiAgICBhd2FpdCBzdG9yYWdlLnNldChrZXksIHsgdmFsdWU6IGtleSwgZXhwaXJ5OiBEYXRlLm5vdygpICsgNjBfMDAwIH0pO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHN0b3JhZ2UuZ2V0KGtleSk7XG4gICAgaWYgKHJlc3VsdD8udmFsdWUgIT09IGtleSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwicGVyc2lzdGVudCBjYWNoZSBwcm9iZSByZXR1cm5lZCBhbiB1bmV4cGVjdGVkIHZhbHVlXCIpO1xuICAgIH1cbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCBzdG9yYWdlLmRlbGV0ZShrZXkpLmNhdGNoKCgpID0+IHVuZGVmaW5lZCk7XG4gIH1cbn1cblxuLyoqXG4gKiBCdWlsZCBhIHNvZnQtZmFpbCBMYWtlYmFzZSBjYWNoZSBzdG9yYWdlIHdoZW4gYSBwb29sIGNhbiBiZSBjcmVhdGVkIGFuZFxuICogQXBwS2l0J3MgUGVyc2lzdGVudFN0b3JhZ2UgY2FuIGJlIGxvYWRlZC4gUmV0dXJucyBgdW5kZWZpbmVkYCBvdGhlcndpc2Ugc29cbiAqIEFwcEtpdCBjYW4gZmFsbCB0aHJvdWdoIHRvIGl0cyBub3JtYWwgY2FjaGUgcGF0aC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVNvZnRQZXJzaXN0ZW50U3RvcmFnZShcbiAgY2FjaGU6IENhY2hlQ29uZmlnIHwgdW5kZWZpbmVkLFxuKTogUHJvbWlzZTxDYWNoZVN0b3JhZ2UgfCB1bmRlZmluZWQ+IHtcbiAgY29uc3QgUGVyc2lzdGVudFN0b3JhZ2UgPSBsb2FkUGVyc2lzdGVudFN0b3JhZ2UoKTtcbiAgaWYgKCFQZXJzaXN0ZW50U3RvcmFnZSkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICBsZXQgcG9vbDogTGFrZWJhc2VQb29sIHwgdW5kZWZpbmVkO1xuICB0cnkge1xuICAgIGNvbnN0IGNyZWF0ZWRQb29sID0gY3JlYXRlTGFrZWJhc2VQb29sKFxuICAgICAgcG9zdGdyZXNDb25uZWN0aW9uT3B0aW9ucyh7IHdvcmtzcGFjZUNsaWVudDogZ2V0V29ya3NwYWNlQ2xpZW50KHt9KSB9KSxcbiAgICApO1xuICAgIHBvb2wgPSBjcmVhdGVkUG9vbDtcbiAgICBjb25zdCBzdG9yYWdlID0gbmV3IFBlcnNpc3RlbnRTdG9yYWdlKGNhY2hlID8/IHt9LCBjcmVhdGVkUG9vbCk7XG4gICAgc29mdGVuSW5pdGlhbGl6ZShzdG9yYWdlLCAoKSA9PiB0YWJsZU93bmVkQnlBbm90aGVyUm9sZShzdG9yYWdlLCBjcmVhdGVkUG9vbCkpO1xuICAgIGlmICghKGF3YWl0IHN0b3JhZ2UuaGVhbHRoQ2hlY2soKSkpIHtcbiAgICAgIGF3YWl0IHN0b3JhZ2UuY2xvc2UoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBhd2FpdCBzdG9yYWdlLmluaXRpYWxpemUoKTtcbiAgICBhd2FpdCBwcm9iZVN0b3JhZ2Uoc3RvcmFnZSk7XG4gICAgcmV0dXJuIG5ldyBMMUNhY2hlU3RvcmFnZShzdG9yYWdlLCB7XG4gICAgICBtYXhCeXRlczogY2FjaGU/Lm1heEJ5dGVzLFxuICAgICAgbWF4RW50cmllczogY2FjaGU/Lm1heFNpemUsXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGxvZ2dlci5kZWJ1ZyhcInNvZnQgcGVyc2lzdGVudCBjYWNoZSB1bmF2YWlsYWJsZVwiLCB7XG4gICAgICBlcnJvcjogZXJyb3JVdGlscy5lcnJvck1lc3NhZ2UoZXJyKSxcbiAgICB9KTtcbiAgICBpZiAocG9vbCkge1xuICAgICAgYXdhaXQgcG9vbC5lbmQoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgfVxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cbiJdfQ==
|