@dbx-tools/fs 0.6.10

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 ADDED
@@ -0,0 +1,60 @@
1
+ # `@dbx-tools/fs`
2
+
3
+ Node local-disk `FileSystem` implementation of the `@dbx-tools/shared-fs`
4
+ contract. Built on `BaseFileSystem`, so this package only owns host separator
5
+ conversion (`toBackendPath`), Node I/O, symlink containment (`preparePath`), and
6
+ errno mapping.
7
+
8
+ Key features:
9
+
10
+ - Contained-by-default path resolution under a configured root
11
+ - `~` / `~/...` roots expand via `os.homedir()` (before `HOME`), then
12
+ Databricks App `/home/app`, then a created `./.home` (create failures skip)
13
+ - Temp dirs via `os.tmpdir()` then `TMPDIR` / `TMP` / `TEMP`, else
14
+ `<home>/.tmp` (each candidate must pass a write/delete probe)
15
+ - Read/write/append/copy/move plus mkdir/rmdir/readdir/stat/exists
16
+ - Optional read-only mode
17
+ - Native append / copy / rename via Node when available
18
+
19
+ ## Why use this over native Node `fs`
20
+
21
+ Use this when callers should speak the portable `FileSystem` interface (the same
22
+ surface FTP, object storage, or Databricks mounts can implement) rather than
23
+ Node APIs directly. Reach for `node:fs` when you only need one-off local I/O.
24
+
25
+ ## Quick start
26
+
27
+ ```ts
28
+ import { LocalFileSystem, localFS } from "@dbx-tools/fs";
29
+
30
+ const local = new LocalFileSystem({ root: "./data" });
31
+ await local.init();
32
+ await local.writeFile("hello.txt", "hi");
33
+
34
+ const home = localFS.homeFS("projects/data");
35
+ const scratch = localFS.tmpFS("job-42");
36
+
37
+ // A throwaway working directory no other run can collide with.
38
+ const work = localFS.scratchFS("import-job");
39
+ await work.init(); // only needed when handing the root to `node:fs` or a subprocess
40
+
41
+ // Regenerate on every boot, but into ONE stable directory: the callback writes
42
+ // into a scratch root that replaces the stable one only on success, so restarts
43
+ // don't pile up directories and a failed rebuild keeps the last good tree.
44
+ const tools = await localFS.rebuildFS("databricks-aitools", (scratch) => installInto(scratch.root));
45
+ ```
46
+
47
+ ## Module map
48
+
49
+ | Export | Role |
50
+ | ---------------------------------- | ------------------------------------------------------ |
51
+ | `LocalFileSystem` | Local-disk `FileSystem<"local">` |
52
+ | `LocalFileSystemOptions` | Constructor options |
53
+ | `localFS.homeFS` / `localFS.tmpFS` | `LocalFileSystem` under resolved home / temp |
54
+ | `localFS.scratchFS` | `tmpFS` on a `<prefix>-<id>` root unique per call |
55
+ | `localFS.rebuildFS` | Rebuild a stable temp tree via an atomic scratch swap |
56
+ | `osPath.resolveLocalHome` | Home: `homedir` → `HOME` → `/home/app` → `./.home` |
57
+ | `osPath.resolveLocalTemp` | Temp: `tmpdir` → `TMPDIR`/`TMP`/`TEMP` → `<home>/.tmp` |
58
+ | `localPath.expandLocalHomePath` | Expand `~` against a home dir |
59
+
60
+ Adjacent: `@dbx-tools/shared-fs` (`FileSystem` contract + `BaseFileSystem`).
package/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+
5
+ export * as localFS from "./src/local-fs.ts";
6
+ export * as localPath from "./src/local-path.ts";
7
+ export * as osPath from "./src/os-path.ts";
8
+ export { LocalFileSystem } from "./src/local-fs.ts";
9
+ export type { LocalFileSystemOptions, OsFileSystemOptions } from "./src/local-fs.ts";
10
+ export { isHomeRelativePath } from "./src/local-path.ts";
11
+ export { APP_HOME } from "./src/os-path.ts";
12
+ export type { OsPaths, ResolveOsPathsOptions } from "./src/os-path.ts";
package/lib/index.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * as localFS from "./src/local-fs.ts";
2
+ export * as localPath from "./src/local-path.ts";
3
+ export * as osPath from "./src/os-path.ts";
4
+ export { LocalFileSystem } from "./src/local-fs.ts";
5
+ export type { LocalFileSystemOptions, OsFileSystemOptions } from "./src/local-fs.ts";
6
+ export { isHomeRelativePath } from "./src/local-path.ts";
7
+ export { APP_HOME } from "./src/os-path.ts";
8
+ export type { OsPaths, ResolveOsPathsOptions } from "./src/os-path.ts";
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+ export * as localFS from "./src/local-fs.js";
5
+ export * as localPath from "./src/local-path.js";
6
+ export * as osPath from "./src/os-path.js";
7
+ export { LocalFileSystem } from "./src/local-fs.js";
8
+ export { isHomeRelativePath } from "./src/local-path.js";
9
+ export { APP_HOME } from "./src/os-path.js";
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQzdDLE9BQU8sS0FBSyxTQUFTLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxLQUFLLE1BQU0sTUFBTSxrQkFBa0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFcEQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDekQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0ICogYXMgbG9jYWxGUyBmcm9tIFwiLi9zcmMvbG9jYWwtZnMudHNcIjtcbmV4cG9ydCAqIGFzIGxvY2FsUGF0aCBmcm9tIFwiLi9zcmMvbG9jYWwtcGF0aC50c1wiO1xuZXhwb3J0ICogYXMgb3NQYXRoIGZyb20gXCIuL3NyYy9vcy1wYXRoLnRzXCI7XG5leHBvcnQgeyBMb2NhbEZpbGVTeXN0ZW0gfSBmcm9tIFwiLi9zcmMvbG9jYWwtZnMudHNcIjtcbmV4cG9ydCB0eXBlIHsgTG9jYWxGaWxlU3lzdGVtT3B0aW9ucywgT3NGaWxlU3lzdGVtT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9sb2NhbC1mcy50c1wiO1xuZXhwb3J0IHsgaXNIb21lUmVsYXRpdmVQYXRoIH0gZnJvbSBcIi4vc3JjL2xvY2FsLXBhdGgudHNcIjtcbmV4cG9ydCB7IEFQUF9IT01FIH0gZnJvbSBcIi4vc3JjL29zLXBhdGgudHNcIjtcbmV4cG9ydCB0eXBlIHsgT3NQYXRocywgUmVzb2x2ZU9zUGF0aHNPcHRpb25zIH0gZnJvbSBcIi4vc3JjL29zLXBhdGgudHNcIjtcbiJdfQ==
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Host-local {@link FileSystem} backed by Node's `node:fs/promises`.
3
+ *
4
+ * Extends {@link BaseFileSystem} so this module only owns Node I/O, host path
5
+ * conversion, and symlink containment. Portable behavior (encoding, recursion,
6
+ * parent creation, fallbacks, POSIX namespace paths) lives in `@dbx-tools/shared-fs`.
7
+ *
8
+ * @module
9
+ */
10
+ import { BaseFileSystem, FileSystemError, type CopyOptions, type FileEntry, type FileStat, type WriteFileOptions } from "@dbx-tools/shared-fs";
11
+ import { type ResolveOsPathsOptions } from "./os-path.ts";
12
+ /** Options for {@link LocalFileSystem}. */
13
+ export interface LocalFileSystemOptions {
14
+ /** Unique identifier. Defaults to a stable hash of the absolute root. */
15
+ id?: string;
16
+ /**
17
+ * Root directory on disk. Relative paths resolve against `process.cwd()`.
18
+ * `~` / `~/...` expand via `os.homedir()` (before `HOME`), App `/home/app`,
19
+ * or a created `./.home` (create failures skip).
20
+ * Stored on {@link LocalFileSystem.root} in POSIX form.
21
+ */
22
+ root: string;
23
+ /** Block all write operations. Defaults to false. */
24
+ readOnly?: boolean;
25
+ /**
26
+ * Keep every resolved path under {@link root}. Defaults to true.
27
+ * When false, absolute input paths are accepted as-is (no sandbox).
28
+ */
29
+ contained?: boolean;
30
+ /**
31
+ * Create {@link root} (and parents) during init. Defaults to true.
32
+ */
33
+ createRoot?: boolean;
34
+ }
35
+ /**
36
+ * Options for the factories that take their root FROM the OS
37
+ * ({@link homeFS} / {@link tmpFS} / {@link scratchFS}) rather than from the
38
+ * caller: {@link LocalFileSystemOptions} without `root`, plus the
39
+ * {@link resolveOsPaths} injectables.
40
+ */
41
+ export type OsFileSystemOptions = Omit<LocalFileSystemOptions, "root"> & {
42
+ /** Forwarded to {@link resolveLocalHome} / {@link resolveLocalTemp}. */
43
+ os?: ResolveOsPathsOptions;
44
+ };
45
+ /**
46
+ * {@link FileSystem} implementation that stores files in a folder on the local
47
+ * machine.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const fs = new LocalFileSystem({ root: "./data" });
52
+ * await fs.init();
53
+ * await fs.writeFile("hello.txt", "hi");
54
+ *
55
+ * const home = new LocalFileSystem({ root: "~/projects/data" });
56
+ * const scratch = tmpFS("my-job");
57
+ * const cache = homeFS(".cache/app");
58
+ * ```
59
+ */
60
+ export declare class LocalFileSystem extends BaseFileSystem<"local"> {
61
+ private readonly contained;
62
+ /** Symlink-resolved host root, refreshed in {@link onInit}. */
63
+ private realRoot;
64
+ constructor(options: LocalFileSystemOptions);
65
+ protected toBackendPath(posixBackendPath: string): string;
66
+ protected createRootDirectory(): Promise<void>;
67
+ protected onInit(): Promise<void>;
68
+ resolvePath(inputPath: string): string;
69
+ /**
70
+ * When contained, resolve symlinks and ensure the real path still sits under
71
+ * {@link root}. Missing leaf paths are allowed when `allowMissing` is set so
72
+ * writes can create new files.
73
+ */
74
+ protected preparePath(resolvedPath: string, options?: {
75
+ allowMissing?: boolean;
76
+ }): Promise<string>;
77
+ /** Classify Node errno codes; {@link BaseFileSystem} wraps every primitive. */
78
+ protected mapError(err: unknown, filePath: string): FileSystemError;
79
+ protected readBytesAt(resolvedPath: string): Promise<Uint8Array>;
80
+ protected writeBytesAt(resolvedPath: string, content: Uint8Array, options: Required<WriteFileOptions>): Promise<void>;
81
+ protected deleteFileAt(resolvedPath: string): Promise<void>;
82
+ protected createDirectoryAt(resolvedPath: string): Promise<void>;
83
+ protected removeDirectoryAt(resolvedPath: string): Promise<void>;
84
+ protected listDirectoryAt(resolvedPath: string): Promise<FileEntry[]>;
85
+ protected statAt(resolvedPath: string): Promise<Omit<FileStat, "path">>;
86
+ protected isNotFoundError(error: unknown): boolean;
87
+ protected tryAppendFileAt(resolvedPath: string, content: Uint8Array): Promise<boolean>;
88
+ protected tryCopyFileAt(sourcePath: string, destinationPath: string, options: Required<CopyOptions>): Promise<boolean>;
89
+ protected tryMoveFileAt(sourcePath: string, destinationPath: string, options: Required<CopyOptions>): Promise<boolean>;
90
+ }
91
+ /**
92
+ * {@link LocalFileSystem} rooted under {@link resolveLocalHome}, with
93
+ * {@link relativeRoot} joined as a relative path (leading `/` / `~` stripped
94
+ * so the result stays under home).
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const cache = homeFS(".cache/my-app");
99
+ * await cache.writeFile("state.json", "{}");
100
+ * ```
101
+ */
102
+ export declare function homeFS(relativeRoot?: string, options?: OsFileSystemOptions): LocalFileSystem;
103
+ /**
104
+ * {@link LocalFileSystem} rooted under {@link resolveLocalTemp}, with
105
+ * {@link relativeRoot} joined as a relative path (leading `/` / `~` stripped
106
+ * so the result stays under temp).
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const scratch = tmpFS("job-42");
111
+ * await scratch.writeFile("out.bin", bytes);
112
+ * ```
113
+ */
114
+ export declare function tmpFS(relativeRoot?: string, options?: OsFileSystemOptions): LocalFileSystem;
115
+ /**
116
+ * {@link tmpFS} under a root that is unique per call (`<prefix>-<id>`), for a
117
+ * scratch area no other process or run can collide with.
118
+ *
119
+ * Prefer this over minting the id at the call site - it is the one place that
120
+ * decides what a throwaway working directory looks like.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * const scratch = scratchFS("remote-skills");
125
+ * await scratch.writeFile("SKILL.md", body);
126
+ * ```
127
+ */
128
+ export declare function scratchFS(prefix: string, options?: OsFileSystemOptions): LocalFileSystem;
129
+ /**
130
+ * Rebuild the STABLE temp tree named by {@link key}, every call.
131
+ *
132
+ * {@link materialize} writes into a throwaway {@link scratchFS} root; only on
133
+ * success does that root REPLACE the stable one. Two properties fall out of
134
+ * that ordering, and both are the reason to prefer this over writing into the
135
+ * stable path directly:
136
+ *
137
+ * - repeated runs reuse ONE directory instead of leaving a new scratch behind
138
+ * on every boot, and
139
+ * - a reader never observes a half-written tree, and a failed rebuild leaves
140
+ * the previous one intact.
141
+ *
142
+ * The swap is a `rename` within the same temp root, so it is atomic.
143
+ *
144
+ * @param key - Stable temp-relative path. May be nested (`"skills/abc123"`)
145
+ * to give each input its own directory.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * const tools = await rebuildFS("databricks-aitools", (scratch) =>
150
+ * installInto(scratch.root),
151
+ * );
152
+ * ```
153
+ */
154
+ export declare function rebuildFS(key: string, materialize: (scratch: LocalFileSystem) => Promise<void>, options?: OsFileSystemOptions): Promise<LocalFileSystem>;
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Host-local {@link FileSystem} backed by Node's `node:fs/promises`.
3
+ *
4
+ * Extends {@link BaseFileSystem} so this module only owns Node I/O, host path
5
+ * conversion, and symlink containment. Portable behavior (encoding, recursion,
6
+ * parent creation, fallbacks, POSIX namespace paths) lives in `@dbx-tools/shared-fs`.
7
+ *
8
+ * @module
9
+ */
10
+ import { appendFile, cp, lstat, mkdir, readdir, readFile, realpath, rename, rm, rmdir, writeFile, } from "node:fs/promises";
11
+ import path from "node:path";
12
+ import { hash } from "@dbx-tools/shared-core";
13
+ import { BaseFileSystem, baseFS, FileSystemError, posixPath, } from "@dbx-tools/shared-fs";
14
+ import { resolveLocalRoot } from "./local-path.js";
15
+ import { resolveLocalHome, resolveLocalTemp } from "./os-path.js";
16
+ /**
17
+ * {@link FileSystem} implementation that stores files in a folder on the local
18
+ * machine.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const fs = new LocalFileSystem({ root: "./data" });
23
+ * await fs.init();
24
+ * await fs.writeFile("hello.txt", "hi");
25
+ *
26
+ * const home = new LocalFileSystem({ root: "~/projects/data" });
27
+ * const scratch = tmpFS("my-job");
28
+ * const cache = homeFS(".cache/app");
29
+ * ```
30
+ */
31
+ export class LocalFileSystem extends BaseFileSystem {
32
+ contained;
33
+ /** Symlink-resolved host root, refreshed in {@link onInit}. */
34
+ realRoot;
35
+ constructor(options) {
36
+ const hostRoot = resolveLocalRoot(options.root);
37
+ const root = posixPath.normalizeRoot(posixPath.toPosix(hostRoot));
38
+ super({
39
+ id: options.id ?? `local-${hash.fnvHash(root)}`,
40
+ backend: "local",
41
+ root,
42
+ readOnly: options.readOnly,
43
+ createRoot: options.createRoot ?? true,
44
+ });
45
+ this.realRoot = this.toBackendPath(root);
46
+ this.contained = options.contained ?? true;
47
+ }
48
+ toBackendPath(posixBackendPath) {
49
+ return posixPath.toHost(posixBackendPath, path.sep);
50
+ }
51
+ async createRootDirectory() {
52
+ await mkdir(this.toBackendPath(this.root), { recursive: true });
53
+ }
54
+ async onInit() {
55
+ const hostRoot = this.toBackendPath(this.root);
56
+ if (!this.createRoot) {
57
+ const info = await lstat(hostRoot);
58
+ if (!info.isDirectory()) {
59
+ throw new FileSystemError("NOT_DIRECTORY", `Local filesystem root is not a directory: ${this.root}`, this.root);
60
+ }
61
+ }
62
+ // Canonicalize so containment checks survive OS root symlinks (e.g. /var -> /private/var).
63
+ this.realRoot = await realpath(hostRoot);
64
+ }
65
+ resolvePath(inputPath) {
66
+ if (!this.contained) {
67
+ const resolved = path.isAbsolute(inputPath)
68
+ ? path.resolve(inputPath)
69
+ : path.resolve(this.toBackendPath(this.root), inputPath);
70
+ return this.toBackendPath(posixPath.toPosix(resolved));
71
+ }
72
+ return super.resolvePath(inputPath);
73
+ }
74
+ /**
75
+ * When contained, resolve symlinks and ensure the real path still sits under
76
+ * {@link root}. Missing leaf paths are allowed when `allowMissing` is set so
77
+ * writes can create new files.
78
+ */
79
+ async preparePath(resolvedPath, options) {
80
+ if (!this.contained)
81
+ return resolvedPath;
82
+ try {
83
+ const real = await realpath(resolvedPath);
84
+ if (!isWithinHostRoot(this.realRoot, real)) {
85
+ throw new FileSystemError("PERMISSION_DENIED", "Path escapes the filesystem root", resolvedPath);
86
+ }
87
+ return resolvedPath;
88
+ }
89
+ catch (err) {
90
+ if (err instanceof FileSystemError)
91
+ throw err;
92
+ if (options?.allowMissing && isErrno(err, "ENOENT")) {
93
+ let cursor = path.dirname(resolvedPath);
94
+ while (cursor !== path.dirname(cursor)) {
95
+ try {
96
+ const real = await realpath(cursor);
97
+ if (!isWithinHostRoot(this.realRoot, real)) {
98
+ throw new FileSystemError("PERMISSION_DENIED", "Path escapes the filesystem root", resolvedPath);
99
+ }
100
+ return resolvedPath;
101
+ }
102
+ catch (inner) {
103
+ if (inner instanceof FileSystemError)
104
+ throw inner;
105
+ if (!isErrno(inner, "ENOENT"))
106
+ throw this.mapError(inner, resolvedPath);
107
+ cursor = path.dirname(cursor);
108
+ }
109
+ }
110
+ return resolvedPath;
111
+ }
112
+ throw this.mapError(err, resolvedPath);
113
+ }
114
+ }
115
+ /** Classify Node errno codes; {@link BaseFileSystem} wraps every primitive. */
116
+ mapError(err, filePath) {
117
+ return baseFS.mapFileSystemError(err, filePath, nodeErrorCode);
118
+ }
119
+ async readBytesAt(resolvedPath) {
120
+ const buffer = await readFile(resolvedPath);
121
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
122
+ }
123
+ async writeBytesAt(resolvedPath, content, options) {
124
+ await writeFile(resolvedPath, content, options.overwrite ? undefined : { flag: "wx" });
125
+ }
126
+ async deleteFileAt(resolvedPath) {
127
+ await rm(resolvedPath, { force: false });
128
+ }
129
+ async createDirectoryAt(resolvedPath) {
130
+ await mkdir(resolvedPath);
131
+ }
132
+ async removeDirectoryAt(resolvedPath) {
133
+ await rmdir(resolvedPath);
134
+ }
135
+ async listDirectoryAt(resolvedPath) {
136
+ const dirents = await readdir(resolvedPath, { withFileTypes: true });
137
+ const entries = [];
138
+ for (const dirent of dirents) {
139
+ const type = entryType(dirent);
140
+ let size;
141
+ if (type === "file" || type === "symbolic-link") {
142
+ try {
143
+ size = (await lstat(path.join(resolvedPath, dirent.name))).size;
144
+ }
145
+ catch {
146
+ // Race with concurrent deletes; omit size.
147
+ }
148
+ }
149
+ entries.push(size === undefined ? { name: dirent.name, type } : { name: dirent.name, type, size });
150
+ }
151
+ return entries;
152
+ }
153
+ async statAt(resolvedPath) {
154
+ const info = await lstat(resolvedPath);
155
+ return {
156
+ name: path.basename(resolvedPath) || path.basename(this.toBackendPath(this.root)),
157
+ type: entryType(info),
158
+ size: info.size,
159
+ createdAt: info.birthtime,
160
+ modifiedAt: info.mtime,
161
+ accessedAt: info.atime,
162
+ };
163
+ }
164
+ isNotFoundError(error) {
165
+ return ((error instanceof FileSystemError && error.code === "NOT_FOUND") || isErrno(error, "ENOENT"));
166
+ }
167
+ async tryAppendFileAt(resolvedPath, content) {
168
+ await appendFile(resolvedPath, content);
169
+ return true;
170
+ }
171
+ async tryCopyFileAt(sourcePath, destinationPath, options) {
172
+ await cp(sourcePath, destinationPath, {
173
+ recursive: true,
174
+ force: options.overwrite,
175
+ errorOnExist: !options.overwrite,
176
+ });
177
+ return true;
178
+ }
179
+ async tryMoveFileAt(sourcePath, destinationPath, options) {
180
+ try {
181
+ await rename(sourcePath, destinationPath);
182
+ }
183
+ catch (err) {
184
+ // Across devices `rename` cannot work; fall back to copy + remove. Any
185
+ // other failure propagates for the base class to map.
186
+ if (!isErrno(err, "EXDEV"))
187
+ throw err;
188
+ await cp(sourcePath, destinationPath, { recursive: true, force: options.overwrite });
189
+ await rm(sourcePath, { recursive: true, force: true });
190
+ }
191
+ return true;
192
+ }
193
+ }
194
+ /* ------------------------------ helpers ------------------------------ */
195
+ function isWithinHostRoot(root, candidate) {
196
+ const relative = path.relative(root, candidate);
197
+ return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
198
+ }
199
+ function isErrno(err, code) {
200
+ return (typeof err === "object" &&
201
+ err !== null &&
202
+ "code" in err &&
203
+ err.code === code);
204
+ }
205
+ function entryType(entry) {
206
+ if (entry.isSymbolicLink())
207
+ return "symbolic-link";
208
+ if (entry.isDirectory())
209
+ return "directory";
210
+ if (entry.isFile())
211
+ return "file";
212
+ return "other";
213
+ }
214
+ /** Node errno -> portable {@link FileSystemErrorCode}. */
215
+ const NODE_ERROR_CODES = {
216
+ ENOENT: "NOT_FOUND",
217
+ EEXIST: "ALREADY_EXISTS",
218
+ ENOTDIR: "NOT_DIRECTORY",
219
+ EISDIR: "IS_DIRECTORY",
220
+ ENOTEMPTY: "DIRECTORY_NOT_EMPTY",
221
+ EACCES: "PERMISSION_DENIED",
222
+ EPERM: "PERMISSION_DENIED",
223
+ };
224
+ function nodeErrorCode(err) {
225
+ const code = err?.code;
226
+ return code ? NODE_ERROR_CODES[code] : undefined;
227
+ }
228
+ /**
229
+ * {@link LocalFileSystem} rooted under {@link resolveLocalHome}, with
230
+ * {@link relativeRoot} joined as a relative path (leading `/` / `~` stripped
231
+ * so the result stays under home).
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * const cache = homeFS(".cache/my-app");
236
+ * await cache.writeFile("state.json", "{}");
237
+ * ```
238
+ */
239
+ export function homeFS(relativeRoot = ".", options = {}) {
240
+ const { os, ...fsOptions } = options;
241
+ return new LocalFileSystem({
242
+ ...fsOptions,
243
+ root: joinUnderBase(resolveLocalHome(os), relativeRoot),
244
+ });
245
+ }
246
+ /**
247
+ * {@link LocalFileSystem} rooted under {@link resolveLocalTemp}, with
248
+ * {@link relativeRoot} joined as a relative path (leading `/` / `~` stripped
249
+ * so the result stays under temp).
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * const scratch = tmpFS("job-42");
254
+ * await scratch.writeFile("out.bin", bytes);
255
+ * ```
256
+ */
257
+ export function tmpFS(relativeRoot = ".", options = {}) {
258
+ const { os, ...fsOptions } = options;
259
+ return new LocalFileSystem({
260
+ ...fsOptions,
261
+ root: joinUnderBase(resolveLocalTemp(os), relativeRoot),
262
+ });
263
+ }
264
+ /**
265
+ * {@link tmpFS} under a root that is unique per call (`<prefix>-<id>`), for a
266
+ * scratch area no other process or run can collide with.
267
+ *
268
+ * Prefer this over minting the id at the call site - it is the one place that
269
+ * decides what a throwaway working directory looks like.
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * const scratch = scratchFS("remote-skills");
274
+ * await scratch.writeFile("SKILL.md", body);
275
+ * ```
276
+ */
277
+ export function scratchFS(prefix, options = {}) {
278
+ return tmpFS(`${prefix}-${hash.id()}`, options);
279
+ }
280
+ /**
281
+ * Rebuild the STABLE temp tree named by {@link key}, every call.
282
+ *
283
+ * {@link materialize} writes into a throwaway {@link scratchFS} root; only on
284
+ * success does that root REPLACE the stable one. Two properties fall out of
285
+ * that ordering, and both are the reason to prefer this over writing into the
286
+ * stable path directly:
287
+ *
288
+ * - repeated runs reuse ONE directory instead of leaving a new scratch behind
289
+ * on every boot, and
290
+ * - a reader never observes a half-written tree, and a failed rebuild leaves
291
+ * the previous one intact.
292
+ *
293
+ * The swap is a `rename` within the same temp root, so it is atomic.
294
+ *
295
+ * @param key - Stable temp-relative path. May be nested (`"skills/abc123"`)
296
+ * to give each input its own directory.
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * const tools = await rebuildFS("databricks-aitools", (scratch) =>
301
+ * installInto(scratch.root),
302
+ * );
303
+ * ```
304
+ */
305
+ export async function rebuildFS(key, materialize, options = {}) {
306
+ const scratch = scratchFS(key, options);
307
+ await scratch.init();
308
+ try {
309
+ await materialize(scratch);
310
+ }
311
+ catch (err) {
312
+ await rm(scratch.root, { recursive: true, force: true }).catch(() => undefined);
313
+ throw err;
314
+ }
315
+ const stable = tmpFS(key, options);
316
+ const stableHost = path.resolve(stable.root);
317
+ await rm(stableHost, { recursive: true, force: true });
318
+ await mkdir(path.dirname(stableHost), { recursive: true });
319
+ await rename(path.resolve(scratch.root), stableHost);
320
+ return stable;
321
+ }
322
+ /**
323
+ * Join {@link relativeRoot} under {@link base}. Empty / `.` keeps {@link base};
324
+ * leading `~` or `/` is stripped so an absolute-looking input cannot escape.
325
+ */
326
+ function joinUnderBase(base, relativeRoot) {
327
+ const rest = posixPath.toRelativeSegment(relativeRoot);
328
+ return rest ? path.resolve(base, rest) : base;
329
+ }
330
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwtZnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbG9jYWwtZnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFFSCxPQUFPLEVBQ0wsVUFBVSxFQUNWLEVBQUUsRUFDRixLQUFLLEVBQ0wsS0FBSyxFQUNMLE9BQU8sRUFDUCxRQUFRLEVBQ1IsUUFBUSxFQUNSLE1BQU0sRUFDTixFQUFFLEVBQ0YsS0FBSyxFQUNMLFNBQVMsR0FDVixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUMsT0FBTyxFQUNMLGNBQWMsRUFDZCxNQUFNLEVBQ04sZUFBZSxFQUNmLFNBQVMsR0FPVixNQUFNLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxnQkFBZ0IsRUFBOEIsTUFBTSxjQUFjLENBQUM7QUF5QzlGOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxPQUFPLGVBQWdCLFNBQVEsY0FBdUI7SUFDekMsU0FBUyxDQUFVO0lBQ3BDLCtEQUErRDtJQUN2RCxRQUFRLENBQVM7SUFFekIsWUFBWSxPQUErQjtRQUN6QyxNQUFNLFFBQVEsR0FBRyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDaEQsTUFBTSxJQUFJLEdBQUcsU0FBUyxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDbEUsS0FBSyxDQUFDO1lBQ0osRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFLElBQUksU0FBUyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQy9DLE9BQU8sRUFBRSxPQUFPO1lBQ2hCLElBQUk7WUFD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bnQiOlsiLyoqXG4gKiBIb3N0LWxvY2FsIHtAbGluayBGaWxlU3lzdGVtfSBiYWNrZWQgYnkgTm9kZSdzIGBub2RlOmZzL3Byb21pc2VzYC5cbiAqXG4gKiBFeHRlbmRzIHtAbGluayBCYXNlRmlsZVN5c3RlbX0gc28gdGhpcyBtb2R1bGUgb25seSBvd25zIE5vZGUgSS9PLCBob3N0IHBhdGhcbiAqIGNvbnZlcnNpb24sIGFuZCBzeW1saW5rIGNvbnRhaW5tZW50LiBQb3J0YWJsZSBiZWhhdmlvciAoZW5jb2RpbmcsIHJlY3Vyc2lvbixcbiAqIHBhcmVudCBjcmVhdGlvbiwgZmFsbGJhY2tzLCBQT1NJWCBuYW1lc3BhY2UgcGF0aHMpIGxpdmVzIGluIGBAZGJ4LXRvb2xzL3NoYXJlZC1mc2AuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7XG4gIGFwcGVuZEZpbGUsXG4gIGNwLFxuICBsc3RhdCxcbiAgbWtkaXIsXG4gIHJlYWRkaXIsXG4gIHJlYWRGaWxlLFxuICByZWFscGF0aCxcbiAgcmVuYW1lLFxuICBybSxcbiAgcm1kaXIsXG4gIHdyaXRlRmlsZSxcbn0gZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIjtcbmltcG9ydCBwYXRoIGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGhhc2ggfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHtcbiAgQmFzZUZpbGVTeXN0ZW0sXG4gIGJhc2VGUyxcbiAgRmlsZVN5c3RlbUVycm9yLFxuICBwb3NpeFBhdGgsXG4gIHR5cGUgQ29weU9wdGlvbnMsXG4gIHR5cGUgRmlsZUVudHJ5LFxuICB0eXBlIEZpbGVFbnRyeVR5cGUsXG4gIHR5cGUgRmlsZVN0YXQsXG4gIHR5cGUgRmlsZVN5c3RlbUVycm9yQ29kZSxcbiAgdHlwZSBXcml0ZUZpbGVPcHRpb25zLFxufSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtZnNcIjtcbmltcG9ydCB7IHJlc29sdmVMb2NhbFJvb3QgfSBmcm9tIFwiLi9sb2NhbC1wYXRoLnRzXCI7XG5pbXBvcnQgeyByZXNvbHZlTG9jYWxIb21lLCByZXNvbHZlTG9jYWxUZW1wLCB0eXBlIFJlc29sdmVPc1BhdGhzT3B0aW9ucyB9IGZyb20gXCIuL29zLXBhdGgudHNcIjtcblxuLyoqIE9wdGlvbnMgZm9yIHtAbGluayBMb2NhbEZpbGVTeXN0ZW19LiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2NhbEZpbGVTeXN0ZW1PcHRpb25zIHtcbiAgLyoqIFVuaXF1ZSBpZGVudGlmaWVyLiBEZWZhdWx0cyB0byBhIHN0YWJsZSBoYXNoIG9mIHRoZSBhYnNvbHV0ZSByb290LiAqL1xuICBpZD86IHN0cmluZztcblxuICAvKipcbiAgICogUm9vdCBkaXJlY3Rvcnkgb24gZGlzay4gUmVsYXRpdmUgcGF0aHMgcmVzb2x2ZSBhZ2FpbnN0IGBwcm9jZXNzLmN3ZCgpYC5cbiAgICogYH5gIC8gYH4vLi4uYCBleHBhbmQgdmlhIGBvcy5ob21lZGlyKClgIChiZWZvcmUgYEhPTUVgKSwgQXBwIGAvaG9tZS9hcHBgLFxuICAgKiBvciBhIGNyZWF0ZWQgYC4vLmhvbWVgIChjcmVhdGUgZmFpbHVyZXMgc2tpcCkuXG4gICAqIFN0b3JlZCBvbiB7QGxpbmsgTG9jYWxGaWxlU3lzdGVtLnJvb3R9IGluIFBPU0lYIGZvcm0uXG4gICAqL1xuICByb290OiBzdHJpbmc7XG5cbiAgLyoqIEJsb2NrIGFsbCB3cml0ZSBvcGVyYXRpb25zLiBEZWZhdWx0cyB0byBmYWxzZS4gKi9cbiAgcmVhZE9ubHk/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBLZWVwIGV2ZXJ5IHJlc29sdmVkIHBhdGggdW5kZXIge0BsaW5rIHJvb3R9LiBEZWZhdWx0cyB0byB0cnVlLlxuICAgKiBXaGVuIGZhbHNlLCBhYnNvbHV0ZSBpbnB1dCBwYXRocyBhcmUgYWNjZXB0ZWQgYXMtaXMgKG5vIHNhbmRib3gpLlxuICAgKi9cbiAgY29udGFpbmVkPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogQ3JlYXRlIHtAbGluayByb290fSAoYW5kIHBhcmVudHMpIGR1cmluZyBpbml0LiBEZWZhdWx0cyB0byB0cnVlLlxuICAgKi9cbiAgY3JlYXRlUm9vdD86IGJvb2xlYW47XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgdGhlIGZhY3RvcmllcyB0aGF0IHRha2UgdGhlaXIgcm9vdCBGUk9NIHRoZSBPU1xuICogKHtAbGluayBob21lRlN9IC8ge0BsaW5rIHRtcEZTfSAvIHtAbGluayBzY3JhdGNoRlN9KSByYXRoZXIgdGhhbiBmcm9tIHRoZVxuICogY2FsbGVyOiB7QGxpbmsgTG9jYWxGaWxlU3lzdGVtT3B0aW9uc30gd2l0aG91dCBgcm9vdGAsIHBsdXMgdGhlXG4gKiB7QGxpbmsgcmVzb2x2ZU9zUGF0aHN9IGluamVjdGFibGVzLlxuICovXG5leHBvcnQgdHlwZSBPc0ZpbGVTeXN0ZW1PcHRpb25zID0gT21pdDxMb2NhbEZpbGVTeXN0ZW1PcHRpb25zLCBcInJvb3RcIj4gJiB7XG4gIC8qKiBGb3J3YXJkZWQgdG8ge0BsaW5rIHJlc29sdmVMb2NhbEhvbWV9IC8ge0BsaW5rIHJlc29sdmVMb2NhbFRlbXB9LiAqL1xuICBvcz86IFJlc29sdmVPc1BhdGhzT3B0aW9ucztcbn07XG5cbi8qKlxuICoge0BsaW5rIEZpbGVTeXN0ZW19IGltcGxlbWVudGF0aW9uIHRoYXQgc3RvcmVzIGZpbGVzIGluIGEgZm9sZGVyIG9uIHRoZSBsb2NhbFxuICogbWFjaGluZS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGNvbnN0IGZzID0gbmV3IExvY2FsRmlsZVN5c3RlbSh7IHJvb3Q6IFwiLi9kYXRhXCIgfSk7XG4gKiBhd2FpdCBmcy5pbml0KCk7XG4gKiBhd2FpdCBmcy53cml0ZUZpbGUoXCJoZWxsby50eHRcIiwgXCJoaVwiKTtcbiAqXG4gKiBjb25zdCBob21lID0gbmV3IExvY2FsRmlsZVN5c3RlbSh7IHJvb3Q6IFwifi9wcm9qZWN0cy9kYXRhXCIgfSk7XG4gKiBjb25zdCBzY3JhdGNoID0gdG1wRlMoXCJteS1qb2JcIik7XG4gKiBjb25zdCBjYWNoZSA9IGhvbWVGUyhcIi5jYWNoZS9hcHBcIik7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIExvY2FsRmlsZVN5c3RlbSBleHRlbmRzIEJhc2VGaWxlU3lzdGVtPFwibG9jYWxcIj4ge1xuICBwcml2YXRlIHJlYWRvbmx5IGNvbnRhaW5lZDogYm9vbGVhbjtcbiAgLyoqIFN5bWxpbmstcmVzb2x2ZWQgaG9zdCByb290LCByZWZyZXNoZWQgaW4ge0BsaW5rIG9uSW5pdH0uICovXG4gIHByaXZhdGUgcmVhbFJvb3Q6IHN0cmluZztcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBMb2NhbEZpbGVTeXN0ZW1PcHRpb25zKSB7XG4gICAgY29uc3QgaG9zdFJvb3QgPSByZXNvbHZlTG9jYWxSb290KG9wdGlvbnMucm9vdCk7XG4gICAgY29uc3Qgcm9vdCA9IHBvc2l4UGF0aC5ub3JtYWxpemVSb290KHBvc2l4UGF0aC50b1Bvc2l4KGhvc3RSb290KSk7XG4gICAgc3VwZXIoe1xuICAgICAgaWQ6IG9wdGlvbnMuaWQgPz8gYGxvY2FsLSR7aGFzaC5mbnZIYXNoKHJvb3QpfWAsXG4gICAgICBiYWNrZW5kOiBcImxvY2FsXCIsXG4gICAgICByb290LFxuICAgICAgcmVhZE9ubHk6IG9wdGlvbnMucmVhZE9ubHksXG4gICAgICBjcmVhdGVSb290OiBvcHRpb25zLmNyZWF0ZVJvb3QgPz8gdHJ1ZSxcbiAgICB9KTtcbiAgICB0aGlzLnJlYWxSb290ID0gdGhpcy50b0JhY2tlbmRQYXRoKHJvb3QpO1xuICAgIHRoaXMuY29udGFpbmVkID0gb3B0aW9ucy5jb250YWluZWQgPz8gdHJ1ZTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvdmVycmlkZSB0b0JhY2tlbmRQYXRoKHBvc2l4QmFja2VuZFBhdGg6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHBvc2l4UGF0aC50b0hvc3QocG9zaXhCYWNrZW5kUGF0aCwgcGF0aC5zZXApO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIGNyZWF0ZVJvb3REaXJlY3RvcnkoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgbWtkaXIodGhpcy50b0JhY2tlbmRQYXRoKHRoaXMucm9vdCksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIG9uSW5pdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBob3N0Um9vdCA9IHRoaXMudG9CYWNrZW5kUGF0aCh0aGlzLnJvb3QpO1xuICAgIGlmICghdGhpcy5jcmVhdGVSb290KSB7XG4gICAgICBjb25zdCBpbmZvID0gYXdhaXQgbHN0YXQoaG9zdFJvb3QpO1xuICAgICAgaWYgKCFpbmZvLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEZpbGVTeXN0ZW1FcnJvcihcbiAgICAgICAgICBcIk5PVF9ESVJFQ1RPUllcIixcbiAgICAgICAgICBgTG9jYWwgZmlsZXN5c3RlbSByb290IGlzIG5vdCBhIGRpcmVjdG9yeTogJHt0aGlzLnJvb3R9YCxcbiAgICAgICAgICB0aGlzLnJvb3QsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIENhbm9uaWNhbGl6ZSBzbyBjb250YWlubWVudCBjaGVja3Mgc3Vydml2ZSBPUyByb290IHN5bWxpbmtzIChlLmcuIC92YXIgLT4gL3ByaXZhdGUvdmFyKS5cbiAgICB0aGlzLnJlYWxSb290ID0gYXdhaXQgcmVhbHBhdGgoaG9zdFJvb3QpO1xuICB9XG5cbiAgb3ZlcnJpZGUgcmVzb2x2ZVBhdGgoaW5wdXRQYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGlmICghdGhpcy5jb250YWluZWQpIHtcbiAgICAgIGNvbnN0IHJlc29sdmVkID0gcGF0aC5pc0Fic29sdXRlKGlucHV0UGF0aClcbiAgICAgICAgPyBwYXRoLnJlc29sdmUoaW5wdXRQYXRoKVxuICAgICAgICA6IHBhdGgucmVzb2x2ZSh0aGlzLnRvQmFja2VuZFBhdGgodGhpcy5yb290KSwgaW5wdXRQYXRoKTtcbiAgICAgIHJldHVybiB0aGlzLnRvQmFja2VuZFBhdGgocG9zaXhQYXRoLnRvUG9zaXgocmVzb2x2ZWQpKTtcbiAgICB9XG4gICAgcmV0dXJuIHN1cGVyLnJlc29sdmVQYXRoKGlucHV0UGF0aCk7XG4gIH1cblxuICAvKipcbiAgICogV2hlbiBjb250YWluZWQsIHJlc29sdmUgc3ltbGlua3MgYW5kIGVuc3VyZSB0aGUgcmVhbCBwYXRoIHN0aWxsIHNpdHMgdW5kZXJcbiAgICoge0BsaW5rIHJvb3R9LiBNaXNzaW5nIGxlYWYgcGF0aHMgYXJlIGFsbG93ZWQgd2hlbiBgYWxsb3dNaXNzaW5nYCBpcyBzZXQgc29cbiAgICogd3JpdGVzIGNhbiBjcmVhdGUgbmV3IGZpbGVzLlxuICAgKi9cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIHByZXBhcmVQYXRoKFxuICAgIHJlc29sdmVkUGF0aDogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGFsbG93TWlzc2luZz86IGJvb2xlYW4gfSxcbiAgKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMuY29udGFpbmVkKSByZXR1cm4gcmVzb2x2ZWRQYXRoO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZWFsID0gYXdhaXQgcmVhbHBhdGgocmVzb2x2ZWRQYXRoKTtcbiAgICAgIGlmICghaXNXaXRoaW5Ib3N0Um9vdCh0aGlzLnJlYWxSb290LCByZWFsKSkge1xuICAgICAgICB0aHJvdyBuZXcgRmlsZVN5c3RlbUVycm9yKFxuICAgICAgICAgIFwiUEVSTUlTU0lPTl9ERU5JRURcIixcbiAgICAgICAgICBcIlBhdGggZXNjYXBlcyB0aGUgZmlsZXN5c3RlbSByb290XCIsXG4gICAgICAgICAgcmVzb2x2ZWRQYXRoLFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc29sdmVkUGF0aDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBGaWxlU3lzdGVtRXJyb3IpIHRocm93IGVycjtcbiAgICAgIGlmIChvcHRpb25zPy5hbGxvd01pc3NpbmcgJiYgaXNFcnJubyhlcnIsIFwiRU5PRU5UXCIpKSB7XG4gICAgICAgIGxldCBjdXJzb3IgPSBwYXRoLmRpcm5hbWUocmVzb2x2ZWRQYXRoKTtcbiAgICAgICAgd2hpbGUgKGN1cnNvciAhPT0gcGF0aC5kaXJuYW1lKGN1cnNvcikpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVhbCA9IGF3YWl0IHJlYWxwYXRoKGN1cnNvcik7XG4gICAgICAgICAgICBpZiAoIWlzV2l0aGluSG9zdFJvb3QodGhpcy5yZWFsUm9vdCwgcmVhbCkpIHtcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEZpbGVTeXN0ZW1FcnJvcihcbiAgICAgICAgICAgICAgICBcIlBFUk1JU1NJT05fREVOSUVEXCIsXG4gICAgICAgICAgICAgICAgXCJQYXRoIGVzY2FwZXMgdGhlIGZpbGVzeXN0ZW0gcm9vdFwiLFxuICAgICAgICAgICAgICAgIHJlc29sdmVkUGF0aCxcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiByZXNvbHZlZFBhdGg7XG4gICAgICAgICAgfSBjYXRjaCAoaW5uZXIpIHtcbiAgICAgICAgICAgIGlmIChpbm5lciBpbnN0YW5jZW9mIEZpbGVTeXN0ZW1FcnJvcikgdGhyb3cgaW5uZXI7XG4gICAgICAgICAgICBpZiAoIWlzRXJybm8oaW5uZXIsIFwiRU5PRU5UXCIpKSB0aHJvdyB0aGlzLm1hcEVycm9yKGlubmVyLCByZXNvbHZlZFBhdGgpO1xuICAgICAgICAgICAgY3Vyc29yID0gcGF0aC5kaXJuYW1lKGN1cnNvcik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXNvbHZlZFBhdGg7XG4gICAgICB9XG4gICAgICB0aHJvdyB0aGlzLm1hcEVycm9yKGVyciwgcmVzb2x2ZWRQYXRoKTtcbiAgICB9XG4gIH1cblxuICAvKiogQ2xhc3NpZnkgTm9kZSBlcnJubyBjb2Rlczsge0BsaW5rIEJhc2VGaWxlU3lzdGVtfSB3cmFwcyBldmVyeSBwcmltaXRpdmUuICovXG4gIHByb3RlY3RlZCBvdmVycmlkZSBtYXBFcnJvcihlcnI6IHVua25vd24sIGZpbGVQYXRoOiBzdHJpbmcpOiBGaWxlU3lzdGVtRXJyb3Ige1xuICAgIHJldHVybiBiYXNlRlMubWFwRmlsZVN5c3RlbUVycm9yKGVyciwgZmlsZVBhdGgsIG5vZGVFcnJvckNvZGUpO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIHJlYWRCeXRlc0F0KHJlc29sdmVkUGF0aDogc3RyaW5nKTogUHJvbWlzZTxVaW50OEFycmF5PiB7XG4gICAgY29uc3QgYnVmZmVyID0gYXdhaXQgcmVhZEZpbGUocmVzb2x2ZWRQYXRoKTtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoYnVmZmVyLmJ1ZmZlciwgYnVmZmVyLmJ5dGVPZmZzZXQsIGJ1ZmZlci5ieXRlTGVuZ3RoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvdmVycmlkZSBhc3luYyB3cml0ZUJ5dGVzQXQoXG4gICAgcmVzb2x2ZWRQYXRoOiBzdHJpbmcsXG4gICAgY29udGVudDogVWludDhBcnJheSxcbiAgICBvcHRpb25zOiBSZXF1aXJlZDxXcml0ZUZpbGVPcHRpb25zPixcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgd3JpdGVGaWxlKHJlc29sdmVkUGF0aCwgY29udGVudCwgb3B0aW9ucy5vdmVyd3JpdGUgPyB1bmRlZmluZWQgOiB7IGZsYWc6IFwid3hcIiB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvdmVycmlkZSBhc3luYyBkZWxldGVGaWxlQXQocmVzb2x2ZWRQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBybShyZXNvbHZlZFBhdGgsIHsgZm9yY2U6IGZhbHNlIH0pO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIGNyZWF0ZURpcmVjdG9yeUF0KHJlc29sdmVkUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgbWtkaXIocmVzb2x2ZWRQYXRoKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBvdmVycmlkZSBhc3luYyByZW1vdmVEaXJlY3RvcnlBdChyZXNvbHZlZFBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHJtZGlyKHJlc29sdmVkUGF0aCk7XG4gIH1cblxuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgYXN5bmMgbGlzdERpcmVjdG9yeUF0KHJlc29sdmVkUGF0aDogc3RyaW5nKTogUHJvbWlzZTxGaWxlRW50cnlbXT4ge1xuICAgIGNvbnN0IGRpcmVudHMgPSBhd2FpdCByZWFkZGlyKHJlc29sdmVkUGF0aCwgeyB3aXRoRmlsZVR5cGVzOiB0cnVlIH0pO1xuICAgIGNvbnN0IGVudHJpZXM6IEZpbGVFbnRyeVtdID0gW107XG4gICAgZm9yIChjb25zdCBkaXJlbnQgb2YgZGlyZW50cykge1xuICAgICAgY29uc3QgdHlwZSA9IGVudHJ5VHlwZShkaXJlbnQpO1xuICAgICAgbGV0IHNpemU6IG51bWJlciB8IHVuZGVmaW5lZDtcbiAgICAgIGlmICh0eXBlID09PSBcImZpbGVcIiB8fCB0eXBlID09PSBcInN5bWJvbGljLWxpbmtcIikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHNpemUgPSAoYXdhaXQgbHN0YXQocGF0aC5qb2luKHJlc29sdmVkUGF0aCwgZGlyZW50Lm5hbWUpKSkuc2l6ZTtcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gUmFjZSB3aXRoIGNvbmN1cnJlbnQgZGVsZXRlczsgb21pdCBzaXplLlxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBlbnRyaWVzLnB1c2goXG4gICAgICAgIHNpemUgPT09IHVuZGVmaW5lZCA/IHsgbmFtZTogZGlyZW50Lm5hbWUsIHR5cGUgfSA6IHsgbmFtZTogZGlyZW50Lm5hbWUsIHR5cGUsIHNpemUgfSxcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBlbnRyaWVzO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIHN0YXRBdChyZXNvbHZlZFBhdGg6IHN0cmluZyk6IFByb21pc2U8T21pdDxGaWxlU3RhdCwgXCJwYXRoXCI+PiB7XG4gICAgY29uc3QgaW5mbyA9IGF3YWl0IGxzdGF0KHJlc29sdmVkUGF0aCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIG5hbWU6IHBhdGguYmFzZW5hbWUocmVzb2x2ZWRQYXRoKSB8fCBwYXRoLmJhc2VuYW1lKHRoaXMudG9CYWNrZW5kUGF0aCh0aGlzLnJvb3QpKSxcbiAgICAgIHR5cGU6IGVudHJ5VHlwZShpbmZvKSxcbiAgICAgIHNpemU6IGluZm8uc2l6ZSxcbiAgICAgIGNyZWF0ZWRBdDogaW5mby5iaXJ0aHRpbWUsXG4gICAgICBtb2RpZmllZEF0OiBpbmZvLm10aW1lLFxuICAgICAgYWNjZXNzZWRBdDogaW5mby5hdGltZSxcbiAgICB9O1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGlzTm90Rm91bmRFcnJvcihlcnJvcjogdW5rbm93bik6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICAoZXJyb3IgaW5zdGFuY2VvZiBGaWxlU3lzdGVtRXJyb3IgJiYgZXJyb3IuY29kZSA9PT0gXCJOT1RfRk9VTkRcIikgfHwgaXNFcnJubyhlcnJvciwgXCJFTk9FTlRcIilcbiAgICApO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIHRyeUFwcGVuZEZpbGVBdChcbiAgICByZXNvbHZlZFBhdGg6IHN0cmluZyxcbiAgICBjb250ZW50OiBVaW50OEFycmF5LFxuICApOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBhd2FpdCBhcHBlbmRGaWxlKHJlc29sdmVkUGF0aCwgY29udGVudCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgYXN5bmMgdHJ5Q29weUZpbGVBdChcbiAgICBzb3VyY2VQYXRoOiBzdHJpbmcsXG4gICAgZGVzdGluYXRpb25QYXRoOiBzdHJpbmcsXG4gICAgb3B0aW9uczogUmVxdWlyZWQ8Q29weU9wdGlvbnM+LFxuICApOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBhd2FpdCBjcChzb3VyY2VQYXRoLCBkZXN0aW5hdGlvblBhdGgsIHtcbiAgICAgIHJlY3Vyc2l2ZTogdHJ1ZSxcbiAgICAgIGZvcmNlOiBvcHRpb25zLm92ZXJ3cml0ZSxcbiAgICAgIGVycm9yT25FeGlzdDogIW9wdGlvbnMub3ZlcndyaXRlLFxuICAgIH0pO1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIHRyeU1vdmVGaWxlQXQoXG4gICAgc291cmNlUGF0aDogc3RyaW5nLFxuICAgIGRlc3RpbmF0aW9uUGF0aDogc3RyaW5nLFxuICAgIG9wdGlvbnM6IFJlcXVpcmVkPENvcHlPcHRpb25zPixcbiAgKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHJlbmFtZShzb3VyY2VQYXRoLCBkZXN0aW5hdGlvblBhdGgpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gQWNyb3NzIGRldmljZXMgYHJlbmFtZWAgY2Fubm90IHdvcms7IGZhbGwgYmFjayB0byBjb3B5ICsgcmVtb3ZlLiBBbnlcbiAgICAgIC8vIG90aGVyIGZhaWx1cmUgcHJvcGFnYXRlcyBmb3IgdGhlIGJhc2UgY2xhc3MgdG8gbWFwLlxuICAgICAgaWYgKCFpc0Vycm5vKGVyciwgXCJFWERFVlwiKSkgdGhyb3cgZXJyO1xuICAgICAgYXdhaXQgY3Aoc291cmNlUGF0aCwgZGVzdGluYXRpb25QYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IG9wdGlvbnMub3ZlcndyaXRlIH0pO1xuICAgICAgYXdhaXQgcm0oc291cmNlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxufVxuXG4vKiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gaGVscGVycyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cblxuZnVuY3Rpb24gaXNXaXRoaW5Ib3N0Um9vdChyb290OiBzdHJpbmcsIGNhbmRpZGF0ZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IHJlbGF0aXZlID0gcGF0aC5yZWxhdGl2ZShyb290LCBjYW5kaWRhdGUpO1xuICByZXR1cm4gcmVsYXRpdmUgPT09IFwiXCIgfHwgKCFyZWxhdGl2ZS5zdGFydHNXaXRoKFwiLi5cIikgJiYgIXBhdGguaXNBYnNvbHV0ZShyZWxhdGl2ZSkpO1xufVxuXG5mdW5jdGlvbiBpc0Vycm5vKGVycjogdW5rbm93biwgY29kZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiAoXG4gICAgdHlwZW9mIGVyciA9PT0gXCJvYmplY3RcIiAmJlxuICAgIGVyciAhPT0gbnVsbCAmJlxuICAgIFwiY29kZVwiIGluIGVyciAmJlxuICAgIChlcnIgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uKS5jb2RlID09PSBjb2RlXG4gICk7XG59XG5cbmZ1bmN0aW9uIGVudHJ5VHlwZShlbnRyeToge1xuICBpc0ZpbGUoKTogYm9vbGVhbjtcbiAgaXNEaXJlY3RvcnkoKTogYm9vbGVhbjtcbiAgaXNTeW1ib2xpY0xpbmsoKTogYm9vbGVhbjtcbn0pOiBGaWxlRW50cnlUeXBlIHtcbiAgaWYgKGVudHJ5LmlzU3ltYm9saWNMaW5rKCkpIHJldHVybiBcInN5bWJvbGljLWxpbmtcIjtcbiAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkpIHJldHVybiBcImRpcmVjdG9yeVwiO1xuICBpZiAoZW50cnkuaXNGaWxlKCkpIHJldHVybiBcImZpbGVcIjtcbiAgcmV0dXJuIFwib3RoZXJcIjtcbn1cblxuLyoqIE5vZGUgZXJybm8gLT4gcG9ydGFibGUge0BsaW5rIEZpbGVTeXN0ZW1FcnJvckNvZGV9LiAqL1xuY29uc3QgTk9ERV9FUlJPUl9DT0RFUzogUmVhZG9ubHk8UmVjb3JkPHN0cmluZywgRmlsZVN5c3RlbUVycm9yQ29kZT4+ID0ge1xuICBFTk9FTlQ6IFwiTk9UX0ZPVU5EXCIsXG4gIEVFWElTVDogXCJBTFJFQURZX0VYSVNUU1wiLFxuICBFTk9URElSOiBcIk5PVF9ESVJFQ1RPUllcIixcbiAgRUlTRElSOiBcIklTX0RJUkVDVE9SWVwiLFxuICBFTk9URU1QVFk6IFwiRElSRUNUT1JZX05PVF9FTVBUWVwiLFxuICBFQUNDRVM6IFwiUEVSTUlTU0lPTl9ERU5JRURcIixcbiAgRVBFUk06IFwiUEVSTUlTU0lPTl9ERU5JRURcIixcbn07XG5cbmZ1bmN0aW9uIG5vZGVFcnJvckNvZGUoZXJyOiB1bmtub3duKTogRmlsZVN5c3RlbUVycm9yQ29kZSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGNvZGUgPSAoZXJyIGFzIE5vZGVKUy5FcnJub0V4Y2VwdGlvbiB8IHVuZGVmaW5lZCk/LmNvZGU7XG4gIHJldHVybiBjb2RlID8gTk9ERV9FUlJPUl9DT0RFU1tjb2RlXSA6IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiB7QGxpbmsgTG9jYWxGaWxlU3lzdGVtfSByb290ZWQgdW5kZXIge0BsaW5rIHJlc29sdmVMb2NhbEhvbWV9LCB3aXRoXG4gKiB7QGxpbmsgcmVsYXRpdmVSb290fSBqb2luZWQgYXMgYSByZWxhdGl2ZSBwYXRoIChsZWFkaW5nIGAvYCAvIGB+YCBzdHJpcHBlZFxuICogc28gdGhlIHJlc3VsdCBzdGF5cyB1bmRlciBob21lKS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGNvbnN0IGNhY2hlID0gaG9tZUZTKFwiLmNhY2hlL215LWFwcFwiKTtcbiAqIGF3YWl0IGNhY2hlLndyaXRlRmlsZShcInN0YXRlLmpzb25cIiwgXCJ7fVwiKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gaG9tZUZTKFxuICByZWxhdGl2ZVJvb3Q6IHN0cmluZyA9IFwiLlwiLFxuICBvcHRpb25zOiBPc0ZpbGVTeXN0ZW1PcHRpb25zID0ge30sXG4pOiBMb2NhbEZpbGVTeXN0ZW0ge1xuICBjb25zdCB7IG9zLCAuLi5mc09wdGlvbnMgfSA9IG9wdGlvbnM7XG4gIHJldHVybiBuZXcgTG9jYWxGaWxlU3lzdGVtKHtcbiAgICAuLi5mc09wdGlvbnMsXG4gICAgcm9vdDogam9pblVuZGVyQmFzZShyZXNvbHZlTG9jYWxIb21lKG9zKSwgcmVsYXRpdmVSb290KSxcbiAgfSk7XG59XG5cbi8qKlxuICoge0BsaW5rIExvY2FsRmlsZVN5c3RlbX0gcm9vdGVkIHVuZGVyIHtAbGluayByZXNvbHZlTG9jYWxUZW1wfSwgd2l0aFxuICoge0BsaW5rIHJlbGF0aXZlUm9vdH0gam9pbmVkIGFzIGEgcmVsYXRpdmUgcGF0aCAobGVhZGluZyBgL2AgLyBgfmAgc3RyaXBwZWRcbiAqIHNvIHRoZSByZXN1bHQgc3RheXMgdW5kZXIgdGVtcCkuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBjb25zdCBzY3JhdGNoID0gdG1wRlMoXCJqb2ItNDJcIik7XG4gKiBhd2FpdCBzY3JhdGNoLndyaXRlRmlsZShcIm91dC5iaW5cIiwgYnl0ZXMpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0bXBGUyhcbiAgcmVsYXRpdmVSb290OiBzdHJpbmcgPSBcIi5cIixcbiAgb3B0aW9uczogT3NGaWxlU3lzdGVtT3B0aW9ucyA9IHt9LFxuKTogTG9jYWxGaWxlU3lzdGVtIHtcbiAgY29uc3QgeyBvcywgLi4uZnNPcHRpb25zIH0gPSBvcHRpb25zO1xuICByZXR1cm4gbmV3IExvY2FsRmlsZVN5c3RlbSh7XG4gICAgLi4uZnNPcHRpb25zLFxuICAgIHJvb3Q6IGpvaW5VbmRlckJhc2UocmVzb2x2ZUxvY2FsVGVtcChvcyksIHJlbGF0aXZlUm9vdCksXG4gIH0pO1xufVxuXG4vKipcbiAqIHtAbGluayB0bXBGU30gdW5kZXIgYSByb290IHRoYXQgaXMgdW5pcXVlIHBlciBjYWxsIChgPHByZWZpeD4tPGlkPmApLCBmb3IgYVxuICogc2NyYXRjaCBhcmVhIG5vIG90aGVyIHByb2Nlc3Mgb3IgcnVuIGNhbiBjb2xsaWRlIHdpdGguXG4gKlxuICogUHJlZmVyIHRoaXMgb3ZlciBtaW50aW5nIHRoZSBpZCBhdCB0aGUgY2FsbCBzaXRlIC0gaXQgaXMgdGhlIG9uZSBwbGFjZSB0aGF0XG4gKiBkZWNpZGVzIHdoYXQgYSB0aHJvd2F3YXkgd29ya2luZyBkaXJlY3RvcnkgbG9va3MgbGlrZS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGNvbnN0IHNjcmF0Y2ggPSBzY3JhdGNoRlMoXCJyZW1vdGUtc2tpbGxzXCIpO1xuICogYXdhaXQgc2NyYXRjaC53cml0ZUZpbGUoXCJTS0lMTC5tZFwiLCBib2R5KTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2NyYXRjaEZTKHByZWZpeDogc3RyaW5nLCBvcHRpb25zOiBPc0ZpbGVTeXN0ZW1PcHRpb25zID0ge30pOiBMb2NhbEZpbGVTeXN0ZW0ge1xuICByZXR1cm4gdG1wRlMoYCR7cHJlZml4fS0ke2hhc2guaWQoKX1gLCBvcHRpb25zKTtcbn1cblxuLyoqXG4gKiBSZWJ1aWxkIHRoZSBTVEFCTEUgdGVtcCB0cmVlIG5hbWVkIGJ5IHtAbGluayBrZXl9LCBldmVyeSBjYWxsLlxuICpcbiAqIHtAbGluayBtYXRlcmlhbGl6ZX0gd3JpdGVzIGludG8gYSB0aHJvd2F3YXkge0BsaW5rIHNjcmF0Y2hGU30gcm9vdDsgb25seSBvblxuICogc3VjY2VzcyBkb2VzIHRoYXQgcm9vdCBSRVBMQUNFIHRoZSBzdGFibGUgb25lLiBUd28gcHJvcGVydGllcyBmYWxsIG91dCBvZlxuICogdGhhdCBvcmRlcmluZywgYW5kIGJvdGggYXJlIHRoZSByZWFzb24gdG8gcHJlZmVyIHRoaXMgb3ZlciB3cml0aW5nIGludG8gdGhlXG4gKiBzdGFibGUgcGF0aCBkaXJlY3RseTpcbiAqXG4gKiAtIHJlcGVhdGVkIHJ1bnMgcmV1c2UgT05FIGRpcmVjdG9yeSBpbnN0ZWFkIG9mIGxlYXZpbmcgYSBuZXcgc2NyYXRjaCBiZWhpbmRcbiAqICAgb24gZXZlcnkgYm9vdCwgYW5kXG4gKiAtIGEgcmVhZGVyIG5ldmVyIG9ic2VydmVzIGEgaGFsZi13cml0dGVuIHRyZWUsIGFuZCBhIGZhaWxlZCByZWJ1aWxkIGxlYXZlc1xuICogICB0aGUgcHJldmlvdXMgb25lIGludGFjdC5cbiAqXG4gKiBUaGUgc3dhcCBpcyBhIGByZW5hbWVgIHdpdGhpbiB0aGUgc2FtZSB0ZW1wIHJvb3QsIHNvIGl0IGlzIGF0b21pYy5cbiAqXG4gKiBAcGFyYW0ga2V5IC0gU3RhYmxlIHRlbXAtcmVsYXRpdmUgcGF0aC4gTWF5IGJlIG5lc3RlZCAoYFwic2tpbGxzL2FiYzEyM1wiYClcbiAqICAgdG8gZ2l2ZSBlYWNoIGlucHV0IGl0cyBvd24gZGlyZWN0b3J5LlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c1xuICogY29uc3QgdG9vbHMgPSBhd2FpdCByZWJ1aWxkRlMoXCJkYXRhYnJpY2tzLWFpdG9vbHNcIiwgKHNjcmF0Y2gpID0+XG4gKiAgIGluc3RhbGxJbnRvKHNjcmF0Y2gucm9vdCksXG4gKiApO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWJ1aWxkRlMoXG4gIGtleTogc3RyaW5nLFxuICBtYXRlcmlhbGl6ZTogKHNjcmF0Y2g6IExvY2FsRmlsZVN5c3RlbSkgPT4gUHJvbWlzZTx2b2lkPixcbiAgb3B0aW9uczogT3NGaWxlU3lzdGVtT3B0aW9ucyA9IHt9LFxuKTogUHJvbWlzZTxMb2NhbEZpbGVTeXN0ZW0+IHtcbiAgY29uc3Qgc2NyYXRjaCA9IHNjcmF0Y2hGUyhrZXksIG9wdGlvbnMpO1xuICBhd2FpdCBzY3JhdGNoLmluaXQoKTtcblxuICB0cnkge1xuICAgIGF3YWl0IG1hdGVyaWFsaXplKHNjcmF0Y2gpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBhd2FpdCBybShzY3JhdGNoLnJvb3QsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KS5jYXRjaCgoKSA9PiB1bmRlZmluZWQpO1xuICAgIHRocm93IGVycjtcbiAgfVxuXG4gIGNvbnN0IHN0YWJsZSA9IHRtcEZTKGtleSwgb3B0aW9ucyk7XG4gIGNvbnN0IHN0YWJsZUhvc3QgPSBwYXRoLnJlc29sdmUoc3RhYmxlLnJvb3QpO1xuICBhd2FpdCBybShzdGFibGVIb3N0LCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gIGF3YWl0IG1rZGlyKHBhdGguZGlybmFtZShzdGFibGVIb3N0KSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gIGF3YWl0IHJlbmFtZShwYXRoLnJlc29sdmUoc2NyYXRjaC5yb290KSwgc3RhYmxlSG9zdCk7XG4gIHJldHVybiBzdGFibGU7XG59XG5cbi8qKlxuICogSm9pbiB7QGxpbmsgcmVsYXRpdmVSb290fSB1bmRlciB7QGxpbmsgYmFzZX0uIEVtcHR5IC8gYC5gIGtlZXBzIHtAbGluayBiYXNlfTtcbiAqIGxlYWRpbmcgYH5gIG9yIGAvYCBpcyBzdHJpcHBlZCBzbyBhbiBhYnNvbHV0ZS1sb29raW5nIGlucHV0IGNhbm5vdCBlc2NhcGUuXG4gKi9cbmZ1bmN0aW9uIGpvaW5VbmRlckJhc2UoYmFzZTogc3RyaW5nLCByZWxhdGl2ZVJvb3Q6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHJlc3QgPSBwb3NpeFBhdGgudG9SZWxhdGl2ZVNlZ21lbnQocmVsYXRpdmVSb290KTtcbiAgcmV0dXJuIHJlc3QgPyBwYXRoLnJlc29sdmUoYmFzZSwgcmVzdCkgOiBiYXNlO1xufVxuIl19