@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 +60 -0
- package/index.ts +12 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +10 -0
- package/lib/src/local-fs.d.ts +154 -0
- package/lib/src/local-fs.js +330 -0
- package/lib/src/local-path.d.ts +23 -0
- package/lib/src/local-path.js +30 -0
- package/lib/src/os-path.d.ts +50 -0
- package/lib/src/os-path.js +143 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +55 -0
- package/src/local-fs.ts +461 -0
- package/src/local-path.ts +42 -0
- package/src/os-path.ts +167 -0
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
|