@dbx-tools/appkit 0.9.93 → 0.9.95

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 CHANGED
@@ -20,6 +20,8 @@ without taking on a heavier feature package.
20
20
  `Context` values.
21
21
  - Lakebase cache-schema provisioning for deployments where the app identity must
22
22
  be granted access before persistent cache initialization.
23
+ - AppKit-global filesystem caching with user-isolated bounded L1 state,
24
+ cross-user invalidation, and stable framework filesystem sources.
23
25
  - An interceptor context on `createApp` (`interceptor?: Interceptor | Interceptor[]`)
24
26
  that hands add-ons the computed env, AppKit lifecycle hooks (`onLifecycle`, using
25
27
  AppKit's own `setup:complete` / `server:ready` / `shutdown` vocabulary).
@@ -136,6 +138,36 @@ The injected persistent storage includes a bounded process-local L1 powered by
136
138
  expiry, deletion, and clear operations remain coherent with the persistent
137
139
  store.
138
140
 
141
+ ## Filesystem Cache Plugin
142
+
143
+ Register `filesCache()` when provider-backed filesystems should share one
144
+ AppKit-lifecycle cache:
145
+
146
+ ```ts
147
+ import { filesCache } from "@dbx-tools/appkit/files-cache";
148
+ import { server } from "@databricks/appkit";
149
+ import { appkit } from "@dbx-tools/appkit";
150
+
151
+ await appkit.createApp({
152
+ plugins: [server(), filesCache()],
153
+ });
154
+ ```
155
+
156
+ The plugin exposes `forScope({ host, workspaceId?, userKey, ttlMs? })`, which
157
+ returns the `FileSystemCache` consumed by `@dbx-tools/shared-fs` `fs.cache()`.
158
+ Values are isolated by host, optional workspace id, and attributed user. A
159
+ batched invalidation clears matching keys across active user scopes without
160
+ sharing authorization-sensitive values.
161
+
162
+ `forFileSystem(scope, { paths }, load)` retains one framework filesystem source
163
+ for the same user and actual mounted paths. This gives frameworks such as Mastra
164
+ a stable source identity across page refreshes and turns without introducing a
165
+ second parsed catalogue cache.
166
+
167
+ Values and retained filesystem sources use a bounded process-local `lru-cache`.
168
+ Nothing is written to Lakebase or another persistent store. App restarts and
169
+ replica changes begin with a cold filesystem cache.
170
+
139
171
  `lakebaseResolver.resolveLakebaseConnection()` accepts:
140
172
 
141
173
  | Option | Type | Default | Description |
package/index.ts CHANGED
@@ -3,12 +3,13 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/appkit";
6
- export const PACKAGE_VERSION = "0.9.93";
6
+ export const PACKAGE_VERSION = "0.9.95";
7
7
  export * as appkit from "./src/appkit.ts";
8
8
  export * as brandContext from "./src/brand-context.ts";
9
9
  export * as bundle from "./src/bundle.ts";
10
10
  export * as childProcess from "./src/child-process.ts";
11
11
  export * as databricks from "./src/databricks.ts";
12
+ export * as filesCache from "./src/files-cache.ts";
12
13
  export * as identity from "./src/identity.ts";
13
14
  export * as interceptor from "./src/interceptor.ts";
14
15
  export * as lakebaseResolver from "./src/lakebase-resolver.ts";
@@ -26,6 +27,8 @@ export type { BundleValidateJson, ConfigFile, ConfigMapValue, ConfigSource, Reso
26
27
  export { AppKitChildProcess } from "./src/child-process.ts";
27
28
  export type { AppKitSpawnArgs, AppKitChildProcessHealthCheckContext, AppKitChildProcessHealthCheck, AppKitChildProcessOptions, AppKitChildProcessRunOptions } from "./src/child-process.ts";
28
29
  export type { ContextLike } from "./src/databricks.ts";
30
+ export { FilesCacheManager, FilesCachePlugin } from "./src/files-cache.ts";
31
+ export type { FilesCachePluginConfig, FilesCacheScope, FilesCacheSourceIdentity, FilesCacheExports } from "./src/files-cache.ts";
29
32
  export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER, IDENTITY_MODES, DEFAULT_IDENTITY_MODE } from "./src/identity.ts";
30
33
  export type { IdentityMode, HeaderBearing } from "./src/identity.ts";
31
34
  export { LifecycleBridgePlugin, lifecycleBridge } from "./src/interceptor.ts";
package/lib/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export declare const PACKAGE_IDENTIFIER = "@dbx-tools/appkit";
2
- export declare const PACKAGE_VERSION = "0.9.93";
2
+ export declare const PACKAGE_VERSION = "0.9.95";
3
3
  export * as appkit from "./src/appkit.ts";
4
4
  export * as brandContext from "./src/brand-context.ts";
5
5
  export * as bundle from "./src/bundle.ts";
6
6
  export * as childProcess from "./src/child-process.ts";
7
7
  export * as databricks from "./src/databricks.ts";
8
+ export * as filesCache from "./src/files-cache.ts";
8
9
  export * as identity from "./src/identity.ts";
9
10
  export * as interceptor from "./src/interceptor.ts";
10
11
  export * as lakebaseResolver from "./src/lakebase-resolver.ts";
@@ -22,6 +23,8 @@ export type { BundleValidateJson, ConfigFile, ConfigMapValue, ConfigSource, Reso
22
23
  export { AppKitChildProcess } from "./src/child-process.ts";
23
24
  export type { AppKitSpawnArgs, AppKitChildProcessHealthCheckContext, AppKitChildProcessHealthCheck, AppKitChildProcessOptions, AppKitChildProcessRunOptions } from "./src/child-process.ts";
24
25
  export type { ContextLike } from "./src/databricks.ts";
26
+ export { FilesCacheManager, FilesCachePlugin } from "./src/files-cache.ts";
27
+ export type { FilesCachePluginConfig, FilesCacheScope, FilesCacheSourceIdentity, FilesCacheExports } from "./src/files-cache.ts";
25
28
  export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER, IDENTITY_MODES, DEFAULT_IDENTITY_MODE } from "./src/identity.ts";
26
29
  export type { IdentityMode, HeaderBearing } from "./src/identity.ts";
27
30
  export { LifecycleBridgePlugin, lifecycleBridge } from "./src/interceptor.ts";
package/lib/index.js CHANGED
@@ -2,12 +2,13 @@
2
2
  // Regenerated from the exporting modules in ./src.
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
  export const PACKAGE_IDENTIFIER = "@dbx-tools/appkit";
5
- export const PACKAGE_VERSION = "0.9.93";
5
+ export const PACKAGE_VERSION = "0.9.95";
6
6
  export * as appkit from "./src/appkit.js";
7
7
  export * as brandContext from "./src/brand-context.js";
8
8
  export * as bundle from "./src/bundle.js";
9
9
  export * as childProcess from "./src/child-process.js";
10
10
  export * as databricks from "./src/databricks.js";
11
+ export * as filesCache from "./src/files-cache.js";
11
12
  export * as identity from "./src/identity.js";
12
13
  export * as interceptor from "./src/interceptor.js";
13
14
  export * as lakebaseResolver from "./src/lakebase-resolver.js";
@@ -21,8 +22,9 @@ export * as toolProvider from "./src/tool-provider.js";
21
22
  export * as toolkitEntries from "./src/toolkit-entries.js";
22
23
  export { bundleAppResourceSchema, flattenAppYamlEnv, flattenAppEnv, getBundlePath } from "./src/bundle.js";
23
24
  export { AppKitChildProcess } from "./src/child-process.js";
25
+ export { FilesCacheManager, FilesCachePlugin } from "./src/files-cache.js";
24
26
  export { ACCESS_TOKEN_HEADER, USER_ID_HEADER, USER_EMAIL_HEADER, IDENTITY_MODES, DEFAULT_IDENTITY_MODE } from "./src/identity.js";
25
27
  export { LifecycleBridgePlugin, lifecycleBridge } from "./src/interceptor.js";
26
28
  export { SSL_MODES } from "./src/pgaddress.js";
27
29
  export { ToolRegistryPlugin } from "./src/tool-provider.js";
28
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztBQUN0RCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsUUFBUSxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLFlBQVksTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sS0FBSyxZQUFZLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFVBQVUsTUFBTSxxQkFBcUIsQ0FBQztBQUNsRCxPQUFPLEtBQUssUUFBUSxNQUFNLG1CQUFtQixDQUFDO0FBQzlDLE9BQU8sS0FBSyxXQUFXLE1BQU0sc0JBQXNCLENBQUM7QUFDcEQsT0FBTyxLQUFLLGdCQUFnQixNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sS0FBSyxTQUFTLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsT0FBTyxLQUFLLFNBQVMsTUFBTSxvQkFBb0IsQ0FBQztBQUNoRCxPQUFPLEtBQUssZUFBZSxNQUFNLDJCQUEyQixDQUFDO0FBQzdELE9BQU8sS0FBSyxjQUFjLE1BQU0sMEJBQTBCLENBQUM7QUFDM0QsT0FBTyxLQUFLLFNBQVMsTUFBTSxvQkFBb0IsQ0FBQztBQUNoRCxPQUFPLEtBQUssV0FBVyxNQUFNLHVCQUF1QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxZQUFZLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxLQUFLLGNBQWMsTUFBTSwwQkFBMEIsQ0FBQztBQUUzRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsaUJBQWlCLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRTNHLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBRzVELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsY0FBYyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFbEksT0FBTyxFQUFFLHFCQUFxQixFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBSTlFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUsvQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCBjb25zdCBQQUNLQUdFX0lERU5USUZJRVIgPSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG5leHBvcnQgY29uc3QgUEFDS0FHRV9WRVJTSU9OID0gXCIwLjkuOTNcIjtcbmV4cG9ydCAqIGFzIGFwcGtpdCBmcm9tIFwiLi9zcmMvYXBwa2l0LnRzXCI7XG5leHBvcnQgKiBhcyBicmFuZENvbnRleHQgZnJvbSBcIi4vc3JjL2JyYW5kLWNvbnRleHQudHNcIjtcbmV4cG9ydCAqIGFzIGJ1bmRsZSBmcm9tIFwiLi9zcmMvYnVuZGxlLnRzXCI7XG5leHBvcnQgKiBhcyBjaGlsZFByb2Nlc3MgZnJvbSBcIi4vc3JjL2NoaWxkLXByb2Nlc3MudHNcIjtcbmV4cG9ydCAqIGFzIGRhdGFicmlja3MgZnJvbSBcIi4vc3JjL2RhdGFicmlja3MudHNcIjtcbmV4cG9ydCAqIGFzIGlkZW50aXR5IGZyb20gXCIuL3NyYy9pZGVudGl0eS50c1wiO1xuZXhwb3J0ICogYXMgaW50ZXJjZXB0b3IgZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgKiBhcyBsYWtlYmFzZVJlc29sdmVyIGZyb20gXCIuL3NyYy9sYWtlYmFzZS1yZXNvbHZlci50c1wiO1xuZXhwb3J0ICogYXMgbWlncmF0aW9uIGZyb20gXCIuL3NyYy9taWdyYXRpb24udHNcIjtcbmV4cG9ydCAqIGFzIHBnYWRkcmVzcyBmcm9tIFwiLi9zcmMvcGdhZGRyZXNzLnRzXCI7XG5leHBvcnQgKiBhcyBwbHVnaW5FeGVjdXRpb24gZnJvbSBcIi4vc3JjL3BsdWdpbi1leGVjdXRpb24udHNcIjtcbmV4cG9ydCAqIGFzIHBsdWdpblJlZ2lzdHJ5IGZyb20gXCIuL3NyYy9wbHVnaW4tcmVnaXN0cnkudHNcIjtcbmV4cG9ydCAqIGFzIHByb3Zpc2lvbiBmcm9tIFwiLi9zcmMvcHJvdmlzaW9uLnRzXCI7XG5leHBvcnQgKiBhcyByZXF1ZXN0VGFncyBmcm9tIFwiLi9zcmMvcmVxdWVzdC10YWdzLnRzXCI7XG5leHBvcnQgKiBhcyB0b29sUHJvdmlkZXIgZnJvbSBcIi4vc3JjL3Rvb2wtcHJvdmlkZXIudHNcIjtcbmV4cG9ydCAqIGFzIHRvb2xraXRFbnRyaWVzIGZyb20gXCIuL3NyYy90b29sa2l0LWVudHJpZXMudHNcIjtcbmV4cG9ydCB0eXBlIHsgQXV0b0NvbmZpZ3VyZU1vZGUsIENyZWF0ZUFwcENvbmZpZywgRXhlY3V0aW9uQ29udGV4dExpa2UsIFdvcmtzcGFjZUNsaWVudExpa2UgfSBmcm9tIFwiLi9zcmMvYXBwa2l0LnRzXCI7XG5leHBvcnQgeyBidW5kbGVBcHBSZXNvdXJjZVNjaGVtYSwgZmxhdHRlbkFwcFlhbWxFbnYsIGZsYXR0ZW5BcHBFbnYsIGdldEJ1bmRsZVBhdGggfSBmcm9tIFwiLi9zcmMvYnVuZGxlLnRzXCI7XG5leHBvcnQgdHlwZSB7IEJ1bmRsZVZhbGlkYXRlSnNvbiwgQ29uZmlnRmlsZSwgQ29uZmlnTWFwVmFsdWUsIENvbmZpZ1NvdXJjZSwgUmVzb2x2ZUNvbmZpZ1ZhbHVlT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9idW5kbGUudHNcIjtcbmV4cG9ydCB7IEFwcEtpdENoaWxkUHJvY2VzcyB9IGZyb20gXCIuL3NyYy9jaGlsZC1wcm9jZXNzLnRzXCI7XG5leHBvcnQgdHlwZSB7IEFwcEtpdFNwYXduQXJncywgQXBwS2l0Q2hpbGRQcm9jZXNzSGVhbHRoQ2hlY2tDb250ZXh0LCBBcHBLaXRDaGlsZFByb2Nlc3NIZWFsdGhDaGVjaywgQXBwS2l0Q2hpbGRQcm9jZXNzT3B0aW9ucywgQXBwS2l0Q2hpbGRQcm9jZXNzUnVuT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9jaGlsZC1wcm9jZXNzLnRzXCI7XG5leHBvcnQgdHlwZSB7IENvbnRleHRMaWtlIH0gZnJvbSBcIi4vc3JjL2RhdGFicmlja3MudHNcIjtcbmV4cG9ydCB7IEFDQ0VTU19UT0tFTl9IRUFERVIsIFVTRVJfSURfSEVBREVSLCBVU0VSX0VNQUlMX0hFQURFUiwgSURFTlRJVFlfTU9ERVMsIERFRkFVTFRfSURFTlRJVFlfTU9ERSB9IGZyb20gXCIuL3NyYy9pZGVudGl0eS50c1wiO1xuZXhwb3J0IHR5cGUgeyBJZGVudGl0eU1vZGUsIEhlYWRlckJlYXJpbmcgfSBmcm9tIFwiLi9zcmMvaWRlbnRpdHkudHNcIjtcbmV4cG9ydCB7IExpZmVjeWNsZUJyaWRnZVBsdWdpbiwgbGlmZWN5Y2xlQnJpZGdlIH0gZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgdHlwZSB7IExpZmVjeWNsZUV2ZW50LCBMaWZlY3ljbGVIYW5kbGVyLCBSZXNvbHZlZEFwcEVudiwgSW50ZXJjZXB0b3JDb250ZXh0LCBJbnRlcmNlcHRvciwgSW50ZXJjZXB0b3JSdW50aW1lIH0gZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgdHlwZSB7IExha2ViYXNlUmVzb2x2ZXJJbnB1dHMsIExha2ViYXNlQ29ubmVjdGlvbiB9IGZyb20gXCIuL3NyYy9sYWtlYmFzZS1yZXNvbHZlci50c1wiO1xuZXhwb3J0IHR5cGUgeyBPd25lcnNoaXBNaWdyYXRpb25FcnJvck9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvbWlncmF0aW9uLnRzXCI7XG5leHBvcnQgeyBTU0xfTU9ERVMgfSBmcm9tIFwiLi9zcmMvcGdhZGRyZXNzLnRzXCI7XG5leHBvcnQgdHlwZSB7IFNzbE1vZGUsIFBhcnNlZEFkZHJlc3MsIExha2ViYXNlQ29ubmVjdGlvbklucHV0cyB9IGZyb20gXCIuL3NyYy9wZ2FkZHJlc3MudHNcIjtcbmV4cG9ydCB0eXBlIHsgUnVuUGx1Z2luRXhlY3V0aW9uT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9wbHVnaW4tZXhlY3V0aW9uLnRzXCI7XG5leHBvcnQgdHlwZSB7IFBsdWdpbkNvbnRleHRMaWtlIH0gZnJvbSBcIi4vc3JjL3BsdWdpbi1yZWdpc3RyeS50c1wiO1xuZXhwb3J0IHR5cGUgeyBSZXF1ZXN0VGFnVmFsdWUgfSBmcm9tIFwiLi9zcmMvcmVxdWVzdC10YWdzLnRzXCI7XG5leHBvcnQgeyBUb29sUmVnaXN0cnlQbHVnaW4gfSBmcm9tIFwiLi9zcmMvdG9vbC1wcm92aWRlci50c1wiO1xuZXhwb3J0IHR5cGUgeyBBZ2VudFRvb2xFeGVjdXRpb25Db250ZXh0IH0gZnJvbSBcIi4vc3JjL3Rvb2wtcHJvdmlkZXIudHNcIjtcbiJdfQ==
30
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztBQUN0RCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsUUFBUSxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLFlBQVksTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sS0FBSyxZQUFZLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFVBQVUsTUFBTSxxQkFBcUIsQ0FBQztBQUNsRCxPQUFPLEtBQUssVUFBVSxNQUFNLHNCQUFzQixDQUFDO0FBQ25ELE9BQU8sS0FBSyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxLQUFLLFdBQVcsTUFBTSxzQkFBc0IsQ0FBQztBQUNwRCxPQUFPLEtBQUssZ0JBQWdCLE1BQU0sNEJBQTRCLENBQUM7QUFDL0QsT0FBTyxLQUFLLFNBQVMsTUFBTSxvQkFBb0IsQ0FBQztBQUNoRCxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxlQUFlLE1BQU0sMkJBQTJCLENBQUM7QUFDN0QsT0FBTyxLQUFLLGNBQWMsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEtBQUssU0FBUyxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sS0FBSyxXQUFXLE1BQU0sdUJBQXVCLENBQUM7QUFDckQsT0FBTyxLQUFLLFlBQVksTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEtBQUssY0FBYyxNQUFNLDBCQUEwQixDQUFDO0FBRTNELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxpQkFBaUIsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFM0csT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFHNUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFM0UsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGNBQWMsRUFBRSxpQkFBaUIsRUFBRSxjQUFjLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUVsSSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFJOUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBSy9DLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9hcHBraXRcIjtcbmV4cG9ydCBjb25zdCBQQUNLQUdFX1ZFUlNJT04gPSBcIjAuOS45NVwiO1xuZXhwb3J0ICogYXMgYXBwa2l0IGZyb20gXCIuL3NyYy9hcHBraXQudHNcIjtcbmV4cG9ydCAqIGFzIGJyYW5kQ29udGV4dCBmcm9tIFwiLi9zcmMvYnJhbmQtY29udGV4dC50c1wiO1xuZXhwb3J0ICogYXMgYnVuZGxlIGZyb20gXCIuL3NyYy9idW5kbGUudHNcIjtcbmV4cG9ydCAqIGFzIGNoaWxkUHJvY2VzcyBmcm9tIFwiLi9zcmMvY2hpbGQtcHJvY2Vzcy50c1wiO1xuZXhwb3J0ICogYXMgZGF0YWJyaWNrcyBmcm9tIFwiLi9zcmMvZGF0YWJyaWNrcy50c1wiO1xuZXhwb3J0ICogYXMgZmlsZXNDYWNoZSBmcm9tIFwiLi9zcmMvZmlsZXMtY2FjaGUudHNcIjtcbmV4cG9ydCAqIGFzIGlkZW50aXR5IGZyb20gXCIuL3NyYy9pZGVudGl0eS50c1wiO1xuZXhwb3J0ICogYXMgaW50ZXJjZXB0b3IgZnJvbSBcIi4vc3JjL2ludGVyY2VwdG9yLnRzXCI7XG5leHBvcnQgKiBhcyBsYWtlYmFzZVJlc29sdmVyIGZyb20gXCIuL3NyYy9sYWtlYmFzZS1yZXNvbHZlci50c1wiO1xuZXhwb3J0ICogYXMgbWlncmF0aW9uIGZyb20gXCIuL3NyYy9taWdyYXRpb24udHNcIjtcbmV4cG9ydCAqIGFzIHBnYWRkcmVzcyBmcm9tIFwiLi9zcmMvcGdhZGRyZXNzLnRzXCI7XG5leHBvcnQgKiBhcyBwbHVnaW5FeGVjdXRpb24gZnJvbSBcIi4vc3JjL3BsdWdpbi1leGVjdXRpb24udHNcIjtcbmV4cG9ydCAqIGFzIHBsdWdpblJlZ2lzdHJ5IGZyb20gXCIuL3NyYy9wbHVnaW4tcmVnaXN0cnkudHNcIjtcbmV4cG9ydCAqIGFzIHByb3Zpc2lvbiBmcm9tIFwiLi9zcmMvcHJvdmlzaW9uLnRzXCI7XG5leHBvcnQgKiBhcyByZXF1ZXN0VGFncyBmcm9tIFwiLi9zcmMvcmVxdWVzdC10YWdzLnRzXCI7XG5leHBvcnQgKiBhcyB0b29sUHJvdmlkZXIgZnJvbSBcIi4vc3JjL3Rvb2wtcHJvdmlkZXIudHNcIjtcbmV4cG9ydCAqIGFzIHRvb2xraXRFbnRyaWVzIGZyb20gXCIuL3NyYy90b29sa2l0LWVudHJpZXMudHNcIjtcbmV4cG9ydCB0eXBlIHsgQXV0b0NvbmZpZ3VyZU1vZGUsIENyZWF0ZUFwcENvbmZpZywgRXhlY3V0aW9uQ29udGV4dExpa2UsIFdvcmtzcGFjZUNsaWVudExpa2UgfSBmcm9tIFwiLi9zcmMvYXBwa2l0LnRzXCI7XG5leHBvcnQgeyBidW5kbGVBcHBSZXNvdXJjZVNjaGVtYSwgZmxhdHRlbkFwcFlhbWxFbnYsIGZsYXR0ZW5BcHBFbnYsIGdldEJ1bmRsZVBhdGggfSBmcm9tIFwiLi9zcmMvYnVuZGxlLnRzXCI7XG5leHBvcnQgdHlwZSB7IEJ1bmRsZVZhbGlkYXRlSnNvbiwgQ29uZmlnRmlsZSwgQ29uZmlnTWFwVmFsdWUsIENvbmZpZ1NvdXJjZSwgUmVzb2x2ZUNvbmZpZ1ZhbHVlT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9idW5kbGUudHNcIjtcbmV4cG9ydCB7IEFwcEtpdENoaWxkUHJvY2VzcyB9IGZyb20gXCIuL3NyYy9jaGlsZC1wcm9jZXNzLnRzXCI7XG5leHBvcnQgdHlwZSB7IEFwcEtpdFNwYXduQXJncywgQXBwS2l0Q2hpbGRQcm9jZXNzSGVhbHRoQ2hlY2tDb250ZXh0LCBBcHBLaXRDaGlsZFByb2Nlc3NIZWFsdGhDaGVjaywgQXBwS2l0Q2hpbGRQcm9jZXNzT3B0aW9ucywgQXBwS2l0Q2hpbGRQcm9jZXNzUnVuT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9jaGlsZC1wcm9jZXNzLnRzXCI7XG5leHBvcnQgdHlwZSB7IENvbnRleHRMaWtlIH0gZnJvbSBcIi4vc3JjL2RhdGFicmlja3MudHNcIjtcbmV4cG9ydCB7IEZpbGVzQ2FjaGVNYW5hZ2VyLCBGaWxlc0NhY2hlUGx1Z2luIH0gZnJvbSBcIi4vc3JjL2ZpbGVzLWNhY2hlLnRzXCI7XG5leHBvcnQgdHlwZSB7IEZpbGVzQ2FjaGVQbHVnaW5Db25maWcsIEZpbGVzQ2FjaGVTY29wZSwgRmlsZXNDYWNoZVNvdXJjZUlkZW50aXR5LCBGaWxlc0NhY2hlRXhwb3J0cyB9IGZyb20gXCIuL3NyYy9maWxlcy1jYWNoZS50c1wiO1xuZXhwb3J0IHsgQUNDRVNTX1RPS0VOX0hFQURFUiwgVVNFUl9JRF9IRUFERVIsIFVTRVJfRU1BSUxfSEVBREVSLCBJREVOVElUWV9NT0RFUywgREVGQVVMVF9JREVOVElUWV9NT0RFIH0gZnJvbSBcIi4vc3JjL2lkZW50aXR5LnRzXCI7XG5leHBvcnQgdHlwZSB7IElkZW50aXR5TW9kZSwgSGVhZGVyQmVhcmluZyB9IGZyb20gXCIuL3NyYy9pZGVudGl0eS50c1wiO1xuZXhwb3J0IHsgTGlmZWN5Y2xlQnJpZGdlUGx1Z2luLCBsaWZlY3ljbGVCcmlkZ2UgfSBmcm9tIFwiLi9zcmMvaW50ZXJjZXB0b3IudHNcIjtcbmV4cG9ydCB0eXBlIHsgTGlmZWN5Y2xlRXZlbnQsIExpZmVjeWNsZUhhbmRsZXIsIFJlc29sdmVkQXBwRW52LCBJbnRlcmNlcHRvckNvbnRleHQsIEludGVyY2VwdG9yLCBJbnRlcmNlcHRvclJ1bnRpbWUgfSBmcm9tIFwiLi9zcmMvaW50ZXJjZXB0b3IudHNcIjtcbmV4cG9ydCB0eXBlIHsgTGFrZWJhc2VSZXNvbHZlcklucHV0cywgTGFrZWJhc2VDb25uZWN0aW9uIH0gZnJvbSBcIi4vc3JjL2xha2ViYXNlLXJlc29sdmVyLnRzXCI7XG5leHBvcnQgdHlwZSB7IE93bmVyc2hpcE1pZ3JhdGlvbkVycm9yT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9taWdyYXRpb24udHNcIjtcbmV4cG9ydCB7IFNTTF9NT0RFUyB9IGZyb20gXCIuL3NyYy9wZ2FkZHJlc3MudHNcIjtcbmV4cG9ydCB0eXBlIHsgU3NsTW9kZSwgUGFyc2VkQWRkcmVzcywgTGFrZWJhc2VDb25uZWN0aW9uSW5wdXRzIH0gZnJvbSBcIi4vc3JjL3BnYWRkcmVzcy50c1wiO1xuZXhwb3J0IHR5cGUgeyBSdW5QbHVnaW5FeGVjdXRpb25PcHRpb25zIH0gZnJvbSBcIi4vc3JjL3BsdWdpbi1leGVjdXRpb24udHNcIjtcbmV4cG9ydCB0eXBlIHsgUGx1Z2luQ29udGV4dExpa2UgfSBmcm9tIFwiLi9zcmMvcGx1Z2luLXJlZ2lzdHJ5LnRzXCI7XG5leHBvcnQgdHlwZSB7IFJlcXVlc3RUYWdWYWx1ZSB9IGZyb20gXCIuL3NyYy9yZXF1ZXN0LXRhZ3MudHNcIjtcbmV4cG9ydCB7IFRvb2xSZWdpc3RyeVBsdWdpbiB9IGZyb20gXCIuL3NyYy90b29sLXByb3ZpZGVyLnRzXCI7XG5leHBvcnQgdHlwZSB7IEFnZW50VG9vbEV4ZWN1dGlvbkNvbnRleHQgfSBmcm9tIFwiLi9zcmMvdG9vbC1wcm92aWRlci50c1wiO1xuIl19
@@ -27,6 +27,7 @@ export interface L1CacheStorageOptions {
27
27
  }
28
28
  /** AppKit's internal persistent storage surface. */
29
29
  export type PersistentStorageBase = CacheStorage & {
30
+ cleanupExpired?(): Promise<number>;
30
31
  initialize(): Promise<void>;
31
32
  initialized: boolean;
32
33
  schemaName?: string;
@@ -47,6 +48,8 @@ export declare class L1CacheStorage implements CacheStorage {
47
48
  isPersistent(): boolean;
48
49
  healthCheck(): Promise<boolean>;
49
50
  close(): Promise<void>;
51
+ /** Forward AppKit's runtime-only persistent cleanup hook when available. */
52
+ cleanupExpired(): Promise<number>;
50
53
  private retain;
51
54
  }
52
55
  /**
@@ -73,6 +73,11 @@ export class L1CacheStorage {
73
73
  this.entries.clear();
74
74
  await this.storage.close();
75
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
+ }
76
81
  retain(key, entry) {
77
82
  const ttl = entry.expiry - Date.now();
78
83
  if (ttl > 0)
@@ -227,4 +232,4 @@ export async function createSoftPersistentStorage(cache) {
227
232
  return undefined;
228
233
  }
229
234
  }
230
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiX2NhY2hlLXN0b3JhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvX2NhY2hlLXN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFFSCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzVDLE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzFDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxrQkFBa0IsRUFBb0IsTUFBTSxvQkFBb0IsQ0FBQztBQUM5RixPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3JDLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRS9ELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7QUE2QjNDLElBQUkscUJBQXFCLEdBQW9ELElBQUksQ0FBQztBQUVsRixnRkFBZ0Y7QUFDaEYsTUFBTSxPQUFPLGNBQWM7SUFJTjtJQUhGLE9BQU8sQ0FBK0I7SUFFdkQsWUFDbUIsT0FBcUIsRUFDdEMsVUFBaUMsRUFBRTtRQURsQixZQUFPLEdBQVAsT0FBTyxDQUFjO1FBR3RDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxRQUFRLENBQUM7WUFDMUIsR0FBRyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsVUFBVSxFQUFFLEtBQUssRUFBRSxZQUFZLENBQUM7WUFDM0QsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxFQUFFLFVBQVUsQ0FBQztZQUN0RSxlQUFlLEVB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LFFBQVE7WUFDekIsVUFBVSxFQUFFLEtBQUssRUFBRSxPQUFPO1NBQzNCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ2IsTUFBTSxDQUFDLEtBQUssQ0FBQyxtQ0FBbUMsRUFBRTtZQUNoRCxLQUFLLEVBQUUsVUFBVSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUM7U0FDcEMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUNULE1BQU0sSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsR0FBRSxDQUFDLENBQUMsQ0FBQztRQUNuQyxDQUFDO1FBQ0QsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNvZnQtZmFpbCBMYWtlYmFzZSBjYWNoZSBzdG9yYWdlIGZvciBBcHBLaXQncyBwZXJzaXN0ZW50IGNhY2hlLlxuICpcbiAqIEFwcEtpdCdzIGBQZXJzaXN0ZW50U3RvcmFnZS5pbml0aWFsaXplKClgIHJ1bnMgRERMIG1pZ3JhdGlvbnMgYW5kIHRocm93cyBvblxuICogYW55IHN0ZXAgZmFpbHVyZS4gV2l0aCBgY2FjaGUuc3RyaWN0UGVyc2lzdGVuY2U6IHRydWVgIHRoYXQgdGhyb3cgbWFrZXNcbiAqIGBDYWNoZU1hbmFnZXJgIHNpbGVudGx5IGRpc2FibGUgdGhlIGNhY2hlLiBUaGlzIHdyYXBzIHRoZSBzYW1lIHN0b3JhZ2UsXG4gKiBzdGlsbCBydW5zIGl0cyBtaWdyYXRpb25zLCBidXQgbG9ncyBhIGZhaWxlZCBzdGVwIGluc3RlYWQgb2YgdGhyb3dpbmcgc28gYVxuICogdXNhYmxlIHRhYmxlIGtlZXBzIHNlcnZpbmcuXG4gKlxuICogYFBlcnNpc3RlbnRTdG9yYWdlYCBpcyBub3Qgb24gQXBwS2l0J3MgcHVibGljIGV4cG9ydCBtYXAuIFRoZSBkZWVwIGxvYWQgaXNcbiAqIGxhenkgYW5kIGJlc3QtZWZmb3J0OiBpZiB0aGUgaW5zdGFsbGVkIEFwcEtpdCBsYXlvdXQgY2hhbmdlcywgdGhpcyByZXR1cm5zXG4gKiBgdW5kZWZpbmVkYCBhbmQgYGNyZWF0ZUFwcGAgbGVhdmVzIEFwcEtpdCBvbiBpdHMgZGVmYXVsdCBjYWNoZSBwYXRoLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSBcIm5vZGU6bW9kdWxlXCI7XG5pbXBvcnQgeyBkaXJuYW1lLCBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgY3JlYXRlTGFrZWJhc2VQb29sLCBnZXRXb3Jrc3BhY2VDbGllbnQsIHR5cGUgQ2FjaGVDb25maWcgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBwb3N0Z3Jlc0Nvbm5lY3Rpb25PcHRpb25zIH0gZnJvbSBcIkBkYngtdG9vbHMvcG9zdGdyZXNcIjtcbmltcG9ydCB7IGVycm9yVXRpbHMsIGhhc2gsIGxvZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBMUlVDYWNoZSB9IGZyb20gXCJscnUtY2FjaGVcIjtcbmltcG9ydCB7IGhhbmRsZU93bmVyc2hpcE1pZ3JhdGlvbkVycm9yIH0gZnJvbSBcIi4vbWlncmF0aW9uLnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJjYWNoZS1zdG9yYWdlXCIpO1xuXG50eXBlIExha2ViYXNlUG9vbCA9IFJldHVyblR5cGU8dHlwZW9mIGNyZWF0ZUxha2ViYXNlUG9vbD47XG50eXBlIENhY2hlU3RvcmFnZSA9IE5vbk51bGxhYmxlPENhY2hlQ29uZmlnW1wic3RvcmFnZVwiXT47XG5cbmludGVyZmFjZSBDYWNoZUVudHJ5PFQgPSB1bmtub3duPiB7XG4gIGV4cGlyeTogbnVtYmVyO1xuICB2YWx1ZTogVDtcbn1cblxuLyoqIEJvdW5kcyBmb3IgdGhlIHByb2Nlc3MtbG9jYWwgY2FjaGUgbGF5ZXJlZCBvdmVyIHBlcnNpc3RlbnQgQXBwS2l0IHN0b3JhZ2UuICovXG5leHBvcnQgaW50ZXJmYWNlIEwxQ2FjaGVTdG9yYWdlT3B0aW9ucyB7XG4gIG1heEJ5dGVzPzogbnVtYmVyO1xuICBtYXhFbnRyaWVzPzogbnVtYmVyO1xufVxuXG4vKiogQXBwS2l0J3MgaW50ZXJuYWwgcGVyc2lzdGVudCBzdG9yYWdlIHN1cmZhY2UuICovXG5leHBvcnQgdHlwZSBQZXJzaXN0ZW50U3RvcmFnZUJhc2UgPSBDYWNoZVN0b3JhZ2UgJiB7XG4gIGluaXRpYWxpemUoKTogUHJvbWlzZTx2b2lkPjtcbiAgaW5pdGlhbGl6ZWQ6IGJvb2xlYW47XG4gIHNjaGVtYU5hbWU/OiBzdHJpbmc7XG4gIHRhYmxlTmFtZT86IHN0cmluZztcbn07XG5cbnR5cGUgUGVyc2lzdGVudFN0b3JhZ2VDb25zdHJ1Y3RvciA9IG5ldyAoXG4gIGNvbmZpZzogQ2FjaGVDb25maWcsXG4gIHBvb2w6IExha2ViYXNlUG9vbCxcbikgPT4gUGVyc2lzdGVudFN0b3JhZ2VCYXNlO1xuXG5sZXQgcGVyc2lzdGVudFN0b3JhZ2VDdG9yOiBQZXJzaXN0ZW50U3RvcmFnZUNvbnN0cnVjdG9yIHwgdW5kZWZpbmVkIHwgbnVsbCA9IG51bGw7XG5cbi8qKiBQcm9jZXNzLWxvY2FsIExSVSB0aGF0IGF2b2lkcyBhIHBlcnNpc3RlbnQgY2FjaGUgcXVlcnkgb24gZXZlcnkgd2FybSBoaXQuICovXG5leHBvcnQgY2xhc3MgTDFDYWNoZVN0b3JhZ2UgaW1wbGVtZW50cyBDYWNoZVN0b3JhZ2Uge1xuICBwcml2YXRlIHJlYWRvbmx5IGVudHJpZXM6IExSVUNhY2hlPHN0cmluZywgQ2FjaGVFbnRyeT47XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBzdG9yYWdlOiBDYWNoZVN0b3JhZ2UsXG4gICAgb3B0aW9uczogTDFDYWNoZVN0b3JhZ2VPcHRpb25zID0ge30sXG4gICkge1xuICAgIHRoaXMuZW50cmllcyA9IG5ldyBMUlVDYWNoZSh7XG4gICAgICBtYXg6IHBvc2l0aXZlQm91bmQob3B0aW9ucy5tYXhFbnRyaWVzLCAxXzAwMCwgXCJtYXhFbnRyaWVzXCIpLFxuICAgICAgbWF4U2l6ZTogcG9zaXRpdmVCb3VuZChvcHRpb25zLm1heEJ5dGVzLCA2NCAqIDEwMjQgKiAxMDI0LCBcIm1heEJ5dGVzXCIpLFxuICAgICAgc2l6ZUNhbGN1bGF0aW9uOiAoZW50cnksIGtleSkgPT4gcmV0YWluZWRTaXplKGtleSwgZW50cnkpLFxuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgZ2V0PFQ+KGtleTogc3RyaW5nKTogUHJvbWlzZTxDYWNoZUVudHJ5PFQ+IHwgbnVsbD4ge1xuICAgIGNvbnN0IGxvY2FsID0gdGhpcy5lbnRyaWVzLmdldChrZXkpO1xuICAgIGlmIChsb2NhbCkgcmV0dXJuIGxvY2FsIGFzIENhY2hlRW50cnk8VD47XG4gICAgY29uc3QgcGVyc2lzdGVkID0gYXdhaXQgdGhpcy5zdG9yYWdlLmdldDxUPihrZXkpO1xuICAgIGlmIChwZXJzaXN0ZWQgJiYgcGVyc2lzdGVkLmV4cGlyeSA+IERhdGUubm93KCkpIHRoaXMucmV0YWluKGtleSwgcGVyc2lzdGVkKTtcbiAgICByZXR1cm4gcGVyc2lzdGVkO1xuICB9XG5cbiAgYXN5bmMgc2V0PFQ+KGtleTogc3RyaW5nLCBlbnRyeTogQ2FjaGVFbnRyeTxUPik6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc3RvcmFnZS5zZXQoa2V5LCBlbnRyeSk7XG4gICAgdGhpcy5yZXRhaW4oa2V5LCBlbnRyeSk7XG4gIH1cblxuICBhc3luYyBkZWxldGUoa2V5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnN0b3JhZ2UuZGVsZXRlKGtleSk7XG4gICAgdGhpcy5lbnRyaWVzLmRlbGV0ZShrZXkpO1xuICB9XG5cbiAgYXN5bmMgY2xlYXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5zdG9yYWdlLmNsZWFyKCk7XG4gICAgdGhpcy5lbnRyaWVzLmNsZWFyKCk7XG4gIH1cblxuICBhc3luYyBoYXMoa2V5OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBpZiAodGhpcy5lbnRyaWVzLmhhcyhrZXkpKSByZXR1cm4gdHJ1ZTtcbiAgICByZXR1cm4gdGhpcy5zdG9yYWdlLmhhcyhrZXkpO1xuICB9XG5cbiAgc2l6ZSgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aGlzLnN0b3JhZ2Uuc2l6ZSgpO1xuICB9XG5cbiAgaXNQZXJzaXN0ZW50KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnN0b3JhZ2UuaXNQZXJzaXN0ZW50KCk7XG4gIH1cblxuICBoZWFsdGhDaGVjaygpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICByZXR1cm4gdGhpcy5zdG9yYWdlLmhlYWx0aENoZWNrKCk7XG4gIH1cblxuICBhc3luYyBjbG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmVudHJpZXMuY2xlYXIoKTtcbiAgICBhd2FpdCB0aGlzLnN0b3JhZ2UuY2xvc2UoKTtcbiAgfVxuXG4gIHByaXZhdGUgcmV0YWluPFQ+KGtleTogc3RyaW5nLCBlbnRyeTogQ2FjaGVFbnRyeTxUPik6IHZvaWQge1xuICAgIGNvbnN0IHR0bCA9IGVudHJ5LmV4cGlyeSAtIERhdGUubm93KCk7XG4gICAgaWYgKHR0bCA+IDApIHRoaXMuZW50cmllcy5zZXQoa2V5LCBlbnRyeSwgeyB0dGwgfSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gcG9zaXRpdmVCb3VuZCh2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkLCBmYWxsYmFjazogbnVtYmVyLCBuYW1lOiBzdHJpbmcpOiBudW1iZXIge1xuICBjb25zdCByZXNvbHZlZCA9IHZhbHVlID8/IGZhbGxiYWNrO1xuICBpZiAoIU51bWJlci5pc0Zpbml0ZShyZXNvbHZlZCkgfHwgcmVzb2x2ZWQgPD0gMCkge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKGAke25hbWV9IG11c3QgYmUgYSBmaW5pdGUgcG9zaXRpdmUgbnVtYmVyYCk7XG4gIH1cbiAgcmV0dXJuIE1hdGguZmxvb3IocmVzb2x2ZWQpO1xufVxuXG5mdW5jdGlvbiByZXRhaW5lZFNpemUoa2V5OiBzdHJpbmcsIGVudHJ5OiBDYWNoZUVudHJ5KTogbnVtYmVyIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gQnVmZmVyLmJ5dGVMZW5ndGgoa2V5KSArIEJ1ZmZlci5ieXRlTGVuZ3RoKEpTT04uc3RyaW5naWZ5KGVudHJ5KSk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBOdW1iZXIuTUFYX1NBRkVfSU5URUdFUjtcbiAgfVxufVxuXG4vKipcbiAqIExhemlseSByZXNvbHZlIEFwcEtpdCdzIGludGVybmFsIGBQZXJzaXN0ZW50U3RvcmFnZWAgY29uc3RydWN0b3IuIGBudWxsYFxuICogbWVhbnMgbm90IGF0dGVtcHRlZCB5ZXQ7IGB1bmRlZmluZWRgIG1lYW5zIHRoZSBsb29rdXAgYWxyZWFkeSBmYWlsZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkUGVyc2lzdGVudFN0b3JhZ2UoKTogUGVyc2lzdGVudFN0b3JhZ2VDb25zdHJ1Y3RvciB8IHVuZGVmaW5lZCB7XG4gIGlmIChwZXJzaXN0ZW50U3RvcmFnZUN0b3IgIT09IG51bGwpIHtcbiAgICByZXR1cm4gcGVyc2lzdGVudFN0b3JhZ2VDdG9yO1xuICB9XG4gIHRyeSB7XG4gICAgY29uc3QgcmVxdWlyZSA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKTtcbiAgICBjb25zdCBtb2R1bGVQYXRoID0gam9pbihcbiAgICAgIGRpcm5hbWUocmVxdWlyZS5yZXNvbHZlKFwiQGRhdGFicmlja3MvYXBwa2l0XCIpKSxcbiAgICAgIFwiY2FjaGUvc3RvcmFnZS9wZXJzaXN0ZW50LmpzXCIsXG4gICAgKTtcbiAgICBjb25zdCBsb2FkZWQgPSByZXF1aXJlKG1vZHVsZVBhdGgpLlBlcnNpc3RlbnRTdG9yYWdlIGFzXG4gICAgICBQZXJzaXN0ZW50U3RvcmFnZUNvbnN0cnVjdG9yIHwgdW5kZWZpbmVkO1xuICAgIGlmICh0eXBlb2YgbG9hZGVkICE9PSBcImZ1bmN0aW9uXCIpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcInNvZnQgcGVyc2lzdGVudCBjYWNoZSBza2lwcGVkIChQZXJzaXN0ZW50U3RvcmFnZSBtaXNzaW5nKVwiKTtcbiAgICAgIHBlcnNpc3RlbnRTdG9yYWdlQ3RvciA9IHVuZGVmaW5lZDtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHBlcnNpc3RlbnRTdG9yYWdlQ3RvciA9IGxvYWRlZDtcbiAgICByZXR1cm4gbG9hZGVkO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJzb2Z0IHBlcnNpc3RlbnQgY2FjaGUgc2tpcHBlZCAoUGVyc2lzdGVudFN0b3JhZ2UgdW5hdmFpbGFibGUpXCIsIHtcbiAgICAgIGVycm9yOiBlcnJvclV0aWxzLmVycm9yTWVzc2FnZShlcnIpLFxuICAgIH0pO1xuICAgIHBlcnNpc3RlbnRTdG9yYWdlQ3RvciA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59XG5cbi8qKlxuICogV2hldGhlciBhbiBleGlzdGluZyBjYWNoZSB0YWJsZSBiZWxvbmdzIHRvIGFub3RoZXIgcm9sZS5cbiAqXG4gKiBQb3N0Z3JlU1FMIGFsbG93cyBncmFudGVkIHJlYWRzL3dyaXRlcyBidXQgcmVzZXJ2ZXMgaW5kZXggRERMIGZvciB0aGUgdGFibGVcbiAqIG93bmVyLiBTa2lwIEFwcEtpdCBtaWdyYXRpb25zIGluIHRoYXQgY2FzZTsge0BsaW5rIHByb2JlU3RvcmFnZX0gc3RpbGwgcHJvdmVzXG4gKiB0aGUgZXhpc3RpbmcgdGFibGUgY2FuIHNlcnZlIHRoZSBjYWNoZSBiZWZvcmUgaXQgaXMgYWNjZXB0ZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHRhYmxlT3duZWRCeUFub3RoZXJSb2xlKFxuICBzdG9yYWdlOiBQZXJzaXN0ZW50U3RvcmFnZUJhc2UsXG4gIHBvb2w6IExha2ViYXNlUG9vbCxcbik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBpZiAoIXN0b3JhZ2Uuc2NoZW1hTmFtZSB8fCAhc3RvcmFnZS50YWJsZU5hbWUpIHJldHVybiBmYWxzZTtcbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcG9vbC5xdWVyeShcbiAgICBgU0VMRUNUIHBnX2dldF91c2VyYnlpZChjLnJlbG93bmVyKSA9IGN1cnJlbnRfdXNlciBBUyBvd25lZFxuICAgICBGUk9NIHBnX2NsYXNzIGNcbiAgICAgSk9JTiBwZ19uYW1lc3BhY2UgbiBPTiBuLm9pZCA9IGMucmVsbmFtZXNwYWNlXG4gICAgIFdIRVJFIG4ubnNwbmFtZSA9ICQxIEFORCBjLnJlbG5hbWUgPSAkMiBBTkQgYy5yZWxraW5kIElOICgncicsICdwJylgLFxuICAgIFtzdG9yYWdlLnNjaGVtYU5hbWUsIHN0b3JhZ2UudGFibGVOYW1lXSxcbiAgKTtcbiAgaWYgKHJlc3VsdC5yb3dzWzBdPy5vd25lZCAhPT0gZmFsc2UpIHJldHVybiBmYWxzZTtcbiAgbG9nZ2VyLndhcm4oXCJwZXJzaXN0ZW50IGNhY2hlIG1pZ3JhdGlvbnMgc2tpcHBlZCAodGFibGUgb3duZWQgYnkgYW5vdGhlciByb2xlKVwiLCB7XG4gICAgc2NoZW1hOiBzdG9yYWdlLnNjaGVtYU5hbWUsXG4gICAgdGFibGU6IHN0b3JhZ2UudGFibGVOYW1lLFxuICB9KTtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKiBTb2Z0ZW4gYGluaXRpYWxpemUoKWAgc28gYW4gb3duZXJzaGlwLW9ubHkgbWlncmF0aW9uIGZhaWx1cmUgaXMgbm90IGZhdGFsLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNvZnRlbkluaXRpYWxpemUoXG4gIHN0b3JhZ2U6IFBlcnNpc3RlbnRTdG9yYWdlQmFzZSxcbiAgc2tpcE1pZ3JhdGlvbnM/OiAoKSA9PiBQcm9taXNlPGJvb2xlYW4+LFxuKTogdm9pZCB7XG4gIGNvbnN0IG9yaWdpbmFsSW5pdGlhbGl6ZSA9IHN0b3JhZ2UuaW5pdGlhbGl6ZS5iaW5kKHN0b3JhZ2UpO1xuICBsZXQgc29mdEluaXRQcm9taXNlOiBQcm9taXNlPHZvaWQ+IHwgdW5kZWZpbmVkO1xuXG4gIHN0b3JhZ2UuaW5pdGlhbGl6ZSA9IGFzeW5jICgpID0+IHtcbiAgICBpZiAoc3RvcmFnZS5pbml0aWFsaXplZCkgcmV0dXJuO1xuICAgIHNvZnRJbml0UHJvbWlzZSA/Pz0gKGFzeW5jICgpID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGlmICghKGF3YWl0IHNraXBNaWdyYXRpb25zPy4oKSkpIGF3YWl0IG9yaWdpbmFsSW5pdGlhbGl6ZSgpO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAhaGFuZGxlT3duZXJzaGlwTWlncmF0aW9uRXJyb3IoZXJyLCB7XG4gICAgICAgICAgICBzY29wZTogXCJjYWNoZS1zdG9yYWdlXCIsXG4gICAgICAgICAgICBsb2dnZXIsXG4gICAgICAgICAgICBldmVudDogXCJwZXJzaXN0ZW50IGNhY2hlIG1pZ3JhdGlvbiBmYWlsZWRcIixcbiAgICAgICAgICB9KVxuICAgICAgICApIHtcbiAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KSgpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBzb2Z0SW5pdFByb21pc2U7XG4gICAgICBzdG9yYWdlLmluaXRpYWxpemVkID0gdHJ1ZTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHNvZnRJbml0UHJvbWlzZSA9IHVuZGVmaW5lZDtcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH07XG59XG5cbi8qKiBWZXJpZnkgdGhlIG1pZ3JhdGVkIGNhY2hlIHRhYmxlIGNhbiBzZXJ2ZSB0aGUgcmVhZHMgYW5kIHdyaXRlcyBBcHBLaXQgbmVlZHMuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHJvYmVTdG9yYWdlKHN0b3JhZ2U6IFBlcnNpc3RlbnRTdG9yYWdlQmFzZSk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBrZXkgPSBgZGJ4LXRvb2xzOmNhY2hlLXByb2JlOiR7aGFzaC5pZCgpfWA7XG4gIHRyeSB7XG4gICAgYXdhaXQgc3RvcmFnZS5zZXQoa2V5LCB7IHZhbHVlOiBrZXksIGV4cGlyeTogRGF0ZS5ub3coKSArIDYwXzAwMCB9KTtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzdG9yYWdlLmdldChrZXkpO1xuICAgIGlmIChyZXN1bHQ/LnZhbHVlICE9PSBrZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcInBlcnNpc3RlbnQgY2FjaGUgcHJvYmUgcmV0dXJuZWQgYW4gdW5leHBlY3RlZCB2YWx1ZVwiKTtcbiAgICB9XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgc3RvcmFnZS5kZWxldGUoa2V5KS5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICB9XG59XG5cbi8qKlxuICogQnVpbGQgYSBzb2Z0LWZhaWwgTGFrZWJhc2UgY2FjaGUgc3RvcmFnZSB3aGVuIGEgcG9vbCBjYW4gYmUgY3JlYXRlZCBhbmRcbiAqIEFwcEtpdCdzIFBlcnNpc3RlbnRTdG9yYWdlIGNhbiBiZSBsb2FkZWQuIFJldHVybnMgYHVuZGVmaW5lZGAgb3RoZXJ3aXNlIHNvXG4gKiBBcHBLaXQgY2FuIGZhbGwgdGhyb3VnaCB0byBpdHMgbm9ybWFsIGNhY2hlIHBhdGguXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVTb2Z0UGVyc2lzdGVudFN0b3JhZ2UoXG4gIGNhY2hlOiBDYWNoZUNvbmZpZyB8IHVuZGVmaW5lZCxcbik6IFByb21pc2U8Q2FjaGVTdG9yYWdlIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IFBlcnNpc3RlbnRTdG9yYWdlID0gbG9hZFBlcnNpc3RlbnRTdG9yYWdlKCk7XG4gIGlmICghUGVyc2lzdGVudFN0b3JhZ2UpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgbGV0IHBvb2w6IExha2ViYXNlUG9vbCB8IHVuZGVmaW5lZDtcbiAgdHJ5IHtcbiAgICBjb25zdCBjcmVhdGVkUG9vbCA9IGNyZWF0ZUxha2ViYXNlUG9vbChcbiAgICAgIHBvc3RncmVzQ29ubmVjdGlvbk9wdGlvbnMoeyB3b3Jrc3BhY2VDbGllbnQ6IGdldFdvcmtzcGFjZUNsaWVudCh7fSkgfSksXG4gICAgKTtcbiAgICBwb29sID0gY3JlYXRlZFBvb2w7XG4gICAgY29uc3Qgc3RvcmFnZSA9IG5ldyBQZXJzaXN0ZW50U3RvcmFnZShjYWNoZSA/PyB7fSwgY3JlYXRlZFBvb2wpO1xuICAgIHNvZnRlbkluaXRpYWxpemUoc3RvcmFnZSwgKCkgPT4gdGFibGVPd25lZEJ5QW5vdGhlclJvbGUoc3RvcmFnZSwgY3JlYXRlZFBvb2wpKTtcbiAgICBpZiAoIShhd2FpdCBzdG9yYWdlLmhlYWx0aENoZWNrKCkpKSB7XG4gICAgICBhd2FpdCBzdG9yYWdlLmNsb3NlKCkuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gICAgYXdhaXQgc3RvcmFnZS5pbml0aWFsaXplKCk7XG4gICAgYXdhaXQgcHJvYmVTdG9yYWdlKHN0b3JhZ2UpO1xuICAgIHJldHVybiBuZXcgTDFDYWNoZVN0b3JhZ2Uoc3RvcmFnZSwge1xuICAgICAgbWF4Qnl0ZXM6IGNhY2hlPy5tYXhCeXRlcyxcbiAgICAgIG1heEVudHJpZXM6IGNhY2hlPy5tYXhTaXplLFxuICAgIH0pO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJzb2Z0IHBlcnNpc3RlbnQgY2FjaGUgdW5hdmFpbGFibGVcIiwge1xuICAgICAgZXJyb3I6IGVycm9yVXRpbHMuZXJyb3JNZXNzYWdlKGVyciksXG4gICAgfSk7XG4gICAgaWYgKHBvb2wpIHtcbiAgICAgIGF3YWl0IHBvb2wuZW5kKCkuY2F0Y2goKCkgPT4ge30pO1xuICAgIH1cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59XG4iXX0=
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==
@@ -0,0 +1,86 @@
1
+ /**
2
+ * AppKit-global filesystem cache with user-isolated process-local LRU values.
3
+ *
4
+ * @module
5
+ */
6
+ import { Plugin, type BasePluginConfig } from "@databricks/appkit";
7
+ import { type FileSystemCache } from "@dbx-tools/shared-fs";
8
+ /** Configuration for the AppKit files-cache plugin. */
9
+ export interface FilesCachePluginConfig extends BasePluginConfig {
10
+ /** Default entry TTL in milliseconds. Defaults to 60 seconds. */
11
+ ttlMs?: number;
12
+ /** Invalidation debounce window in milliseconds. Defaults to 25ms. */
13
+ invalidationBatchMs?: number;
14
+ /** Maximum entries retained in each user L1. Defaults to 1,000. */
15
+ maxEntries?: number;
16
+ /** Maximum retained bytes in each user L1. Defaults to 64 MiB. */
17
+ maxBytes?: number;
18
+ /** Maximum active user scopes retained by the process. Defaults to 256. */
19
+ maxScopes?: number;
20
+ }
21
+ /** Identity for one authorization-safe filesystem cache scope. */
22
+ export interface FilesCacheScope {
23
+ /** Databricks workspace host. */
24
+ host: string;
25
+ /** Attributed user identifier used to isolate values. */
26
+ userKey: string;
27
+ /** Optional workspace identifier included in shared invalidation scope. */
28
+ workspaceId?: string;
29
+ /** Optional per-scope entry TTL override in milliseconds. */
30
+ ttlMs?: number;
31
+ }
32
+ /** Stable identity for a framework filesystem source retained within one user scope. */
33
+ export interface FilesCacheSourceIdentity {
34
+ /** Actual mounted filesystem paths represented by the source. */
35
+ paths: readonly string[];
36
+ }
37
+ /** Public exports from {@link FilesCachePlugin}. */
38
+ export interface FilesCacheExports {
39
+ /** Resolve one user-isolated filesystem cache. */
40
+ forScope(scope: FilesCacheScope): Promise<FileSystemCache>;
41
+ /** Resolve one app-lifetime filesystem source for a user and its actual mounted paths. */
42
+ forFileSystem<T extends object>(scope: FilesCacheScope, identity: FilesCacheSourceIdentity, load: () => T | Promise<T>): Promise<T>;
43
+ /** Flush pending invalidation batches. */
44
+ flush(): Promise<void>;
45
+ }
46
+ /** Process-local filesystem cache manager. */
47
+ export declare class FilesCacheManager {
48
+ private readonly config;
49
+ private readonly groups;
50
+ private readonly scopes;
51
+ constructor(config?: FilesCachePluginConfig);
52
+ /** Return one user-isolated cache within a host/workspace invalidation group. */
53
+ forScope(input: FilesCacheScope): Promise<FileSystemCache>;
54
+ /** Retain one stable framework filesystem source for the scope and mounted paths. */
55
+ forFileSystem<T extends object>(input: FilesCacheScope, identity: FilesCacheSourceIdentity, load: () => T | Promise<T>): Promise<T>;
56
+ private resolveScopeState;
57
+ /** Flush every pending invalidation batch. */
58
+ flush(): Promise<void>;
59
+ /** Flush state and release all process-local entries and timers. */
60
+ close(): Promise<void>;
61
+ private group;
62
+ private createScope;
63
+ private read;
64
+ private keys;
65
+ private queueInvalidation;
66
+ private flushInvalidations;
67
+ }
68
+ /** AppKit plugin that owns process-local filesystem LRU caches. */
69
+ export declare class FilesCachePlugin extends Plugin<FilesCachePluginConfig> {
70
+ static manifest: {
71
+ name: "files-cache";
72
+ displayName: string;
73
+ description: string;
74
+ stability: "beta";
75
+ resources: {
76
+ required: never[];
77
+ optional: never[];
78
+ };
79
+ };
80
+ private manager?;
81
+ setup(): Promise<void>;
82
+ exports(): FilesCacheExports;
83
+ private requireManager;
84
+ }
85
+ /** AppKit registration factory for {@link FilesCachePlugin}. */
86
+ export declare const filesCache: import("@databricks/appkit").ToPlugin<typeof FilesCachePlugin, FilesCachePluginConfig, "files-cache">;
@@ -0,0 +1,230 @@
1
+ /**
2
+ * AppKit-global filesystem cache with user-isolated process-local LRU values.
3
+ *
4
+ * @module
5
+ */
6
+ import { Plugin, toPlugin } from "@databricks/appkit";
7
+ import { posixPath } from "@dbx-tools/shared-fs";
8
+ import { errorUtils, log, object } from "@dbx-tools/shared-core";
9
+ import { LRUCache } from "lru-cache";
10
+ const DEFAULT_TTL_MS = 60_000;
11
+ const DEFAULT_INVALIDATION_BATCH_MS = 25;
12
+ const DEFAULT_MAX_ENTRIES = 1_000;
13
+ const DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
14
+ const DEFAULT_MAX_SCOPES = 256;
15
+ const logger = log.logger("files-cache");
16
+ /** Process-local filesystem cache manager. */
17
+ export class FilesCacheManager {
18
+ config;
19
+ groups = new Map();
20
+ scopes;
21
+ constructor(config = {}) {
22
+ this.config = {
23
+ ttlMs: positiveDuration(config.ttlMs, DEFAULT_TTL_MS, "ttlMs"),
24
+ invalidationBatchMs: positiveDuration(config.invalidationBatchMs, DEFAULT_INVALIDATION_BATCH_MS, "invalidationBatchMs"),
25
+ maxEntries: positiveInteger(config.maxEntries, DEFAULT_MAX_ENTRIES, "maxEntries"),
26
+ maxBytes: positiveInteger(config.maxBytes, DEFAULT_MAX_BYTES, "maxBytes"),
27
+ maxScopes: positiveInteger(config.maxScopes, DEFAULT_MAX_SCOPES, "maxScopes"),
28
+ };
29
+ this.scopes = new LRUCache({
30
+ max: this.config.maxScopes,
31
+ dispose: (state) => {
32
+ state.group.scopes.delete(state);
33
+ },
34
+ });
35
+ }
36
+ /** Return one user-isolated cache within a host/workspace invalidation group. */
37
+ async forScope(input) {
38
+ return (await this.resolveScopeState(input)).cache;
39
+ }
40
+ /** Retain one stable framework filesystem source for the scope and mounted paths. */
41
+ async forFileSystem(input, identity, load) {
42
+ const state = await this.resolveScopeState(input);
43
+ const paths = identity.paths.map((path) => posixPath.normalizeRoot(path));
44
+ if (paths.length === 0)
45
+ throw new TypeError("Filesystem source paths must not be empty");
46
+ const sourceKey = object.toStableKey({ paths });
47
+ let source = state.sources.get(sourceKey);
48
+ if (!source) {
49
+ source = Promise.resolve(load());
50
+ state.sources.set(sourceKey, source);
51
+ }
52
+ try {
53
+ return (await source);
54
+ }
55
+ catch (error) {
56
+ if (state.sources.get(sourceKey) === source)
57
+ state.sources.delete(sourceKey);
58
+ throw error;
59
+ }
60
+ }
61
+ async resolveScopeState(input) {
62
+ const scope = resolveScope(input, this.config.ttlMs);
63
+ const scopeKey = object.toStableKey({
64
+ host: scope.host,
65
+ userKey: scope.userKey,
66
+ workspaceId: scope.workspaceId,
67
+ });
68
+ let state = this.scopes.get(scopeKey);
69
+ if (!state) {
70
+ const group = this.group(scope);
71
+ state = this.createScope(scope, group);
72
+ group.scopes.add(state);
73
+ this.scopes.set(scopeKey, state);
74
+ }
75
+ return state;
76
+ }
77
+ /** Flush every pending invalidation batch. */
78
+ async flush() {
79
+ await Promise.all([...this.groups.values()].map((group) => this.flushInvalidations(group)));
80
+ }
81
+ /** Flush state and release all process-local entries and timers. */
82
+ async close() {
83
+ for (const group of this.groups.values()) {
84
+ if (group.timer)
85
+ clearTimeout(group.timer);
86
+ group.timer = undefined;
87
+ }
88
+ await this.flush();
89
+ this.scopes.clear();
90
+ this.groups.clear();
91
+ }
92
+ group(scope) {
93
+ const key = object.toStableKey({
94
+ host: scope.host,
95
+ workspaceId: scope.workspaceId,
96
+ });
97
+ const existing = this.groups.get(key);
98
+ if (existing)
99
+ return existing;
100
+ const group = {
101
+ pending: new Set(),
102
+ scopes: new Set(),
103
+ };
104
+ this.groups.set(key, group);
105
+ return group;
106
+ }
107
+ createScope(scope, group) {
108
+ const state = {};
109
+ state.scope = scope;
110
+ state.group = group;
111
+ state.entries = new LRUCache({
112
+ max: this.config.maxEntries,
113
+ maxSize: this.config.maxBytes,
114
+ sizeCalculation: (entry, cacheKey) => retainedSize(cacheKey, entry),
115
+ });
116
+ state.sources = new Map();
117
+ state.cache = {
118
+ read: (cacheKey, load) => this.read(state, cacheKey, load),
119
+ invalidate: (cacheKey) => this.queueInvalidation(state, cacheKey),
120
+ keys: () => this.keys(state),
121
+ };
122
+ return state;
123
+ }
124
+ async read(state, key, load) {
125
+ if (!state.group.pending.has(key)) {
126
+ const retained = state.entries.get(key);
127
+ if (retained)
128
+ return retained.value;
129
+ }
130
+ const value = await load();
131
+ state.entries.set(key, { value }, { ttl: state.scope.ttlMs });
132
+ return value;
133
+ }
134
+ *keys(state) {
135
+ for (const key of state.entries.keys()) {
136
+ if (!state.group.pending.has(key))
137
+ yield key;
138
+ }
139
+ }
140
+ queueInvalidation(state, key) {
141
+ state.group.pending.add(key);
142
+ if (state.group.timer)
143
+ return;
144
+ state.group.timer = setTimeout(() => {
145
+ state.group.timer = undefined;
146
+ void this.flushInvalidations(state.group).catch((error) => {
147
+ logger.warn("filesystem cache invalidation batch failed", {
148
+ error: errorUtils.errorMessage(error),
149
+ });
150
+ });
151
+ }, this.config.invalidationBatchMs);
152
+ state.group.timer.unref?.();
153
+ }
154
+ async flushInvalidations(group) {
155
+ if (group.timer)
156
+ clearTimeout(group.timer);
157
+ group.timer = undefined;
158
+ if (group.pending.size === 0)
159
+ return;
160
+ const keys = [...group.pending];
161
+ group.pending.clear();
162
+ for (const state of group.scopes) {
163
+ for (const key of keys)
164
+ state.entries.delete(key);
165
+ }
166
+ }
167
+ }
168
+ /** AppKit plugin that owns process-local filesystem LRU caches. */
169
+ export class FilesCachePlugin extends Plugin {
170
+ static manifest = {
171
+ name: "files-cache",
172
+ displayName: "Files Cache",
173
+ description: "Provides bounded user-scoped process-local filesystem caches.",
174
+ stability: "beta",
175
+ resources: { required: [], optional: [] },
176
+ };
177
+ manager;
178
+ async setup() {
179
+ this.manager = new FilesCacheManager(this.config);
180
+ this.context?.onLifecycle("shutdown", () => this.manager?.close());
181
+ }
182
+ exports() {
183
+ return {
184
+ forScope: (scope) => this.requireManager().forScope(scope),
185
+ forFileSystem: (scope, identity, load) => this.requireManager().forFileSystem(scope, identity, load),
186
+ flush: () => this.requireManager().flush(),
187
+ };
188
+ }
189
+ requireManager() {
190
+ if (!this.manager)
191
+ throw new Error("Files cache plugin has not completed setup");
192
+ return this.manager;
193
+ }
194
+ }
195
+ /** AppKit registration factory for {@link FilesCachePlugin}. */
196
+ export const filesCache = toPlugin(FilesCachePlugin);
197
+ function resolveScope(input, defaultTtlMs) {
198
+ const host = input.host.trim().replace(/\/+$/, "");
199
+ const userKey = input.userKey.trim();
200
+ const workspaceId = input.workspaceId?.trim() || undefined;
201
+ if (!host)
202
+ throw new TypeError("Files cache scope host must not be blank");
203
+ if (!userKey)
204
+ throw new TypeError("Files cache scope userKey must not be blank");
205
+ return {
206
+ host,
207
+ userKey,
208
+ ...(workspaceId ? { workspaceId } : {}),
209
+ ttlMs: positiveDuration(input.ttlMs, defaultTtlMs, "scope.ttlMs"),
210
+ };
211
+ }
212
+ function positiveDuration(value, fallback, name) {
213
+ const resolved = value ?? fallback;
214
+ if (!Number.isFinite(resolved) || resolved <= 0) {
215
+ throw new RangeError(`${name} must be a finite positive number`);
216
+ }
217
+ return resolved;
218
+ }
219
+ function positiveInteger(value, fallback, name) {
220
+ return Math.floor(positiveDuration(value, fallback, name));
221
+ }
222
+ function retainedSize(key, entry) {
223
+ try {
224
+ return Buffer.byteLength(key) + Buffer.byteLength(JSON.stringify(entry.value));
225
+ }
226
+ catch {
227
+ return Number.MAX_SAFE_INTEGER;
228
+ }
229
+ }
230
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZXMtY2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmlsZXMtY2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUE4QyxNQUFNLG9CQUFvQixDQUFDO0FBQ2xHLE9BQU8sRUFBRSxTQUFTLEVBQXlDLE1BQU0sc0JBQXNCLENBQUM7QUFDeEYsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDakUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUVyQyxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUM7QUFDOUIsTUFBTSw2QkFBNkIsR0FBRyxFQUFFLENBQUM7QUFDekMsTUFBTSxtQkFBbUIsR0FBRyxLQUFLLENBQUM7QUFDbEMsTUFBTSxpQkFBaUIsR0FBRyxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQztBQUMzQyxNQUFNLGtCQUFrQixHQUFHLEdBQUcsQ0FBQztBQUMvQixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0FBaUZ6Qyw4Q0FBOEM7QUFDOUMsTUFBTSxPQUFPLGlCQUFpQjtJQUNYLE1BQU0sQ0FBMkI7SUFDakMsTUFBTSxHQUFHLElBQUksR0FBRyxFQUFzQixDQUFDO0lBQ3ZDLE1BQU0sQ0FBK0I7SUFFdEQsWUFBWSxTQUFpQyxFQUFFO1FBQzdDLElBQUksQ0FBQyxNQUFNLEdBQUc7WUFDWixLQUFLLEVBQUUsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxjQUFjLEVBQUUsT0FBTyxDQUFDO1lBQzlELG1CQUFtQixFQUFFLGdCQUFnQixDQUNuQyxNQUFNLENBQUMsbUJBQW1CLEVBQzFCLDZCQUE2QixFQUM3QixxQkFBcUIsQ0FDdEI7WUFDRCxVQUFVLEVBQUUsZUFBZSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsbUJBQW1CLEVBQUUsWUFBWSxDQUFDO1lBQ2pGLFFBQVEsRUFBRSxlQUFlLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsRUFBRSxVQUFVLENBQUM7WUFDekUsU0FBUyxFQUFFLGVBQWUsQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLGtCQUFrQixFQUFFLFdBQVcsQ0FBQztTQUM5RSxDQUFDO1FBQ0YsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFFBQVEsQ0FBQztZQUN6QixHQUFHLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTO1lBQzFCLE9BQU8sRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO2dCQUNqQixLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbkMsQ0FBQztTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxpRkFBaUY7SUFDakYsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUFzQjtRQUNuQyxPQUFPLENBQUMsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDckQsQ0FBQztJQUVELHFGQUFxRjtJQUNyRixLQUFLLENBQUMsYUFBYSxDQUNqQixLQUFzQixFQUN0QixRQUFrQyxFQUNsQyxJQUEwQjtRQUUxQixNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNsRCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQzFFLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsTUFBTSxJQUFJLFNBQVMsQ0FBQywyQ0FBMkMsQ0FBQyxDQUFDO1FBQ3pGLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2hELElBQUksTUFBTSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7WUFDakMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZDLENBQUM7UUFDRCxJQUFJLENBQUM7WUFDSCxPQUFPLENBQUMsTUFBTSxNQUFNLENBQU0sQ0FBQztRQUM3QixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLEtBQUssTUFBTTtnQkFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM3RSxNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0lBRU8sS0FBSyxDQUFDLGlCQUFpQixDQUFDLEtBQXNCO1FBQ3BELE1BQU0sS0FBSyxHQUFHLFlBQVksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNyRCxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDO1lBQ2xDLElBQUksRUFBRSxLQUFLLENBQUMsSUFBSTtZQUNoQixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87WUFDdEIsV0FBVyxFQUFFLEtBQUssQ0FBQyxXQUFXO1NBQy9CLENBQUMsQ0FBQztRQUNILElBQUksS0FBSyxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3RDLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNYLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDaEMsS0FBSyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3ZDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3hCLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNuQyxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsOENBQThDO0lBQzlDLEtBQUssQ0FBQyxLQUFLO1FBQ1QsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlGLENBQUM7SUFFRCxvRUFBb0U7SUFDcEUsS0FBSyxDQUFDLEtBQUs7UUFDVCxLQUFLLE1BQU0sS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQztZQUN6QyxJQUFJLEtBQUssQ0FBQyxLQUFLO2dCQUFFLFlBQVksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDM0MsS0FBSyxDQUFDLEtBQUssR0FBRyxTQUFTLENBQUM7UUFDMUIsQ0FBQztRQUNELE1BQU0sSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ25CLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDcEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRU8sS0FBSyxDQUFDLEtBQThCO1FBQzFDLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUM7WUFDN0IsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO1lBQ2hCLFdBQVcsRUFBRSxLQUFLLENBQUMsV0FBVztTQUMvQixDQUFDLENBQUM7UUFDSCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN0QyxJQUFJLFFBQVE7WUFBRSxPQUFPLFFBQVEsQ0FBQztRQUM5QixNQUFNLEtBQUssR0FBZTtZQUN4QixPQUFPLEVBQUUsSUFBSSxHQUFHLEVBQUU7WUFDbEIsTUFBTSxFQUFFLElBQUksR0FBRyxFQUFFO1NBQ2xCLENBQUM7UUFDRixJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDNUIsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRU8sV0FBVyxDQUFDLEtBQThCLEVBQUUsS0FBaUI7UUFDbkUsTUFBTSxLQUFLLEdBQUcsRUFBZ0IsQ0FBQztRQUMvQixLQUFLLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNwQixLQUFLLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNwQixLQUFLLENBQUMsT0FBTyxHQUFHLElBQUksUUFBUSxDQUFDO1lBQzNCLEdBQUcsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVU7WUFDM0IsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUTtZQUM3QixlQUFlLEVBQUUsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLEVBQUUsQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQztTQUNwRSxDQUFDLENBQUM7UUFDSCxLQUFLLENBQUMsT0FBTyxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7UUFDMUIsS0FBSyxDQUFDLEtBQUssR0FBRztZQUNaLElBQUksRUFBRSxDQUF1QixRQUFnQixFQUFFLElBQTBCLEVBQUUsRUFBRSxDQUMzRSxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1lBQ2xDLFVBQVUsRUFBRSxDQUFDLFFBQWdCLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDO1lBQ3pFLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQztTQUM3QixDQUFDO1FBQ0YsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRU8sS0FBSyxDQUFDLElBQUksQ0FDaEIsS0FBaUIsRUFDakIsR0FBVyxFQUNYLElBQTBCO1FBRTFCLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNsQyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4QyxJQUFJLFFBQVE7Z0JBQUUsT0FBTyxRQUFRLENBQUMsS0FBVSxDQUFDO1FBQzNDLENBQUM7UUFDRCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksRUFBRSxDQUFDO1FBQzNCLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLEtBQUssQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUM5RCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFTyxDQUFDLElBQUksQ0FBQyxLQUFpQjtRQUM3QixLQUFLLE1BQU0sR0FBRyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUN2QyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQztnQkFBRSxNQUFNLEdBQUcsQ0FBQztRQUMvQyxDQUFDO0lBQ0gsQ0FBQztJQUVPLGlCQUFpQixDQUFDLEtBQWlCLEVBQUUsR0FBVztRQUN0RCxLQUFLLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDN0IsSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLEtBQUs7WUFBRSxPQUFPO1FBQzlCLEtBQUssQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDbEMsS0FBSyxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsU0FBUyxDQUFDO1lBQzlCLEtBQUssSUFBSSxDQUFDLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtnQkFDeEQsTUFBTSxDQUFDLElBQUksQ0FBQyw0Q0FBNEMsRUFBRTtvQkFDeEQsS0FBSyxFQUFFLFVBQVUsQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDO2lCQUN0QyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDcEMsS0FBSyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBRU8sS0FBSyxDQUFDLGtCQUFrQixDQUFDLEtBQWlCO1FBQ2hELElBQUksS0FBSyxDQUFDLEtBQUs7WUFBRSxZQUFZLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzNDLEtBQUssQ0FBQyxLQUFLLEdBQUcsU0FBUyxDQUFDO1FBQ3hCLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEtBQUssQ0FBQztZQUFFLE9BQU87UUFDckMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNoQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3RCLEtBQUssTUFBTSxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2pDLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSTtnQkFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNwRCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBRUQsbUVBQW1FO0FBQ25FLE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxNQUE4QjtJQUNsRSxNQUFNLENBQUMsUUFBUSxHQUFHO1FBQ2hCLElBQUksRUFBRSxhQUFhO1FBQ25CLFdBQVcsRUFBRSxhQUFhO1FBQzFCLFdBQVcsRUFBRSwrREFBK0Q7UUFDNUUsU0FBUyxFQUFFLE1BQU07UUFDakIsU0FBUyxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFO0tBQ0YsQ0FBQztJQUVsQyxPQUFPLENBQXFCO0lBRTNCLEtBQUssQ0FBQyxLQUFLO1FBQ2xCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUMsVUFBVSxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRVEsT0FBTztRQUNkLE9BQU87WUFDTCxRQUFRLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDO1lBQzFELGFBQWEsRUFBRSxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEVBQUUsQ0FDdkMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQztZQUM1RCxLQUFLLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLEtBQUssRUFBRTtTQUMzQyxDQUFDO0lBQ0osQ0FBQztJQUVPLGNBQWM7UUFDcEIsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFDO1FBQ2pGLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQztJQUN0QixDQUFDOztBQUdILGdFQUFnRTtBQUNoRSxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLGdCQUFnQixDQUFDLENBQUM7QUFFckQsU0FBUyxZQUFZLENBQUMsS0FBc0IsRUFBRSxZQUFvQjtJQUNoRSxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDbkQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNyQyxNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLFNBQVMsQ0FBQztJQUMzRCxJQUFJLENBQUMsSUFBSTtRQUFFLE1BQU0sSUFBSSxTQUFTLENBQUMsMENBQTBDLENBQUMsQ0FBQztJQUMzRSxJQUFJLENBQUMsT0FBTztRQUFFLE1BQU0sSUFBSSxTQUFTLENBQUMsNkNBQTZDLENBQUMsQ0FBQztJQUNqRixPQUFPO1FBQ0wsSUFBSTtRQUNKLE9BQU87UUFDUCxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDdkMsS0FBSyxFQUFFLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsWUFBWSxFQUFFLGFBQWEsQ0FBQztLQUNsRSxDQUFDO0FBQ0osQ0FBQztBQUVELFNBQVMsZ0JBQWdCLENBQUMsS0FBeUIsRUFBRSxRQUFnQixFQUFFLElBQVk7SUFDakYsTUFBTSxRQUFRLEdBQUcsS0FBSyxJQUFJLFFBQVEsQ0FBQztJQUNuQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDaEQsTUFBTSxJQUFJLFVBQVUsQ0FBQyxHQUFHLElBQUksbUNBQW1DLENBQUMsQ0FBQztJQUNuRSxDQUFDO0lBQ0QsT0FBTyxRQUFRLENBQUM7QUFDbEIsQ0FBQztBQUVELFNBQVMsZUFBZSxDQUFDLEtBQXlCLEVBQUUsUUFBZ0IsRUFBRSxJQUFZO0lBQ2hGLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDN0QsQ0FBQztBQUVELFNBQVMsWUFBWSxDQUFDLEdBQVcsRUFBRSxLQUFvQjtJQUNyRCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBQ2pGLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCxPQUFPLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztJQUNqQyxDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQXBwS2l0LWdsb2JhbCBmaWxlc3lzdGVtIGNhY2hlIHdpdGggdXNlci1pc29sYXRlZCBwcm9jZXNzLWxvY2FsIExSVSB2YWx1ZXMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IFBsdWdpbiwgdG9QbHVnaW4sIHR5cGUgQmFzZVBsdWdpbkNvbmZpZywgdHlwZSBQbHVnaW5NYW5pZmVzdCB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IHBvc2l4UGF0aCwgdHlwZSBDYWNoZVZhbHVlLCB0eXBlIEZpbGVTeXN0ZW1DYWNoZSB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1mc1wiO1xuaW1wb3J0IHsgZXJyb3JVdGlscywgbG9nLCBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgTFJVQ2FjaGUgfSBmcm9tIFwibHJ1LWNhY2hlXCI7XG5cbmNvbnN0IERFRkFVTFRfVFRMX01TID0gNjBfMDAwO1xuY29uc3QgREVGQVVMVF9JTlZBTElEQVRJT05fQkFUQ0hfTVMgPSAyNTtcbmNvbnN0IERFRkFVTFRfTUFYX0VOVFJJRVMgPSAxXzAwMDtcbmNvbnN0IERFRkFVTFRfTUFYX0JZVEVTID0gNjQgKiAxMDI0ICogMTAyNDtcbmNvbnN0IERFRkFVTFRfTUFYX1NDT1BFUyA9IDI1NjtcbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJmaWxlcy1jYWNoZVwiKTtcblxuaW50ZXJmYWNlIFJldGFpbmVkRW50cnkge1xuICB2YWx1ZTogQ2FjaGVWYWx1ZTtcbn1cblxuaW50ZXJmYWNlIFNjb3BlU3RhdGUge1xuICBjYWNoZTogRmlsZVN5c3RlbUNhY2hlO1xuICBlbnRyaWVzOiBMUlVDYWNoZTxzdHJpbmcsIFJldGFpbmVkRW50cnk+O1xuICBncm91cDogR3JvdXBTdGF0ZTtcbiAgc2NvcGU6IFJlc29sdmVkRmlsZXNDYWNoZVNjb3BlO1xuICBzb3VyY2VzOiBNYXA8c3RyaW5nLCBQcm9taXNlPG9iamVjdD4+O1xufVxuXG5pbnRlcmZhY2UgR3JvdXBTdGF0ZSB7XG4gIHBlbmRpbmc6IFNldDxzdHJpbmc+O1xuICBzY29wZXM6IFNldDxTY29wZVN0YXRlPjtcbiAgdGltZXI/OiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0Pjtcbn1cblxuaW50ZXJmYWNlIFJlc29sdmVkRmlsZXNDYWNoZUNvbmZpZyB7XG4gIGludmFsaWRhdGlvbkJhdGNoTXM6IG51bWJlcjtcbiAgbWF4Qnl0ZXM6IG51bWJlcjtcbiAgbWF4RW50cmllczogbnVtYmVyO1xuICBtYXhTY29wZXM6IG51bWJlcjtcbiAgdHRsTXM6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFJlc29sdmVkRmlsZXNDYWNoZVNjb3BlIHtcbiAgaG9zdDogc3RyaW5nO1xuICB0dGxNczogbnVtYmVyO1xuICB1c2VyS2V5OiBzdHJpbmc7XG4gIHdvcmtzcGFjZUlkPzogc3RyaW5nO1xufVxuXG4vKiogQ29uZmlndXJhdGlvbiBmb3IgdGhlIEFwcEtpdCBmaWxlcy1jYWNoZSBwbHVnaW4uICovXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVzQ2FjaGVQbHVnaW5Db25maWcgZXh0ZW5kcyBCYXNlUGx1Z2luQ29uZmlnIHtcbiAgLyoqIERlZmF1bHQgZW50cnkgVFRMIGluIG1pbGxpc2Vjb25kcy4gRGVmYXVsdHMgdG8gNjAgc2Vjb25kcy4gKi9cbiAgdHRsTXM/OiBudW1iZXI7XG4gIC8qKiBJbnZhbGlkYXRpb24gZGVib3VuY2Ugd2luZG93IGluIG1pbGxpc2Vjb25kcy4gRGVmYXVsdHMgdG8gMjVtcy4gKi9cbiAgaW52YWxpZGF0aW9uQmF0Y2hNcz86IG51bWJlcjtcbiAgLyoqIE1heGltdW0gZW50cmllcyByZXRhaW5lZCBpbiBlYWNoIHVzZXIgTDEuIERlZmF1bHRzIHRvIDEsMDAwLiAqL1xuICBtYXhFbnRyaWVzPzogbnVtYmVyO1xuICAvKiogTWF4aW11bSByZXRhaW5lZCBieXRlcyBpbiBlYWNoIHVzZXIgTDEuIERlZmF1bHRzIHRvIDY0IE1pQi4gKi9cbiAgbWF4Qnl0ZXM/OiBudW1iZXI7XG4gIC8qKiBNYXhpbXVtIGFjdGl2ZSB1c2VyIHNjb3BlcyByZXRhaW5lZCBieSB0aGUgcHJvY2Vzcy4gRGVmYXVsdHMgdG8gMjU2LiAqL1xuICBtYXhTY29wZXM/OiBudW1iZXI7XG59XG5cbi8qKiBJZGVudGl0eSBmb3Igb25lIGF1dGhvcml6YXRpb24tc2FmZSBmaWxlc3lzdGVtIGNhY2hlIHNjb3BlLiAqL1xuZXhwb3J0IGludGVyZmFjZSBGaWxlc0NhY2hlU2NvcGUge1xuICAvKiogRGF0YWJyaWNrcyB3b3Jrc3BhY2UgaG9zdC4gKi9cbiAgaG9zdDogc3RyaW5nO1xuICAvKiogQXR0cmlidXRlZCB1c2VyIGlkZW50aWZpZXIgdXNlZCB0byBpc29sYXRlIHZhbHVlcy4gKi9cbiAgdXNlcktleTogc3RyaW5nO1xuICAvKiogT3B0aW9uYWwgd29ya3NwYWNlIGlkZW50aWZpZXIgaW5jbHVkZWQgaW4gc2hhcmVkIGludmFsaWRhdGlvbiBzY29wZS4gKi9cbiAgd29ya3NwYWNlSWQ/OiBzdHJpbmc7XG4gIC8qKiBPcHRpb25hbCBwZXItc2NvcGUgZW50cnkgVFRMIG92ZXJyaWRlIGluIG1pbGxpc2Vjb25kcy4gKi9cbiAgdHRsTXM/OiBudW1iZXI7XG59XG5cbi8qKiBTdGFibGUgaWRlbnRpdHkgZm9yIGEgZnJhbWV3b3JrIGZpbGVzeXN0ZW0gc291cmNlIHJldGFpbmVkIHdpdGhpbiBvbmUgdXNlciBzY29wZS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRmlsZXNDYWNoZVNvdXJjZUlkZW50aXR5IHtcbiAgLyoqIEFjdHVhbCBtb3VudGVkIGZpbGVzeXN0ZW0gcGF0aHMgcmVwcmVzZW50ZWQgYnkgdGhlIHNvdXJjZS4gKi9cbiAgcGF0aHM6IHJlYWRvbmx5IHN0cmluZ1tdO1xufVxuXG4vKiogUHVibGljIGV4cG9ydHMgZnJvbSB7QGxpbmsgRmlsZXNDYWNoZVBsdWdpbn0uICovXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVzQ2FjaGVFeHBvcnRzIHtcbiAgLyoqIFJlc29sdmUgb25lIHVzZXItaXNvbGF0ZWQgZmlsZXN5c3RlbSBjYWNoZS4gKi9cbiAgZm9yU2NvcGUoc2NvcGU6IEZpbGVzQ2FjaGVTY29wZSk6IFByb21pc2U8RmlsZVN5c3RlbUNhY2hlPjtcbiAgLyoqIFJlc29sdmUgb25lIGFwcC1saWZldGltZSBmaWxlc3lzdGVtIHNvdXJjZSBmb3IgYSB1c2VyIGFuZCBpdHMgYWN0dWFsIG1vdW50ZWQgcGF0aHMuICovXG4gIGZvckZpbGVTeXN0ZW08VCBleHRlbmRzIG9iamVjdD4oXG4gICAgc2NvcGU6IEZpbGVzQ2FjaGVTY29wZSxcbiAgICBpZGVudGl0eTogRmlsZXNDYWNoZVNvdXJjZUlkZW50aXR5LFxuICAgIGxvYWQ6ICgpID0+IFQgfCBQcm9taXNlPFQ+LFxuICApOiBQcm9taXNlPFQ+O1xuICAvKiogRmx1c2ggcGVuZGluZyBpbnZhbGlkYXRpb24gYmF0Y2hlcy4gKi9cbiAgZmx1c2goKTogUHJvbWlzZTx2b2lkPjtcbn1cblxuLyoqIFByb2Nlc3MtbG9jYWwgZmlsZXN5c3RlbSBjYWNoZSBtYW5hZ2VyLiAqL1xuZXhwb3J0IGNsYXNzIEZpbGVzQ2FjaGVNYW5hZ2VyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBjb25maWc6IFJlc29sdmVkRmlsZXNDYWNoZUNvbmZpZztcbiAgcHJpdmF0ZSByZWFkb25seSBncm91cHMgPSBuZXcgTWFwPHN0cmluZywgR3JvdXBTdGF0ZT4oKTtcbiAgcHJpdmF0ZSByZWFkb25seSBzY29wZXM6IExSVUNhY2hlPHN0cmluZywgU2NvcGVTdGF0ZT47XG5cbiAgY29uc3RydWN0b3IoY29uZmlnOiBGaWxlc0NhY2hlUGx1Z2luQ29uZmlnID0ge30pIHtcbiAgICB0aGlzLmNvbmZpZyA9IHtcbiAgICAgIHR0bE1zOiBwb3NpdGl2ZUR1cmF0aW9uKGNvbmZpZy50dGxNcywgREVGQVVMVF9UVExfTVMsIFwidHRsTXNcIiksXG4gICAgICBpbnZhbGlkYXRpb25CYXRjaE1zOiBwb3NpdGl2ZUR1cmF0aW9uKFxuICAgICAgICBjb25maWcuaW52YWxpZGF0aW9uQmF0Y2hNcyxcbiAgICAgICAgREVGQVVMVF9JTlZBTElEQVRJT05fQkFUQ0hfTVMsXG4gICAgICAgIFwiaW52YWxpZGF0aW9uQmF0Y2hNc1wiLFxuICAgICAgKSxcbiAgICAgIG1heEVudHJpZXM6IHBvc2l0aXZlSW50ZWdlcihjb25maWcubWF4RW50cmllcywgREVGQVVMVF9NQVhfRU5UUklFUywgXCJtYXhFbnRyaWVzXCIpLFxuICAgICAgbWF4Qnl0ZXM6IHBvc2l0aXZlSW50ZWdlcihjb25maWcubWF4Qnl0ZXMsIERFRkFVTFRfTUFYX0JZVEVTLCBcIm1heEJ5dGVzXCIpLFxuICAgICAgbWF4U2NvcGVzOiBwb3NpdGl2ZUludGVnZXIoY29uZmlnLm1heFNjb3BlcywgREVGQVVMVF9NQVhfU0NPUEVTLCBcIm1heFNjb3Blc1wiKSxcbiAgICB9O1xuICAgIHRoaXMuc2NvcGVzID0gbmV3IExSVUNhY2hlKHtcbiAgICAgIG1heDogdGhpcy5jb25maWcubWF4U2NvcGVzLFxuICAgICAgZGlzcG9zZTogKHN0YXRlKSA9PiB7XG4gICAgICAgIHN0YXRlLmdyb3VwLnNjb3Blcy5kZWxldGUoc3RhdGUpO1xuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBSZXR1cm4gb25lIHVzZXItaXNvbGF0ZWQgY2FjaGUgd2l0aGluIGEgaG9zdC93b3Jrc3BhY2UgaW52YWxpZGF0aW9uIGdyb3VwLiAqL1xuICBhc3luYyBmb3JTY29wZShpbnB1dDogRmlsZXNDYWNoZVNjb3BlKTogUHJvbWlzZTxGaWxlU3lzdGVtQ2FjaGU+IHtcbiAgICByZXR1cm4gKGF3YWl0IHRoaXMucmVzb2x2ZVNjb3BlU3RhdGUoaW5wdXQpKS5jYWNoZTtcbiAgfVxuXG4gIC8qKiBSZXRhaW4gb25lIHN0YWJsZSBmcmFtZXdvcmsgZmlsZXN5c3RlbSBzb3VyY2UgZm9yIHRoZSBzY29wZSBhbmQgbW91bnRlZCBwYXRocy4gKi9cbiAgYXN5bmMgZm9yRmlsZVN5c3RlbTxUIGV4dGVuZHMgb2JqZWN0PihcbiAgICBpbnB1dDogRmlsZXNDYWNoZVNjb3BlLFxuICAgIGlkZW50aXR5OiBGaWxlc0NhY2hlU291cmNlSWRlbnRpdHksXG4gICAgbG9hZDogKCkgPT4gVCB8IFByb21pc2U8VD4sXG4gICk6IFByb21pc2U8VD4ge1xuICAgIGNvbnN0IHN0YXRlID0gYXdhaXQgdGhpcy5yZXNvbHZlU2NvcGVTdGF0ZShpbnB1dCk7XG4gICAgY29uc3QgcGF0aHMgPSBpZGVudGl0eS5wYXRocy5tYXAoKHBhdGgpID0+IHBvc2l4UGF0aC5ub3JtYWxpemVSb290KHBhdGgpKTtcbiAgICBpZiAocGF0aHMubGVuZ3RoID09PSAwKSB0aHJvdyBuZXcgVHlwZUVycm9yKFwiRmlsZXN5c3RlbSBzb3VyY2UgcGF0aHMgbXVzdCBub3QgYmUgZW1wdHlcIik7XG4gICAgY29uc3Qgc291cmNlS2V5ID0gb2JqZWN0LnRvU3RhYmxlS2V5KHsgcGF0aHMgfSk7XG4gICAgbGV0IHNvdXJjZSA9IHN0YXRlLnNvdXJjZXMuZ2V0KHNvdXJjZUtleSk7XG4gICAgaWYgKCFzb3VyY2UpIHtcbiAgICAgIHNvdXJjZSA9IFByb21pc2UucmVzb2x2ZShsb2FkKCkpO1xuICAgICAgc3RhdGUuc291cmNlcy5zZXQoc291cmNlS2V5LCBzb3VyY2UpO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIChhd2FpdCBzb3VyY2UpIGFzIFQ7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChzdGF0ZS5zb3VyY2VzLmdldChzb3VyY2VLZXkpID09PSBzb3VyY2UpIHN0YXRlLnNvdXJjZXMuZGVsZXRlKHNvdXJjZUtleSk7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJlc29sdmVTY29wZVN0YXRlKGlucHV0OiBGaWxlc0NhY2hlU2NvcGUpOiBQcm9taXNlPFNjb3BlU3RhdGU+IHtcbiAgICBjb25zdCBzY29wZSA9IHJlc29sdmVTY29wZShpbnB1dCwgdGhpcy5jb25maWcudHRsTXMpO1xuICAgIGNvbnN0IHNjb3BlS2V5ID0gb2JqZWN0LnRvU3RhYmxlS2V5KHtcbiAgICAgIGhvc3Q6IHNjb3BlLmhvc3QsXG4gICAgICB1c2VyS2V5OiBzY29wZS51c2VyS2V5LFxuICAgICAgd29ya3NwYWNlSWQ6IHNjb3BlLndvcmtzcGFjZUlkLFxuICAgIH0pO1xuICAgIGxldCBzdGF0ZSA9IHRoaXMuc2NvcGVzLmdldChzY29wZUtleSk7XG4gICAgaWYgKCFzdGF0ZSkge1xuICAgICAgY29uc3QgZ3JvdXAgPSB0aGlzLmdyb3VwKHNjb3BlKTtcbiAgICAgIHN0YXRlID0gdGhpcy5jcmVhdGVTY29wZShzY29wZSwgZ3JvdXApO1xuICAgICAgZ3JvdXAuc2NvcGVzLmFkZChzdGF0ZSk7XG4gICAgICB0aGlzLnNjb3Blcy5zZXQoc2NvcGVLZXksIHN0YXRlKTtcbiAgICB9XG4gICAgcmV0dXJuIHN0YXRlO1xuICB9XG5cbiAgLyoqIEZsdXNoIGV2ZXJ5IHBlbmRpbmcgaW52YWxpZGF0aW9uIGJhdGNoLiAqL1xuICBhc3luYyBmbHVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBQcm9taXNlLmFsbChbLi4udGhpcy5ncm91cHMudmFsdWVzKCldLm1hcCgoZ3JvdXApID0+IHRoaXMuZmx1c2hJbnZhbGlkYXRpb25zKGdyb3VwKSkpO1xuICB9XG5cbiAgLyoqIEZsdXNoIHN0YXRlIGFuZCByZWxlYXNlIGFsbCBwcm9jZXNzLWxvY2FsIGVudHJpZXMgYW5kIHRpbWVycy4gKi9cbiAgYXN5bmMgY2xvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgZm9yIChjb25zdCBncm91cCBvZiB0aGlzLmdyb3Vwcy52YWx1ZXMoKSkge1xuICAgICAgaWYgKGdyb3VwLnRpbWVyKSBjbGVhclRpbWVvdXQoZ3JvdXAudGltZXIpO1xuICAgICAgZ3JvdXAudGltZXIgPSB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGF3YWl0IHRoaXMuZmx1c2goKTtcbiAgICB0aGlzLnNjb3Blcy5jbGVhcigpO1xuICAgIHRoaXMuZ3JvdXBzLmNsZWFyKCk7XG4gIH1cblxuICBwcml2YXRlIGdyb3VwKHNjb3BlOiBSZXNvbHZlZEZpbGVzQ2FjaGVTY29wZSk6IEdyb3VwU3RhdGUge1xuICAgIGNvbnN0IGtleSA9IG9iamVjdC50b1N0YWJsZUtleSh7XG4gICAgICBob3N0OiBzY29wZS5ob3N0LFxuICAgICAgd29ya3NwYWNlSWQ6IHNjb3BlLndvcmtzcGFjZUlkLFxuICAgIH0pO1xuICAgIGNvbnN0IGV4aXN0aW5nID0gdGhpcy5ncm91cHMuZ2V0KGtleSk7XG4gICAgaWYgKGV4aXN0aW5nKSByZXR1cm4gZXhpc3Rpbmc7XG4gICAgY29uc3QgZ3JvdXA6IEdyb3VwU3RhdGUgPSB7XG4gICAgICBwZW5kaW5nOiBuZXcgU2V0KCksXG4gICAgICBzY29wZXM6IG5ldyBTZXQoKSxcbiAgICB9O1xuICAgIHRoaXMuZ3JvdXBzLnNldChrZXksIGdyb3VwKTtcbiAgICByZXR1cm4gZ3JvdXA7XG4gIH1cblxuICBwcml2YXRlIGNyZWF0ZVNjb3BlKHNjb3BlOiBSZXNvbHZlZEZpbGVzQ2FjaGVTY29wZSwgZ3JvdXA6IEdyb3VwU3RhdGUpOiBTY29wZVN0YXRlIHtcbiAgICBjb25zdCBzdGF0ZSA9IHt9IGFzIFNjb3BlU3RhdGU7XG4gICAgc3RhdGUuc2NvcGUgPSBzY29wZTtcbiAgICBzdGF0ZS5ncm91cCA9IGdyb3VwO1xuICAgIHN0YXRlLmVudHJpZXMgPSBuZXcgTFJVQ2FjaGUoe1xuICAgICAgbWF4OiB0aGlzLmNvbmZpZy5tYXhFbnRyaWVzLFxuICAgICAgbWF4U2l6ZTogdGhpcy5jb25maWcubWF4Qnl0ZXMsXG4gICAgICBzaXplQ2FsY3VsYXRpb246IChlbnRyeSwgY2FjaGVLZXkpID0+IHJldGFpbmVkU2l6ZShjYWNoZUtleSwgZW50cnkpLFxuICAgIH0pO1xuICAgIHN0YXRlLnNvdXJjZXMgPSBuZXcgTWFwKCk7XG4gICAgc3RhdGUuY2FjaGUgPSB7XG4gICAgICByZWFkOiA8VCBleHRlbmRzIENhY2hlVmFsdWU+KGNhY2hlS2V5OiBzdHJpbmcsIGxvYWQ6ICgpID0+IFQgfCBQcm9taXNlPFQ+KSA9PlxuICAgICAgICB0aGlzLnJlYWQoc3RhdGUsIGNhY2hlS2V5LCBsb2FkKSxcbiAgICAgIGludmFsaWRhdGU6IChjYWNoZUtleTogc3RyaW5nKSA9PiB0aGlzLnF1ZXVlSW52YWxpZGF0aW9uKHN0YXRlLCBjYWNoZUtleSksXG4gICAgICBrZXlzOiAoKSA9PiB0aGlzLmtleXMoc3RhdGUpLFxuICAgIH07XG4gICAgcmV0dXJuIHN0YXRlO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWFkPFQgZXh0ZW5kcyBDYWNoZVZhbHVlPihcbiAgICBzdGF0ZTogU2NvcGVTdGF0ZSxcbiAgICBrZXk6IHN0cmluZyxcbiAgICBsb2FkOiAoKSA9PiBUIHwgUHJvbWlzZTxUPixcbiAgKTogUHJvbWlzZTxUPiB7XG4gICAgaWYgKCFzdGF0ZS5ncm91cC5wZW5kaW5nLmhhcyhrZXkpKSB7XG4gICAgICBjb25zdCByZXRhaW5lZCA9IHN0YXRlLmVudHJpZXMuZ2V0KGtleSk7XG4gICAgICBpZiAocmV0YWluZWQpIHJldHVybiByZXRhaW5lZC52YWx1ZSBhcyBUO1xuICAgIH1cbiAgICBjb25zdCB2YWx1ZSA9IGF3YWl0IGxvYWQoKTtcbiAgICBzdGF0ZS5lbnRyaWVzLnNldChrZXksIHsgdmFsdWUgfSwgeyB0dGw6IHN0YXRlLnNjb3BlLnR0bE1zIH0pO1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuXG4gIHByaXZhdGUgKmtleXMoc3RhdGU6IFNjb3BlU3RhdGUpOiBJdGVyYWJsZTxzdHJpbmc+IHtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiBzdGF0ZS5lbnRyaWVzLmtleXMoKSkge1xuICAgICAgaWYgKCFzdGF0ZS5ncm91cC5wZW5kaW5nLmhhcyhrZXkpKSB5aWVsZCBrZXk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBxdWV1ZUludmFsaWRhdGlvbihzdGF0ZTogU2NvcGVTdGF0ZSwga2V5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBzdGF0ZS5ncm91cC5wZW5kaW5nLmFkZChrZXkpO1xuICAgIGlmIChzdGF0ZS5ncm91cC50aW1lcikgcmV0dXJuO1xuICAgIHN0YXRlLmdyb3VwLnRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBzdGF0ZS5ncm91cC50aW1lciA9IHVuZGVmaW5lZDtcbiAgICAgIHZvaWQgdGhpcy5mbHVzaEludmFsaWRhdGlvbnMoc3RhdGUuZ3JvdXApLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgICBsb2dnZXIud2FybihcImZpbGVzeXN0ZW0gY2FjaGUgaW52YWxpZGF0aW9uIGJhdGNoIGZhaWxlZFwiLCB7XG4gICAgICAgICAgZXJyb3I6IGVycm9yVXRpbHMuZXJyb3JNZXNzYWdlKGVycm9yKSxcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9LCB0aGlzLmNvbmZpZy5pbnZhbGlkYXRpb25CYXRjaE1zKTtcbiAgICBzdGF0ZS5ncm91cC50aW1lci51bnJlZj8uKCk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGZsdXNoSW52YWxpZGF0aW9ucyhncm91cDogR3JvdXBTdGF0ZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmIChncm91cC50aW1lcikgY2xlYXJUaW1lb3V0KGdyb3VwLnRpbWVyKTtcbiAgICBncm91cC50aW1lciA9IHVuZGVmaW5lZDtcbiAgICBpZiAoZ3JvdXAucGVuZGluZy5zaXplID09PSAwKSByZXR1cm47XG4gICAgY29uc3Qga2V5cyA9IFsuLi5ncm91cC5wZW5kaW5nXTtcbiAgICBncm91cC5wZW5kaW5nLmNsZWFyKCk7XG4gICAgZm9yIChjb25zdCBzdGF0ZSBvZiBncm91cC5zY29wZXMpIHtcbiAgICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHN0YXRlLmVudHJpZXMuZGVsZXRlKGtleSk7XG4gICAgfVxuICB9XG59XG5cbi8qKiBBcHBLaXQgcGx1Z2luIHRoYXQgb3ducyBwcm9jZXNzLWxvY2FsIGZpbGVzeXN0ZW0gTFJVIGNhY2hlcy4gKi9cbmV4cG9ydCBjbGFzcyBGaWxlc0NhY2hlUGx1Z2luIGV4dGVuZHMgUGx1Z2luPEZpbGVzQ2FjaGVQbHVnaW5Db25maWc+IHtcbiAgc3RhdGljIG1hbmlmZXN0ID0ge1xuICAgIG5hbWU6IFwiZmlsZXMtY2FjaGVcIixcbiAgICBkaXNwbGF5TmFtZTogXCJGaWxlcyBDYWNoZVwiLFxuICAgIGRlc2NyaXB0aW9uOiBcIlByb3ZpZGVzIGJvdW5kZWQgdXNlci1zY29wZWQgcHJvY2Vzcy1sb2NhbCBmaWxlc3lzdGVtIGNhY2hlcy5cIixcbiAgICBzdGFiaWxpdHk6IFwiYmV0YVwiLFxuICAgIHJlc291cmNlczogeyByZXF1aXJlZDogW10sIG9wdGlvbmFsOiBbXSB9LFxuICB9IHNhdGlzZmllcyBQbHVnaW5NYW5pZmVzdDxcImZpbGVzLWNhY2hlXCI+O1xuXG4gIHByaXZhdGUgbWFuYWdlcj86IEZpbGVzQ2FjaGVNYW5hZ2VyO1xuXG4gIG92ZXJyaWRlIGFzeW5jIHNldHVwKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubWFuYWdlciA9IG5ldyBGaWxlc0NhY2hlTWFuYWdlcih0aGlzLmNvbmZpZyk7XG4gICAgdGhpcy5jb250ZXh0Py5vbkxpZmVjeWNsZShcInNodXRkb3duXCIsICgpID0+IHRoaXMubWFuYWdlcj8uY2xvc2UoKSk7XG4gIH1cblxuICBvdmVycmlkZSBleHBvcnRzKCk6IEZpbGVzQ2FjaGVFeHBvcnRzIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9yU2NvcGU6IChzY29wZSkgPT4gdGhpcy5yZXF1aXJlTWFuYWdlcigpLmZvclNjb3BlKHNjb3BlKSxcbiAgICAgIGZvckZpbGVTeXN0ZW06IChzY29wZSwgaWRlbnRpdHksIGxvYWQpID0+XG4gICAgICAgIHRoaXMucmVxdWlyZU1hbmFnZXIoKS5mb3JGaWxlU3lzdGVtKHNjb3BlLCBpZGVudGl0eSwgbG9hZCksXG4gICAgICBmbHVzaDogKCkgPT4gdGhpcy5yZXF1aXJlTWFuYWdlcigpLmZsdXNoKCksXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgcmVxdWlyZU1hbmFnZXIoKTogRmlsZXNDYWNoZU1hbmFnZXIge1xuICAgIGlmICghdGhpcy5tYW5hZ2VyKSB0aHJvdyBuZXcgRXJyb3IoXCJGaWxlcyBjYWNoZSBwbHVnaW4gaGFzIG5vdCBjb21wbGV0ZWQgc2V0dXBcIik7XG4gICAgcmV0dXJuIHRoaXMubWFuYWdlcjtcbiAgfVxufVxuXG4vKiogQXBwS2l0IHJlZ2lzdHJhdGlvbiBmYWN0b3J5IGZvciB7QGxpbmsgRmlsZXNDYWNoZVBsdWdpbn0uICovXG5leHBvcnQgY29uc3QgZmlsZXNDYWNoZSA9IHRvUGx1Z2luKEZpbGVzQ2FjaGVQbHVnaW4pO1xuXG5mdW5jdGlvbiByZXNvbHZlU2NvcGUoaW5wdXQ6IEZpbGVzQ2FjaGVTY29wZSwgZGVmYXVsdFR0bE1zOiBudW1iZXIpOiBSZXNvbHZlZEZpbGVzQ2FjaGVTY29wZSB7XG4gIGNvbnN0IGhvc3QgPSBpbnB1dC5ob3N0LnRyaW0oKS5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpO1xuICBjb25zdCB1c2VyS2V5ID0gaW5wdXQudXNlcktleS50cmltKCk7XG4gIGNvbnN0IHdvcmtzcGFjZUlkID0gaW5wdXQud29ya3NwYWNlSWQ/LnRyaW0oKSB8fCB1bmRlZmluZWQ7XG4gIGlmICghaG9zdCkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkZpbGVzIGNhY2hlIHNjb3BlIGhvc3QgbXVzdCBub3QgYmUgYmxhbmtcIik7XG4gIGlmICghdXNlcktleSkgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkZpbGVzIGNhY2hlIHNjb3BlIHVzZXJLZXkgbXVzdCBub3QgYmUgYmxhbmtcIik7XG4gIHJldHVybiB7XG4gICAgaG9zdCxcbiAgICB1c2VyS2V5LFxuICAgIC4uLih3b3Jrc3BhY2VJZCA/IHsgd29ya3NwYWNlSWQgfSA6IHt9KSxcbiAgICB0dGxNczogcG9zaXRpdmVEdXJhdGlvbihpbnB1dC50dGxNcywgZGVmYXVsdFR0bE1zLCBcInNjb3BlLnR0bE1zXCIpLFxuICB9O1xufVxuXG5mdW5jdGlvbiBwb3NpdGl2ZUR1cmF0aW9uKHZhbHVlOiBudW1iZXIgfCB1bmRlZmluZWQsIGZhbGxiYWNrOiBudW1iZXIsIG5hbWU6IHN0cmluZyk6IG51bWJlciB7XG4gIGNvbnN0IHJlc29sdmVkID0gdmFsdWUgPz8gZmFsbGJhY2s7XG4gIGlmICghTnVtYmVyLmlzRmluaXRlKHJlc29sdmVkKSB8fCByZXNvbHZlZCA8PSAwKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoYCR7bmFtZX0gbXVzdCBiZSBhIGZpbml0ZSBwb3NpdGl2ZSBudW1iZXJgKTtcbiAgfVxuICByZXR1cm4gcmVzb2x2ZWQ7XG59XG5cbmZ1bmN0aW9uIHBvc2l0aXZlSW50ZWdlcih2YWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkLCBmYWxsYmFjazogbnVtYmVyLCBuYW1lOiBzdHJpbmcpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5mbG9vcihwb3NpdGl2ZUR1cmF0aW9uKHZhbHVlLCBmYWxsYmFjaywgbmFtZSkpO1xufVxuXG5mdW5jdGlvbiByZXRhaW5lZFNpemUoa2V5OiBzdHJpbmcsIGVudHJ5OiBSZXRhaW5lZEVudHJ5KTogbnVtYmVyIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gQnVmZmVyLmJ5dGVMZW5ndGgoa2V5KSArIEJ1ZmZlci5ieXRlTGVuZ3RoKEpTT04uc3RyaW5naWZ5KGVudHJ5LnZhbHVlKSk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBOdW1iZXIuTUFYX1NBRkVfSU5URUdFUjtcbiAgfVxufVxuIl19
@@ -1 +1 @@
1
- {"root":["../src/_auto-configure.ts","../src/_cache-storage.ts","../src/appkit.ts","../src/brand-context.ts","../src/bundle.ts","../src/child-process.ts","../src/databricks.ts","../src/identity.ts","../src/interceptor.ts","../src/lakebase-resolver.ts","../src/migration.ts","../src/pgaddress.ts","../src/plugin-execution.ts","../src/plugin-registry.ts","../src/provision.ts","../src/request-tags.ts","../src/tool-provider.ts","../src/toolkit-entries.ts","../index.ts"],"version":"5.9.3"}
1
+ {"root":["../src/_auto-configure.ts","../src/_cache-storage.ts","../src/appkit.ts","../src/brand-context.ts","../src/bundle.ts","../src/child-process.ts","../src/databricks.ts","../src/files-cache.ts","../src/identity.ts","../src/interceptor.ts","../src/lakebase-resolver.ts","../src/migration.ts","../src/pgaddress.ts","../src/plugin-execution.ts","../src/plugin-registry.ts","../src/provision.ts","../src/request-tags.ts","../src/tool-provider.ts","../src/toolkit-entries.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "devDependencies": {
22
22
  "@databricks/appkit": "0.81.0",
23
- "@dbx-tools/projen": "^0.9.93",
23
+ "@dbx-tools/projen": "^0.9.95",
24
24
  "@types/node": "^24.6.0",
25
25
  "typescript": "^5.9.3",
26
26
  "vitest": "3.2.4"
@@ -30,11 +30,12 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@databricks/sdk-experimental": "^0.17.0",
33
- "@dbx-tools/auth": "^0.9.93",
34
- "@dbx-tools/core": "^0.9.93",
35
- "@dbx-tools/lakebase": "^0.9.93",
36
- "@dbx-tools/postgres": "^0.9.93",
37
- "@dbx-tools/shared-core": "^0.9.93",
33
+ "@dbx-tools/auth": "^0.9.95",
34
+ "@dbx-tools/core": "^0.9.95",
35
+ "@dbx-tools/lakebase": "^0.9.95",
36
+ "@dbx-tools/postgres": "^0.9.95",
37
+ "@dbx-tools/shared-core": "^0.9.95",
38
+ "@dbx-tools/shared-fs": "^0.9.95",
38
39
  "lru-cache": "11.5.3",
39
40
  "zod": "4.3.6"
40
41
  },
@@ -69,6 +70,10 @@
69
70
  "types": "./lib/src/databricks.d.ts",
70
71
  "default": "./lib/src/databricks.js"
71
72
  },
73
+ "./files-cache": {
74
+ "types": "./lib/src/files-cache.d.ts",
75
+ "default": "./lib/src/files-cache.js"
76
+ },
72
77
  "./identity": {
73
78
  "types": "./lib/src/identity.d.ts",
74
79
  "default": "./lib/src/identity.js"
@@ -116,7 +121,7 @@
116
121
  "./package.json": "./package.json"
117
122
  }
118
123
  },
119
- "version": "0.9.93",
124
+ "version": "0.9.95",
120
125
  "types": "./lib/index.d.ts",
121
126
  "dbxToolsConfig": {
122
127
  "tags": [
@@ -149,6 +154,10 @@
149
154
  "types": "./lib/src/databricks.d.ts",
150
155
  "default": "./lib/src/databricks.js"
151
156
  },
157
+ "./files-cache": {
158
+ "types": "./lib/src/files-cache.d.ts",
159
+ "default": "./lib/src/files-cache.js"
160
+ },
152
161
  "./identity": {
153
162
  "types": "./lib/src/identity.d.ts",
154
163
  "default": "./lib/src/identity.js"
@@ -40,6 +40,7 @@ export interface L1CacheStorageOptions {
40
40
 
41
41
  /** AppKit's internal persistent storage surface. */
42
42
  export type PersistentStorageBase = CacheStorage & {
43
+ cleanupExpired?(): Promise<number>;
43
44
  initialize(): Promise<void>;
44
45
  initialized: boolean;
45
46
  schemaName?: string;
@@ -113,6 +114,16 @@ export class L1CacheStorage implements CacheStorage {
113
114
  await this.storage.close();
114
115
  }
115
116
 
117
+ /** Forward AppKit's runtime-only persistent cleanup hook when available. */
118
+ async cleanupExpired(): Promise<number> {
119
+ const cleanup = (
120
+ this.storage as CacheStorage & {
121
+ cleanupExpired?: () => Promise<number>;
122
+ }
123
+ ).cleanupExpired;
124
+ return cleanup ? cleanup.call(this.storage) : 0;
125
+ }
126
+
116
127
  private retain<T>(key: string, entry: CacheEntry<T>): void {
117
128
  const ttl = entry.expiry - Date.now();
118
129
  if (ttl > 0) this.entries.set(key, entry, { ttl });
@@ -0,0 +1,332 @@
1
+ /**
2
+ * AppKit-global filesystem cache with user-isolated process-local LRU values.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { Plugin, toPlugin, type BasePluginConfig, type PluginManifest } from "@databricks/appkit";
8
+ import { posixPath, type CacheValue, type FileSystemCache } from "@dbx-tools/shared-fs";
9
+ import { errorUtils, log, object } from "@dbx-tools/shared-core";
10
+ import { LRUCache } from "lru-cache";
11
+
12
+ const DEFAULT_TTL_MS = 60_000;
13
+ const DEFAULT_INVALIDATION_BATCH_MS = 25;
14
+ const DEFAULT_MAX_ENTRIES = 1_000;
15
+ const DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
16
+ const DEFAULT_MAX_SCOPES = 256;
17
+ const logger = log.logger("files-cache");
18
+
19
+ interface RetainedEntry {
20
+ value: CacheValue;
21
+ }
22
+
23
+ interface ScopeState {
24
+ cache: FileSystemCache;
25
+ entries: LRUCache<string, RetainedEntry>;
26
+ group: GroupState;
27
+ scope: ResolvedFilesCacheScope;
28
+ sources: Map<string, Promise<object>>;
29
+ }
30
+
31
+ interface GroupState {
32
+ pending: Set<string>;
33
+ scopes: Set<ScopeState>;
34
+ timer?: ReturnType<typeof setTimeout>;
35
+ }
36
+
37
+ interface ResolvedFilesCacheConfig {
38
+ invalidationBatchMs: number;
39
+ maxBytes: number;
40
+ maxEntries: number;
41
+ maxScopes: number;
42
+ ttlMs: number;
43
+ }
44
+
45
+ interface ResolvedFilesCacheScope {
46
+ host: string;
47
+ ttlMs: number;
48
+ userKey: string;
49
+ workspaceId?: string;
50
+ }
51
+
52
+ /** Configuration for the AppKit files-cache plugin. */
53
+ export interface FilesCachePluginConfig extends BasePluginConfig {
54
+ /** Default entry TTL in milliseconds. Defaults to 60 seconds. */
55
+ ttlMs?: number;
56
+ /** Invalidation debounce window in milliseconds. Defaults to 25ms. */
57
+ invalidationBatchMs?: number;
58
+ /** Maximum entries retained in each user L1. Defaults to 1,000. */
59
+ maxEntries?: number;
60
+ /** Maximum retained bytes in each user L1. Defaults to 64 MiB. */
61
+ maxBytes?: number;
62
+ /** Maximum active user scopes retained by the process. Defaults to 256. */
63
+ maxScopes?: number;
64
+ }
65
+
66
+ /** Identity for one authorization-safe filesystem cache scope. */
67
+ export interface FilesCacheScope {
68
+ /** Databricks workspace host. */
69
+ host: string;
70
+ /** Attributed user identifier used to isolate values. */
71
+ userKey: string;
72
+ /** Optional workspace identifier included in shared invalidation scope. */
73
+ workspaceId?: string;
74
+ /** Optional per-scope entry TTL override in milliseconds. */
75
+ ttlMs?: number;
76
+ }
77
+
78
+ /** Stable identity for a framework filesystem source retained within one user scope. */
79
+ export interface FilesCacheSourceIdentity {
80
+ /** Actual mounted filesystem paths represented by the source. */
81
+ paths: readonly string[];
82
+ }
83
+
84
+ /** Public exports from {@link FilesCachePlugin}. */
85
+ export interface FilesCacheExports {
86
+ /** Resolve one user-isolated filesystem cache. */
87
+ forScope(scope: FilesCacheScope): Promise<FileSystemCache>;
88
+ /** Resolve one app-lifetime filesystem source for a user and its actual mounted paths. */
89
+ forFileSystem<T extends object>(
90
+ scope: FilesCacheScope,
91
+ identity: FilesCacheSourceIdentity,
92
+ load: () => T | Promise<T>,
93
+ ): Promise<T>;
94
+ /** Flush pending invalidation batches. */
95
+ flush(): Promise<void>;
96
+ }
97
+
98
+ /** Process-local filesystem cache manager. */
99
+ export class FilesCacheManager {
100
+ private readonly config: ResolvedFilesCacheConfig;
101
+ private readonly groups = new Map<string, GroupState>();
102
+ private readonly scopes: LRUCache<string, ScopeState>;
103
+
104
+ constructor(config: FilesCachePluginConfig = {}) {
105
+ this.config = {
106
+ ttlMs: positiveDuration(config.ttlMs, DEFAULT_TTL_MS, "ttlMs"),
107
+ invalidationBatchMs: positiveDuration(
108
+ config.invalidationBatchMs,
109
+ DEFAULT_INVALIDATION_BATCH_MS,
110
+ "invalidationBatchMs",
111
+ ),
112
+ maxEntries: positiveInteger(config.maxEntries, DEFAULT_MAX_ENTRIES, "maxEntries"),
113
+ maxBytes: positiveInteger(config.maxBytes, DEFAULT_MAX_BYTES, "maxBytes"),
114
+ maxScopes: positiveInteger(config.maxScopes, DEFAULT_MAX_SCOPES, "maxScopes"),
115
+ };
116
+ this.scopes = new LRUCache({
117
+ max: this.config.maxScopes,
118
+ dispose: (state) => {
119
+ state.group.scopes.delete(state);
120
+ },
121
+ });
122
+ }
123
+
124
+ /** Return one user-isolated cache within a host/workspace invalidation group. */
125
+ async forScope(input: FilesCacheScope): Promise<FileSystemCache> {
126
+ return (await this.resolveScopeState(input)).cache;
127
+ }
128
+
129
+ /** Retain one stable framework filesystem source for the scope and mounted paths. */
130
+ async forFileSystem<T extends object>(
131
+ input: FilesCacheScope,
132
+ identity: FilesCacheSourceIdentity,
133
+ load: () => T | Promise<T>,
134
+ ): Promise<T> {
135
+ const state = await this.resolveScopeState(input);
136
+ const paths = identity.paths.map((path) => posixPath.normalizeRoot(path));
137
+ if (paths.length === 0) throw new TypeError("Filesystem source paths must not be empty");
138
+ const sourceKey = object.toStableKey({ paths });
139
+ let source = state.sources.get(sourceKey);
140
+ if (!source) {
141
+ source = Promise.resolve(load());
142
+ state.sources.set(sourceKey, source);
143
+ }
144
+ try {
145
+ return (await source) as T;
146
+ } catch (error) {
147
+ if (state.sources.get(sourceKey) === source) state.sources.delete(sourceKey);
148
+ throw error;
149
+ }
150
+ }
151
+
152
+ private async resolveScopeState(input: FilesCacheScope): Promise<ScopeState> {
153
+ const scope = resolveScope(input, this.config.ttlMs);
154
+ const scopeKey = object.toStableKey({
155
+ host: scope.host,
156
+ userKey: scope.userKey,
157
+ workspaceId: scope.workspaceId,
158
+ });
159
+ let state = this.scopes.get(scopeKey);
160
+ if (!state) {
161
+ const group = this.group(scope);
162
+ state = this.createScope(scope, group);
163
+ group.scopes.add(state);
164
+ this.scopes.set(scopeKey, state);
165
+ }
166
+ return state;
167
+ }
168
+
169
+ /** Flush every pending invalidation batch. */
170
+ async flush(): Promise<void> {
171
+ await Promise.all([...this.groups.values()].map((group) => this.flushInvalidations(group)));
172
+ }
173
+
174
+ /** Flush state and release all process-local entries and timers. */
175
+ async close(): Promise<void> {
176
+ for (const group of this.groups.values()) {
177
+ if (group.timer) clearTimeout(group.timer);
178
+ group.timer = undefined;
179
+ }
180
+ await this.flush();
181
+ this.scopes.clear();
182
+ this.groups.clear();
183
+ }
184
+
185
+ private group(scope: ResolvedFilesCacheScope): GroupState {
186
+ const key = object.toStableKey({
187
+ host: scope.host,
188
+ workspaceId: scope.workspaceId,
189
+ });
190
+ const existing = this.groups.get(key);
191
+ if (existing) return existing;
192
+ const group: GroupState = {
193
+ pending: new Set(),
194
+ scopes: new Set(),
195
+ };
196
+ this.groups.set(key, group);
197
+ return group;
198
+ }
199
+
200
+ private createScope(scope: ResolvedFilesCacheScope, group: GroupState): ScopeState {
201
+ const state = {} as ScopeState;
202
+ state.scope = scope;
203
+ state.group = group;
204
+ state.entries = new LRUCache({
205
+ max: this.config.maxEntries,
206
+ maxSize: this.config.maxBytes,
207
+ sizeCalculation: (entry, cacheKey) => retainedSize(cacheKey, entry),
208
+ });
209
+ state.sources = new Map();
210
+ state.cache = {
211
+ read: <T extends CacheValue>(cacheKey: string, load: () => T | Promise<T>) =>
212
+ this.read(state, cacheKey, load),
213
+ invalidate: (cacheKey: string) => this.queueInvalidation(state, cacheKey),
214
+ keys: () => this.keys(state),
215
+ };
216
+ return state;
217
+ }
218
+
219
+ private async read<T extends CacheValue>(
220
+ state: ScopeState,
221
+ key: string,
222
+ load: () => T | Promise<T>,
223
+ ): Promise<T> {
224
+ if (!state.group.pending.has(key)) {
225
+ const retained = state.entries.get(key);
226
+ if (retained) return retained.value as T;
227
+ }
228
+ const value = await load();
229
+ state.entries.set(key, { value }, { ttl: state.scope.ttlMs });
230
+ return value;
231
+ }
232
+
233
+ private *keys(state: ScopeState): Iterable<string> {
234
+ for (const key of state.entries.keys()) {
235
+ if (!state.group.pending.has(key)) yield key;
236
+ }
237
+ }
238
+
239
+ private queueInvalidation(state: ScopeState, key: string): void {
240
+ state.group.pending.add(key);
241
+ if (state.group.timer) return;
242
+ state.group.timer = setTimeout(() => {
243
+ state.group.timer = undefined;
244
+ void this.flushInvalidations(state.group).catch((error) => {
245
+ logger.warn("filesystem cache invalidation batch failed", {
246
+ error: errorUtils.errorMessage(error),
247
+ });
248
+ });
249
+ }, this.config.invalidationBatchMs);
250
+ state.group.timer.unref?.();
251
+ }
252
+
253
+ private async flushInvalidations(group: GroupState): Promise<void> {
254
+ if (group.timer) clearTimeout(group.timer);
255
+ group.timer = undefined;
256
+ if (group.pending.size === 0) return;
257
+ const keys = [...group.pending];
258
+ group.pending.clear();
259
+ for (const state of group.scopes) {
260
+ for (const key of keys) state.entries.delete(key);
261
+ }
262
+ }
263
+ }
264
+
265
+ /** AppKit plugin that owns process-local filesystem LRU caches. */
266
+ export class FilesCachePlugin extends Plugin<FilesCachePluginConfig> {
267
+ static manifest = {
268
+ name: "files-cache",
269
+ displayName: "Files Cache",
270
+ description: "Provides bounded user-scoped process-local filesystem caches.",
271
+ stability: "beta",
272
+ resources: { required: [], optional: [] },
273
+ } satisfies PluginManifest<"files-cache">;
274
+
275
+ private manager?: FilesCacheManager;
276
+
277
+ override async setup(): Promise<void> {
278
+ this.manager = new FilesCacheManager(this.config);
279
+ this.context?.onLifecycle("shutdown", () => this.manager?.close());
280
+ }
281
+
282
+ override exports(): FilesCacheExports {
283
+ return {
284
+ forScope: (scope) => this.requireManager().forScope(scope),
285
+ forFileSystem: (scope, identity, load) =>
286
+ this.requireManager().forFileSystem(scope, identity, load),
287
+ flush: () => this.requireManager().flush(),
288
+ };
289
+ }
290
+
291
+ private requireManager(): FilesCacheManager {
292
+ if (!this.manager) throw new Error("Files cache plugin has not completed setup");
293
+ return this.manager;
294
+ }
295
+ }
296
+
297
+ /** AppKit registration factory for {@link FilesCachePlugin}. */
298
+ export const filesCache = toPlugin(FilesCachePlugin);
299
+
300
+ function resolveScope(input: FilesCacheScope, defaultTtlMs: number): ResolvedFilesCacheScope {
301
+ const host = input.host.trim().replace(/\/+$/, "");
302
+ const userKey = input.userKey.trim();
303
+ const workspaceId = input.workspaceId?.trim() || undefined;
304
+ if (!host) throw new TypeError("Files cache scope host must not be blank");
305
+ if (!userKey) throw new TypeError("Files cache scope userKey must not be blank");
306
+ return {
307
+ host,
308
+ userKey,
309
+ ...(workspaceId ? { workspaceId } : {}),
310
+ ttlMs: positiveDuration(input.ttlMs, defaultTtlMs, "scope.ttlMs"),
311
+ };
312
+ }
313
+
314
+ function positiveDuration(value: number | undefined, fallback: number, name: string): number {
315
+ const resolved = value ?? fallback;
316
+ if (!Number.isFinite(resolved) || resolved <= 0) {
317
+ throw new RangeError(`${name} must be a finite positive number`);
318
+ }
319
+ return resolved;
320
+ }
321
+
322
+ function positiveInteger(value: number | undefined, fallback: number, name: string): number {
323
+ return Math.floor(positiveDuration(value, fallback, name));
324
+ }
325
+
326
+ function retainedSize(key: string, entry: RetainedEntry): number {
327
+ try {
328
+ return Buffer.byteLength(key) + Buffer.byteLength(JSON.stringify(entry.value));
329
+ } catch {
330
+ return Number.MAX_SAFE_INTEGER;
331
+ }
332
+ }