@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.
@@ -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==