@dbx-tools/auth 0.9.31 → 0.9.32
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/index.ts +3 -3
- package/lib/index.d.ts +3 -3
- package/lib/index.js +4 -4
- package/lib/src/databricks-auth.js +2 -2
- package/lib/src/{node-storage.d.ts → file-storage.d.ts} +1 -1
- package/lib/src/file-storage.js +207 -0
- package/lib/src/lifecycle.js +15 -11
- package/lib/src/storage.d.ts +12 -3
- package/lib/src/storage.js +65 -51
- package/lib/src/types.d.ts +14 -6
- package/lib/src/types.js +4 -4
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/databricks-auth.ts +1 -1
- package/src/{node-storage.ts → file-storage.ts} +4 -4
- package/src/lifecycle.ts +14 -10
- package/src/storage.ts +81 -47
- package/src/types.ts +17 -9
- package/lib/src/node-storage.js +0 -207
package/src/lifecycle.ts
CHANGED
|
@@ -16,9 +16,9 @@ export class TokenLifecycle {
|
|
|
16
16
|
logger.debug("created authentication lifecycle", {
|
|
17
17
|
credential: credentialId(key),
|
|
18
18
|
storage: store.name(),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
refreshBufferMs: options.refreshBufferMs,
|
|
20
|
+
lockTimeoutMs: options.lockTimeoutMs,
|
|
21
|
+
loginTimeoutMs: options.loginTimeoutMs,
|
|
22
22
|
silentProvider: provider.canAuthenticateSilently(),
|
|
23
23
|
});
|
|
24
24
|
}
|
|
@@ -32,7 +32,7 @@ export class TokenLifecycle {
|
|
|
32
32
|
return this.withLock(async () => {
|
|
33
33
|
await this.store.prepareWrite();
|
|
34
34
|
const token = validateToken(
|
|
35
|
-
await this.provider.login(this.options.
|
|
35
|
+
await this.provider.login(this.options.loginTimeoutMs),
|
|
36
36
|
);
|
|
37
37
|
await this.store.save(this.key, token);
|
|
38
38
|
logger.debug("interactive login stored credential", {
|
|
@@ -137,7 +137,7 @@ export class TokenLifecycle {
|
|
|
137
137
|
});
|
|
138
138
|
if (!login) throw error;
|
|
139
139
|
logger.debug("falling back to interactive login", this.context());
|
|
140
|
-
token = await this.provider.login(this.options.
|
|
140
|
+
token = await this.provider.login(this.options.loginTimeoutMs);
|
|
141
141
|
}
|
|
142
142
|
} else if (this.provider.canAuthenticateSilently()) {
|
|
143
143
|
logger.debug("attempting silent credential acquisition", {
|
|
@@ -145,7 +145,7 @@ export class TokenLifecycle {
|
|
|
145
145
|
loginFallback: login,
|
|
146
146
|
});
|
|
147
147
|
try {
|
|
148
|
-
token = await this.provider.authenticate(this.options.
|
|
148
|
+
token = await this.provider.authenticate(this.options.loginTimeoutMs);
|
|
149
149
|
} catch (error) {
|
|
150
150
|
logger.debug("silent credential acquisition failed", {
|
|
151
151
|
...this.context(),
|
|
@@ -154,11 +154,11 @@ export class TokenLifecycle {
|
|
|
154
154
|
});
|
|
155
155
|
if (!login) throw error;
|
|
156
156
|
logger.debug("falling back to interactive login", this.context());
|
|
157
|
-
token = await this.provider.login(this.options.
|
|
157
|
+
token = await this.provider.login(this.options.loginTimeoutMs);
|
|
158
158
|
}
|
|
159
159
|
} else if (login) {
|
|
160
160
|
logger.debug("provider requires interactive login", this.context());
|
|
161
|
-
token = await this.provider.login(this.options.
|
|
161
|
+
token = await this.provider.login(this.options.loginTimeoutMs);
|
|
162
162
|
} else {
|
|
163
163
|
throw new AuthError(
|
|
164
164
|
"oauth",
|
|
@@ -180,13 +180,17 @@ export class TokenLifecycle {
|
|
|
180
180
|
if (!isValid(token, this.now())) return false;
|
|
181
181
|
if (!token.expiry) return true;
|
|
182
182
|
return (
|
|
183
|
-
Date.parse(token.expiry) - this.now().getTime() > this.options.
|
|
183
|
+
Date.parse(token.expiry) - this.now().getTime() > this.options.refreshBufferMs
|
|
184
184
|
);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
private async withLock<T>(action: () => Promise<T>): Promise<T> {
|
|
188
188
|
logger.debug("waiting for credential lock", this.context());
|
|
189
|
-
|
|
189
|
+
// Omit the timeout (wait indefinitely) unless a positive cap is configured.
|
|
190
|
+
// The holder may run for minutes; the caller owns the overall request budget
|
|
191
|
+
// and the lock is always released when `action` settles (success or error).
|
|
192
|
+
const lockTimeoutMs = this.options.lockTimeoutMs > 0 ? this.options.lockTimeoutMs : undefined;
|
|
193
|
+
const lease = await this.store.acquireLock(this.key, lockTimeoutMs);
|
|
190
194
|
logger.debug("credential lock acquired", this.context());
|
|
191
195
|
let failure: unknown;
|
|
192
196
|
try {
|
package/src/storage.ts
CHANGED
|
@@ -3,44 +3,94 @@ import type { CredentialStore, LockAdapter, Token } from "./types.ts";
|
|
|
3
3
|
|
|
4
4
|
const logger = authLogger("memory-storage");
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/** One queued waiter for a held key: resolved when promoted, rejected on timeout. */
|
|
7
|
+
interface MemoryLockWaiter {
|
|
8
|
+
resolve: (lease: string) => void;
|
|
9
|
+
reject: (error: unknown) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A key's current owner lease plus its FIFO queue of waiters.
|
|
14
|
+
*
|
|
15
|
+
* Owner and queue live in one entry so the key is never half-present: it is
|
|
16
|
+
* dropped only when it is both unowned and unwanted. A waiter that times out
|
|
17
|
+
* removes only itself from `queue` and never disturbs `lease`, so a timed-out
|
|
18
|
+
* waiter can no longer let a later caller bypass the active holder.
|
|
19
|
+
*/
|
|
20
|
+
interface MemoryLockState {
|
|
21
|
+
lease: string;
|
|
22
|
+
queue: MemoryLockWaiter[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Process-local keyed mutex used by the in-memory store.
|
|
27
|
+
*
|
|
28
|
+
* Modeled on `@dbx-tools/core`'s `withProcessLock` coordinator: one holder per
|
|
29
|
+
* key, the rest queue in arrival order, distinct keys proceed concurrently.
|
|
30
|
+
* Waiting is purely promise-based, so a waiter never blocks the event loop and,
|
|
31
|
+
* by default, waits indefinitely - a holder may legitimately run for minutes
|
|
32
|
+
* (e.g. an interactive `databricks auth login` that opens a browser). A finite
|
|
33
|
+
* `timeoutMs` is opt-in for callers that prefer to fail fast.
|
|
34
|
+
*/
|
|
7
35
|
export class MemoryLockAdapter implements LockAdapter {
|
|
8
|
-
readonly #
|
|
9
|
-
readonly #leases = new Map<string, () => void>();
|
|
36
|
+
readonly #states = new Map<string, MemoryLockState>();
|
|
10
37
|
#sequence = 0;
|
|
11
38
|
|
|
12
|
-
|
|
39
|
+
acquire(key: string, timeoutMs?: number): Promise<string> {
|
|
13
40
|
const credential = credentialId(key);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.#tails.set(key, tail);
|
|
21
|
-
try {
|
|
22
|
-
logger.debug("waiting for memory lock", { credential, timeoutMs });
|
|
23
|
-
await withTimeout(previous, timeoutMs, `Timed out waiting for lock ${key}`);
|
|
24
|
-
} catch (error) {
|
|
25
|
-
release();
|
|
26
|
-
if (this.#tails.get(key) === tail) this.#tails.delete(key);
|
|
27
|
-
throw error;
|
|
41
|
+
const state = this.#states.get(key);
|
|
42
|
+
if (!state) {
|
|
43
|
+
const lease = this.#nextLease(key);
|
|
44
|
+
this.#states.set(key, { lease, queue: [] });
|
|
45
|
+
logger.debug("memory lock acquired", { credential });
|
|
46
|
+
return Promise.resolve(lease);
|
|
28
47
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
logger.debug("waiting for memory lock", { credential, timeoutMs });
|
|
49
|
+
return new Promise<string>((resolve, reject) => {
|
|
50
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
51
|
+
const waiter: MemoryLockWaiter = {
|
|
52
|
+
resolve: (lease) => {
|
|
53
|
+
if (timer) clearTimeout(timer);
|
|
54
|
+
logger.debug("memory lock acquired", { credential });
|
|
55
|
+
resolve(lease);
|
|
56
|
+
},
|
|
57
|
+
reject: (error) => {
|
|
58
|
+
if (timer) clearTimeout(timer);
|
|
59
|
+
reject(error);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
state.queue.push(waiter);
|
|
63
|
+
// A timer is created only for a finite positive budget. Omitted, Infinity,
|
|
64
|
+
// or non-positive all mean "wait forever" - no timer to leak or fire.
|
|
65
|
+
if (timeoutMs !== undefined && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
66
|
+
timer = setTimeout(() => {
|
|
67
|
+
const index = state.queue.indexOf(waiter);
|
|
68
|
+
if (index >= 0) state.queue.splice(index, 1);
|
|
69
|
+
waiter.reject(new Error(`Timed out waiting for lock ${key}`));
|
|
70
|
+
}, timeoutMs);
|
|
71
|
+
}
|
|
34
72
|
});
|
|
35
|
-
logger.debug("memory lock acquired", { credential });
|
|
36
|
-
return lease;
|
|
37
73
|
}
|
|
38
74
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
75
|
+
release(lease: string): Promise<void> {
|
|
76
|
+
const key = lease.split(":").slice(1).join(":");
|
|
77
|
+
logger.debug("releasing memory lock", { credential: credentialId(key) });
|
|
78
|
+
const state = this.#states.get(key);
|
|
79
|
+
// Ignore a stale release from anyone who no longer owns the key (a timed-out
|
|
80
|
+
// or superseded lease). Honoring it would revoke the lock from the holder.
|
|
81
|
+
if (!state || state.lease !== lease) return Promise.resolve();
|
|
82
|
+
const next = state.queue.shift();
|
|
83
|
+
if (!next) {
|
|
84
|
+
this.#states.delete(key);
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
}
|
|
87
|
+
state.lease = this.#nextLease(key);
|
|
88
|
+
next.resolve(state.lease);
|
|
89
|
+
return Promise.resolve();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#nextLease(key: string): string {
|
|
93
|
+
return `${++this.#sequence}:${key}`;
|
|
44
94
|
}
|
|
45
95
|
}
|
|
46
96
|
|
|
@@ -74,7 +124,7 @@ export class MemoryCredentialStore implements CredentialStore {
|
|
|
74
124
|
logger.debug("removed memory credential", { credential: credentialId(key) });
|
|
75
125
|
}
|
|
76
126
|
|
|
77
|
-
acquireLock(key: string, timeoutMs
|
|
127
|
+
acquireLock(key: string, timeoutMs?: number): Promise<string> {
|
|
78
128
|
return this.locks.acquire(key, timeoutMs);
|
|
79
129
|
}
|
|
80
130
|
|
|
@@ -86,19 +136,3 @@ export class MemoryCredentialStore implements CredentialStore {
|
|
|
86
136
|
return "memory";
|
|
87
137
|
}
|
|
88
138
|
}
|
|
89
|
-
|
|
90
|
-
async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message: string): Promise<T> {
|
|
91
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs < 0)
|
|
92
|
-
throw new TypeError("timeoutMs must be non-negative");
|
|
93
|
-
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
94
|
-
try {
|
|
95
|
-
return await Promise.race([
|
|
96
|
-
promise,
|
|
97
|
-
new Promise<never>((_, reject) => {
|
|
98
|
-
timeout = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
99
|
-
}),
|
|
100
|
-
]);
|
|
101
|
-
} finally {
|
|
102
|
-
if (timeout) clearTimeout(timeout);
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -44,17 +44,24 @@ export interface AccessToken {
|
|
|
44
44
|
scopes: string[];
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/** Shared lifecycle configuration embedded by every provider. */
|
|
47
|
+
/** Shared lifecycle configuration embedded by every provider. All times in milliseconds. */
|
|
48
48
|
export interface AuthOptions {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
refreshBufferMs: number;
|
|
50
|
+
/**
|
|
51
|
+
* Opt-in cap, in milliseconds, on waiting to acquire the per-credential
|
|
52
|
+
* refresh lock. `0` (the default) means wait indefinitely: a holder may
|
|
53
|
+
* legitimately run for minutes (an interactive login that opens a browser),
|
|
54
|
+
* and the caller owns the overall request budget. Set a positive value to
|
|
55
|
+
* fail fast instead.
|
|
56
|
+
*/
|
|
57
|
+
lockTimeoutMs: number;
|
|
58
|
+
loginTimeoutMs: number;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
const AUTH_DEFAULTS: AuthOptions = {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
refreshBufferMs: 300_000,
|
|
63
|
+
lockTimeoutMs: 0,
|
|
64
|
+
loginTimeoutMs: 900_000,
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
/** Record-style factory retained for straightforward Node/Python bridging. */
|
|
@@ -131,7 +138,8 @@ export interface DatabricksProfile extends DatabricksProfileSummary {
|
|
|
131
138
|
|
|
132
139
|
/** Cross-language lease adapter; implementations may use files, databases, or FFI. */
|
|
133
140
|
export interface LockAdapter {
|
|
134
|
-
|
|
141
|
+
/** Acquire the key's lease. Omit `timeoutMs` (or pass `0`) to wait indefinitely. */
|
|
142
|
+
acquire(key: string, timeoutMs?: number): Promise<string>;
|
|
135
143
|
release(lease: string): Promise<void>;
|
|
136
144
|
}
|
|
137
145
|
|
|
@@ -141,7 +149,7 @@ export interface CredentialStore {
|
|
|
141
149
|
prepareWrite(): Promise<void>;
|
|
142
150
|
save(key: string, token: Token): Promise<void>;
|
|
143
151
|
remove(key: string): Promise<void>;
|
|
144
|
-
acquireLock(key: string, timeoutMs
|
|
152
|
+
acquireLock(key: string, timeoutMs?: number): Promise<string>;
|
|
145
153
|
releaseLock(lease: string): Promise<void>;
|
|
146
154
|
name(): string;
|
|
147
155
|
}
|
package/lib/src/node-storage.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { chmod, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { basename, dirname, join } from "node:path";
|
|
5
|
-
import { acquireFileLock } from "@dbx-tools/core/file-lock";
|
|
6
|
-
import { authLogger, credentialId, tokenMetadata } from "./_logging.js";
|
|
7
|
-
import { AuthError } from "./errors.js";
|
|
8
|
-
import { FileLayout } from "./types.js";
|
|
9
|
-
const logger = authLogger("file-storage");
|
|
10
|
-
class NodeFileLocks {
|
|
11
|
-
lockDirectory;
|
|
12
|
-
leases = new Map();
|
|
13
|
-
constructor(lockDirectory) {
|
|
14
|
-
this.lockDirectory = lockDirectory;
|
|
15
|
-
}
|
|
16
|
-
async acquire(key, timeoutMs) {
|
|
17
|
-
logger.debug("waiting for file lock", {
|
|
18
|
-
credential: credentialId(key),
|
|
19
|
-
timeoutMs,
|
|
20
|
-
});
|
|
21
|
-
const lease = await acquireFileLock(key, { dir: this.lockDirectory, timeoutMs });
|
|
22
|
-
const id = randomUUID();
|
|
23
|
-
this.leases.set(id, lease);
|
|
24
|
-
logger.debug("file lock acquired", {
|
|
25
|
-
credential: credentialId(key),
|
|
26
|
-
backend: lease.backend,
|
|
27
|
-
});
|
|
28
|
-
return id;
|
|
29
|
-
}
|
|
30
|
-
async release(id) {
|
|
31
|
-
const lease = this.leases.get(id);
|
|
32
|
-
if (!lease)
|
|
33
|
-
return;
|
|
34
|
-
this.leases.delete(id);
|
|
35
|
-
await lease.release();
|
|
36
|
-
logger.debug("file lock released", { backend: lease.backend });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** File-backed credential store preserving unrelated Databricks CLI entries. */
|
|
40
|
-
export class FileCredentialStore {
|
|
41
|
-
root;
|
|
42
|
-
layout;
|
|
43
|
-
locks;
|
|
44
|
-
constructor(root = join(homedir(), ".databricks"), layout = FileLayout.Single, locks) {
|
|
45
|
-
this.root = root;
|
|
46
|
-
this.layout = layout;
|
|
47
|
-
this.locks = locks ?? new NodeFileLocks(join(root, "locks"));
|
|
48
|
-
}
|
|
49
|
-
async load(key) {
|
|
50
|
-
const store = this.forKey(key);
|
|
51
|
-
return store.withCacheLock(async () => {
|
|
52
|
-
const token = deserializeToken((await store.readCache()).tokens[key]);
|
|
53
|
-
logger.debug("loaded file credential", {
|
|
54
|
-
credential: credentialId(key),
|
|
55
|
-
layout: this.layout,
|
|
56
|
-
token: tokenMetadata(token),
|
|
57
|
-
});
|
|
58
|
-
return token;
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
async prepareWrite() {
|
|
62
|
-
await ensureDirectory(this.root, 0o700);
|
|
63
|
-
logger.debug("prepared credential directory", { layout: this.layout });
|
|
64
|
-
}
|
|
65
|
-
async save(key, token) {
|
|
66
|
-
const store = this.forKey(key);
|
|
67
|
-
await store.withCacheLock(async () => {
|
|
68
|
-
const cache = await store.readCache();
|
|
69
|
-
cache.tokens[key] = serializeToken(token);
|
|
70
|
-
await store.writeCache(cache);
|
|
71
|
-
logger.debug("saved file credential", {
|
|
72
|
-
credential: credentialId(key),
|
|
73
|
-
layout: this.layout,
|
|
74
|
-
token: tokenMetadata(token),
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
async remove(key) {
|
|
79
|
-
const store = this.forKey(key);
|
|
80
|
-
await store.withCacheLock(async () => {
|
|
81
|
-
const cache = await store.readCache();
|
|
82
|
-
delete cache.tokens[key];
|
|
83
|
-
await store.writeCache(cache);
|
|
84
|
-
logger.debug("removed file credential", {
|
|
85
|
-
credential: credentialId(key),
|
|
86
|
-
layout: this.layout,
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
acquireLock(key, timeoutMs) {
|
|
91
|
-
return this.locks.acquire(this.layout === FileLayout.Single ? `${this.root}:refresh` : `${this.root}:${key}:refresh`, timeoutMs);
|
|
92
|
-
}
|
|
93
|
-
releaseLock(lease) {
|
|
94
|
-
return this.locks.release(lease);
|
|
95
|
-
}
|
|
96
|
-
name() {
|
|
97
|
-
return "file";
|
|
98
|
-
}
|
|
99
|
-
forKey(key) {
|
|
100
|
-
if (this.layout === FileLayout.Single)
|
|
101
|
-
return this;
|
|
102
|
-
const digest = createHash("sha256").update(key).digest("hex");
|
|
103
|
-
return new FileCredentialStore(join(this.root, digest), FileLayout.Single, this.locks);
|
|
104
|
-
}
|
|
105
|
-
async withCacheLock(action) {
|
|
106
|
-
await ensureDirectory(this.root, 0o700);
|
|
107
|
-
const lease = await this.locks.acquire(`${this.root}:cache`, 30_000);
|
|
108
|
-
try {
|
|
109
|
-
return await action();
|
|
110
|
-
}
|
|
111
|
-
finally {
|
|
112
|
-
await this.locks.release(lease);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async readCache() {
|
|
116
|
-
try {
|
|
117
|
-
const source = await readTextFile(join(this.root, "token-cache.json"));
|
|
118
|
-
const cache = source ? JSON.parse(source) : { version: 1, tokens: {} };
|
|
119
|
-
if (cache.version !== 1 || typeof cache.tokens !== "object" || !cache.tokens) {
|
|
120
|
-
throw new AuthError("storage", "Token cache must use version 1");
|
|
121
|
-
}
|
|
122
|
-
logger.debug("read token cache", {
|
|
123
|
-
exists: Boolean(source),
|
|
124
|
-
credentialCount: Object.keys(cache.tokens).length,
|
|
125
|
-
});
|
|
126
|
-
return cache;
|
|
127
|
-
}
|
|
128
|
-
catch (cause) {
|
|
129
|
-
if (cause instanceof AuthError)
|
|
130
|
-
throw cause;
|
|
131
|
-
throw new AuthError("storage", "Could not read Databricks token cache", { cause });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async writeCache(cache) {
|
|
135
|
-
try {
|
|
136
|
-
await atomicWriteTextFile(join(this.root, "token-cache.json"), `${JSON.stringify(cache, null, 2)}\n`, 0o600);
|
|
137
|
-
logger.debug("wrote token cache", { credentialCount: Object.keys(cache.tokens).length });
|
|
138
|
-
}
|
|
139
|
-
catch (cause) {
|
|
140
|
-
throw new AuthError("storage", "Could not write Databricks token cache", { cause });
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
async function ensureDirectory(path, mode) {
|
|
145
|
-
await mkdir(path, { recursive: true, mode });
|
|
146
|
-
await chmod(path, mode);
|
|
147
|
-
}
|
|
148
|
-
async function readTextFile(path) {
|
|
149
|
-
try {
|
|
150
|
-
return await readFile(path, "utf8");
|
|
151
|
-
}
|
|
152
|
-
catch (cause) {
|
|
153
|
-
if (cause.code === "ENOENT")
|
|
154
|
-
return undefined;
|
|
155
|
-
throw cause;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async function atomicWriteTextFile(path, content, mode) {
|
|
159
|
-
const parent = dirname(path);
|
|
160
|
-
await ensureDirectory(parent, 0o700);
|
|
161
|
-
const temporary = join(parent, `.${basename(path)}-${randomUUID()}.tmp`);
|
|
162
|
-
try {
|
|
163
|
-
await writeFile(temporary, content, { mode, flag: "wx" });
|
|
164
|
-
await chmod(temporary, mode);
|
|
165
|
-
await rename(temporary, path);
|
|
166
|
-
}
|
|
167
|
-
finally {
|
|
168
|
-
await unlink(temporary).catch((cause) => {
|
|
169
|
-
if (cause.code !== "ENOENT")
|
|
170
|
-
throw cause;
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function serializeToken(token) {
|
|
175
|
-
return {
|
|
176
|
-
access_token: token.accessToken,
|
|
177
|
-
token_type: token.tokenType,
|
|
178
|
-
...(token.refreshToken ? { refresh_token: token.refreshToken } : {}),
|
|
179
|
-
...(token.expiry ? { expiry: token.expiry } : {}),
|
|
180
|
-
...(token.scopes.length ? { scopes: token.scopes } : {}),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
function deserializeToken(value) {
|
|
184
|
-
if (!value || typeof value !== "object")
|
|
185
|
-
return undefined;
|
|
186
|
-
const record = value;
|
|
187
|
-
const accessToken = stringValue(record.access_token ?? record.accessToken);
|
|
188
|
-
if (!accessToken)
|
|
189
|
-
return undefined;
|
|
190
|
-
return {
|
|
191
|
-
accessToken,
|
|
192
|
-
tokenType: stringValue(record.token_type ?? record.tokenType) ?? "Bearer",
|
|
193
|
-
...(stringValue(record.refresh_token ?? record.refreshToken)
|
|
194
|
-
? { refreshToken: stringValue(record.refresh_token ?? record.refreshToken) }
|
|
195
|
-
: {}),
|
|
196
|
-
...(stringValue(record.expiry ?? record.expires_at)
|
|
197
|
-
? { expiry: stringValue(record.expiry ?? record.expires_at) }
|
|
198
|
-
: {}),
|
|
199
|
-
scopes: Array.isArray(record.scopes)
|
|
200
|
-
? record.scopes.filter((scope) => typeof scope === "string")
|
|
201
|
-
: [],
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
function stringValue(value) {
|
|
205
|
-
return typeof value === "string" && value.trim() ? value : undefined;
|
|
206
|
-
}
|
|
207
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibm9kZS1zdG9yYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL25vZGUtc3RvcmFnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNyRCxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNyRixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUVwRCxPQUFPLEVBQUUsZUFBZSxFQUFzQixNQUFNLDJCQUEyQixDQUFDO0FBRWhGLE9BQU8sRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN4RSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRXhDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFeEMsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0FBTzFDLE1BQU0sYUFBYTtJQUdZO0lBRlosTUFBTSxHQUFHLElBQUksR0FBRyxFQUF5QixDQUFDO0lBRTNELFlBQTZCLGFBQXFCO1FBQXJCLGtCQUFhLEdBQWIsYUFBYSxDQUFRO0lBQUcsQ0FBQztJQUV0RCxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQVcsRUFBRSxTQUFpQjtRQUMxQyxNQUFNLENBQUMsS0FBSyxDQUFDLHVCQUF1QixFQUFFO1lBQ3BDLFVBQVUsRUFBRSxZQUFZLENBQUMsR0FBRyxDQUFDO1lBQzdCLFNBQVM7U0FDVixDQUFDLENBQUM7UUFDSCxNQUFNLEtBQUssR0FBRyxNQUFNLGVBQWUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxH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dCB7IGNyZWF0ZUhhc2gsIHJhbmRvbVVVSUQgfSBmcm9tIFwibm9kZTpjcnlwdG9cIjtcbmltcG9ydCB7IGNobW9kLCBta2RpciwgcmVhZEZpbGUsIHJlbmFtZSwgdW5saW5rLCB3cml0ZUZpbGUgfSBmcm9tIFwibm9kZTpmcy9wcm9taXNlc1wiO1xuaW1wb3J0IHsgaG9tZWRpciB9IGZyb20gXCJub2RlOm9zXCI7XG5pbXBvcnQgeyBiYXNlbmFtZSwgZGlybmFtZSwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcblxuaW1wb3J0IHsgYWNxdWlyZUZpbGVMb2NrLCB0eXBlIEZpbGVMb2NrTGVhc2UgfSBmcm9tIFwiQGRieC10b29scy9jb3JlL2ZpbGUtbG9ja1wiO1xuXG5pbXBvcnQgeyBhdXRoTG9nZ2VyLCBjcmVkZW50aWFsSWQsIHRva2VuTWV0YWRhdGEgfSBmcm9tIFwiLi9fbG9nZ2luZy50c1wiO1xuaW1wb3J0IHsgQXV0aEVycm9yIH0gZnJvbSBcIi4vZXJyb3JzLnRzXCI7XG5pbXBvcnQgdHlwZSB7IENyZWRlbnRpYWxTdG9yZSwgTG9ja0FkYXB0ZXIsIFRva2VuIH0gZnJvbSBcIi4vdHlwZXMudHNcIjtcbmltcG9ydCB7IEZpbGVMYXlvdXQgfSBmcm9tIFwiLi90eXBlcy50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBhdXRoTG9nZ2VyKFwiZmlsZS1zdG9yYWdlXCIpO1xuXG5pbnRlcmZhY2UgVG9rZW5DYWNoZSB7XG4gIHZlcnNpb246IG51bWJlcjtcbiAgdG9rZW5zOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbn1cblxuY2xhc3MgTm9kZUZpbGVMb2NrcyBpbXBsZW1lbnRzIExvY2tBZGFwdGVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBsZWFzZXMgPSBuZXcgTWFwPHN0cmluZywgRmlsZUxvY2tMZWFzZT4oKTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IGxvY2tEaXJlY3Rvcnk6IHN0cmluZykge31cblxuICBhc3luYyBhY3F1aXJlKGtleTogc3RyaW5nLCB0aW1lb3V0TXM6IG51bWJlcik6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgbG9nZ2VyLmRlYnVnKFwid2FpdGluZyBmb3IgZmlsZSBsb2NrXCIsIHtcbiAgICAgIGNyZWRlbnRpYWw6IGNyZWRlbnRpYWxJZChrZXkpLFxuICAgICAgdGltZW91dE1zLFxuICAgIH0pO1xuICAgIGNvbnN0IGxlYXNlID0gYXdhaXQgYWNxdWlyZUZpbGVMb2NrKGtleSwgeyBkaXI6IHRoaXMubG9ja0RpcmVjdG9yeSwgdGltZW91dE1zIH0pO1xuICAgIGNvbnN0IGlkID0gcmFuZG9tVVVJRCgpO1xuICAgIHRoaXMubGVhc2VzLnNldChpZCwgbGVhc2UpO1xuICAgIGxvZ2dlci5kZWJ1ZyhcImZpbGUgbG9jayBhY3F1aXJlZFwiLCB7XG4gICAgICBjcmVkZW50aWFsOiBjcmVkZW50aWFsSWQoa2V5KSxcbiAgICAgIGJhY2tlbmQ6IGxlYXNlLmJhY2tlbmQsXG4gICAgfSk7XG4gICAgcmV0dXJuIGlkO1xuICB9XG5cbiAgYXN5bmMgcmVsZWFzZShpZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbGVhc2UgPSB0aGlzLmxlYXNlcy5nZXQoaWQpO1xuICAgIGlmICghbGVhc2UpIHJldHVybjtcbiAgICB0aGlzLmxlYXNlcy5kZWxldGUoaWQpO1xuICAgIGF3YWl0IGxlYXNlLnJlbGVhc2UoKTtcbiAgICBsb2dnZXIuZGVidWcoXCJmaWxlIGxvY2sgcmVsZWFzZWRcIiwgeyBiYWNrZW5kOiBsZWFzZS5iYWNrZW5kIH0pO1xuICB9XG59XG5cbi8qKiBGaWxlLWJhY2tlZCBjcmVkZW50aWFsIHN0b3JlIHByZXNlcnZpbmcgdW5yZWxhdGVkIERhdGFicmlja3MgQ0xJIGVudHJpZXMuICovXG5leHBvcnQgY2xhc3MgRmlsZUNyZWRlbnRpYWxTdG9yZSBpbXBsZW1lbnRzIENyZWRlbnRpYWxTdG9yZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgbG9ja3M6IExvY2tBZGFwdGVyO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgcm9vdCA9IGpvaW4oaG9tZWRpcigpLCBcIi5kYXRhYnJpY2tzXCIpLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbGF5b3V0ID0gRmlsZUxheW91dC5TaW5nbGUsXG4gICAgbG9ja3M/OiBMb2NrQWRhcHRlcixcbiAgKSB7XG4gICAgdGhpcy5sb2NrcyA9IGxvY2tzID8/IG5ldyBOb2RlRmlsZUxvY2tzKGpvaW4ocm9vdCwgXCJsb2Nrc1wiKSk7XG4gIH1cblxuICBhc3luYyBsb2FkKGtleTogc3RyaW5nKTogUHJvbWlzZTxUb2tlbiB8IHVuZGVmaW5lZD4ge1xuICAgIGNvbnN0IHN0b3JlID0gdGhpcy5mb3JLZXkoa2V5KTtcbiAgICByZXR1cm4gc3RvcmUud2l0aENhY2hlTG9jayhhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB0b2tlbiA9IGRlc2VyaWFsaXplVG9rZW4oKGF3YWl0IHN0b3JlLnJlYWRDYWNoZSgpKS50b2tlbnNba2V5XSk7XG4gICAgICBsb2dnZXIuZGVidWcoXCJsb2FkZWQgZmlsZSBjcmVkZW50aWFsXCIsIHtcbiAgICAgICAgY3JlZGVudGlhbDogY3JlZGVudGlhbElkKGtleSksXG4gICAgICAgIGxheW91dDogdGhpcy5sYXlvdXQsXG4gICAgICAgIHRva2VuOiB0b2tlbk1ldGFkYXRhKHRva2VuKSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHRva2VuO1xuICAgIH0pO1xuICB9XG5cbiAgYXN5bmMgcHJlcGFyZVdyaXRlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IGVuc3VyZURpcmVjdG9yeSh0aGlzLnJvb3QsIDBvNzAwKTtcbiAgICBsb2dnZXIuZGVidWcoXCJwcmVwYXJlZCBjcmVkZW50aWFsIGRpcmVjdG9yeVwiLCB7IGxheW91dDogdGhpcy5sYXlvdXQgfSk7XG4gIH1cblxuICBhc3luYyBzYXZlKGtleTogc3RyaW5nLCB0b2tlbjogVG9rZW4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzdG9yZSA9IHRoaXMuZm9yS2V5KGtleSk7XG4gICAgYXdhaXQgc3RvcmUud2l0aENhY2hlTG9jayhhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjYWNoZSA9IGF3YWl0IHN0b3JlLnJlYWRDYWNoZSgpO1xuICAgICAgY2FjaGUudG9rZW5zW2tleV0gPSBzZXJpYWxpemVUb2tlbih0b2tlbik7XG4gICAgICBhd2FpdCBzdG9yZS53cml0ZUNhY2hlKGNhY2hlKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcInNhdmVkIGZpbGUgY3JlZGVudGlhbFwiLCB7XG4gICAgICAgIGNyZWRlbnRpYWw6IGNyZWRlbnRpYWxJZChrZXkpLFxuICAgICAgICBsYXlvdXQ6IHRoaXMubGF5b3V0LFxuICAgICAgICB0b2tlbjogdG9rZW5NZXRhZGF0YSh0b2tlbiksXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIHJlbW92ZShrZXk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHN0b3JlID0gdGhpcy5mb3JLZXkoa2V5KTtcbiAgICBhd2FpdCBzdG9yZS53aXRoQ2FjaGVMb2NrKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgc3RvcmUucmVhZENhY2hlKCk7XG4gICAgICBkZWxldGUgY2FjaGUudG9rZW5zW2tleV07XG4gICAgICBhd2FpdCBzdG9yZS53cml0ZUNhY2hlKGNhY2hlKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcInJlbW92ZWQgZmlsZSBjcmVkZW50aWFsXCIsIHtcbiAgICAgICAgY3JlZGVudGlhbDogY3JlZGVudGlhbElkKGtleSksXG4gICAgICAgIGxheW91dDogdGhpcy5sYXlvdXQsXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIGFjcXVpcmVMb2NrKGtleTogc3RyaW5nLCB0aW1lb3V0TXM6IG51bWJlcik6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgcmV0dXJuIHRoaXMubG9ja3MuYWNxdWlyZShcbiAgICAgIHRoaXMubGF5b3V0ID09PSBGaWxlTGF5b3V0LlNpbmdsZSA/IGAke3RoaXMucm9vdH06cmVmcmVzaGAgOiBgJHt0aGlzLnJvb3R9OiR7a2V5fTpyZWZyZXNoYCxcbiAgICAgIHRpbWVvdXRNcyxcbiAgICApO1xuICB9XG5cbiAgcmVsZWFzZUxvY2sobGVhc2U6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmxvY2tzLnJlbGVhc2UobGVhc2UpO1xuICB9XG5cbiAgbmFtZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiBcImZpbGVcIjtcbiAgfVxuXG4gIHByaXZhdGUgZm9yS2V5KGtleTogc3RyaW5nKTogRmlsZUNyZWRlbnRpYWxTdG9yZSB7XG4gICAgaWYgKHRoaXMubGF5b3V0ID09PSBGaWxlTGF5b3V0LlNpbmdsZSkgcmV0dXJuIHRoaXM7XG4gICAgY29uc3QgZGlnZXN0ID0gY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoa2V5KS5kaWdlc3QoXCJoZXhcIik7XG4gICAgcmV0dXJuIG5ldyBGaWxlQ3JlZGVudGlhbFN0b3JlKGpvaW4odGhpcy5yb290LCBkaWdlc3QpLCBGaWxlTGF5b3V0LlNpbmdsZSwgdGhpcy5sb2Nrcyk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHdpdGhDYWNoZUxvY2s8VD4oYWN0aW9uOiAoKSA9PiBQcm9taXNlPFQ+KTogUHJvbWlzZTxUPiB7XG4gICAgYXdhaXQgZW5zdXJlRGlyZWN0b3J5KHRoaXMucm9vdCwgMG83MDApO1xuICAgIGNvbnN0IGxlYXNlID0gYXdhaXQgdGhpcy5sb2Nrcy5hY3F1aXJlKGAke3RoaXMucm9vdH06Y2FjaGVgLCAzMF8wMDApO1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgYWN0aW9uKCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGF3YWl0IHRoaXMubG9ja3MucmVsZWFzZShsZWFzZSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWFkQ2FjaGUoKTogUHJvbWlzZTxUb2tlbkNhY2hlPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHNvdXJjZSA9IGF3YWl0IHJlYWRUZXh0RmlsZShqb2luKHRoaXMucm9vdCwgXCJ0b2tlbi1jYWNoZS5qc29uXCIpKTtcbiAgICAgIGNvbnN0IGNhY2hlID0gc291cmNlID8gKEpTT04ucGFyc2Uoc291cmNlKSBhcyBUb2tlbkNhY2hlKSA6IHsgdmVyc2lvbjogMSwgdG9rZW5zOiB7fSB9O1xuICAgICAgaWYgKGNhY2hlLnZlcnNpb24gIT09IDEgfHwgdHlwZW9mIGNhY2hlLnRva2VucyAhPT0gXCJvYmplY3RcIiB8fCAhY2FjaGUudG9rZW5zKSB7XG4gICAgICAgIHRocm93IG5ldyBBdXRoRXJyb3IoXCJzdG9yYWdlXCIsIFwiVG9rZW4gY2FjaGUgbXVzdCB1c2UgdmVyc2lvbiAxXCIpO1xuICAgICAgfVxuICAgICAgbG9nZ2VyLmRlYnVnKFwicmVhZCB0b2tlbiBjYWNoZVwiLCB7XG4gICAgICAgIGV4aXN0czogQm9vbGVhbihzb3VyY2UpLFxuICAgICAgICBjcmVkZW50aWFsQ291bnQ6IE9iamVjdC5rZXlzKGNhY2hlLnRva2VucykubGVuZ3RoLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gY2FjaGU7XG4gICAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICAgIGlmIChjYXVzZSBpbnN0YW5jZW9mIEF1dGhFcnJvcikgdGhyb3cgY2F1c2U7XG4gICAgICB0aHJvdyBuZXcgQXV0aEVycm9yKFwic3RvcmFnZVwiLCBcIkNvdWxkIG5vdCByZWFkIERhdGFicmlja3MgdG9rZW4gY2FjaGVcIiwgeyBjYXVzZSB9KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHdyaXRlQ2FjaGUoY2FjaGU6IFRva2VuQ2FjaGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgYXRvbWljV3JpdGVUZXh0RmlsZShcbiAgICAgICAgam9pbih0aGlzLnJvb3QsIFwidG9rZW4tY2FjaGUuanNvblwiKSxcbiAgICAgICAgYCR7SlNPTi5zdHJpbmdpZnkoY2FjaGUsIG51bGwsIDIpfVxcbmAsXG4gICAgICAgIDBvNjAwLFxuICAgICAgKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcIndyb3RlIHRva2VuIGNhY2hlXCIsIHsgY3JlZGVudGlhbENvdW50OiBPYmplY3Qua2V5cyhjYWNoZS50b2tlbnMpLmxlbmd0aCB9KTtcbiAgICB9IGNhdGNoIChjYXVzZSkge1xuICAgICAgdGhyb3cgbmV3IEF1dGhFcnJvcihcInN0b3JhZ2VcIiwgXCJDb3VsZCBub3Qgd3JpdGUgRGF0YWJyaWNrcyB0b2tlbiBjYWNoZVwiLCB7IGNhdXNlIH0pO1xuICAgIH1cbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBlbnN1cmVEaXJlY3RvcnkocGF0aDogc3RyaW5nLCBtb2RlOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgbWtkaXIocGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIG1vZGUgfSk7XG4gIGF3YWl0IGNobW9kKHBhdGgsIG1vZGUpO1xufVxuXG5hc3luYyBmdW5jdGlvbiByZWFkVGV4dEZpbGUocGF0aDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCB1bmRlZmluZWQ+IHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgcmVhZEZpbGUocGF0aCwgXCJ1dGY4XCIpO1xuICB9IGNhdGNoIChjYXVzZSkge1xuICAgIGlmICgoY2F1c2UgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uKS5jb2RlID09PSBcIkVOT0VOVFwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIHRocm93IGNhdXNlO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGF0b21pY1dyaXRlVGV4dEZpbGUocGF0aDogc3RyaW5nLCBjb250ZW50OiBzdHJpbmcsIG1vZGU6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBwYXJlbnQgPSBkaXJuYW1lKHBhdGgpO1xuICBhd2FpdCBlbnN1cmVEaXJlY3RvcnkocGFyZW50LCAwbzcwMCk7XG4gIGNvbnN0IHRlbXBvcmFyeSA9IGpvaW4ocGFyZW50LCBgLiR7YmFzZW5hbWUocGF0aCl9LSR7cmFuZG9tVVVJRCgpfS50bXBgKTtcbiAgdHJ5IHtcbiAgICBhd2FpdCB3cml0ZUZpbGUodGVtcG9yYXJ5LCBjb250ZW50LCB7IG1vZGUsIGZsYWc6IFwid3hcIiB9KTtcbiAgICBhd2FpdCBjaG1vZCh0ZW1wb3JhcnksIG1vZGUpO1xuICAgIGF3YWl0IHJlbmFtZSh0ZW1wb3JhcnksIHBhdGgpO1xuICB9IGZpbmFsbHkge1xuICAgIGF3YWl0IHVubGluayh0ZW1wb3JhcnkpLmNhdGNoKChjYXVzZTogTm9kZUpTLkVycm5vRXhjZXB0aW9uKSA9PiB7XG4gICAgICBpZiAoY2F1c2UuY29kZSAhPT0gXCJFTk9FTlRcIikgdGhyb3cgY2F1c2U7XG4gICAgfSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gc2VyaWFsaXplVG9rZW4odG9rZW46IFRva2VuKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4ge1xuICAgIGFjY2Vzc190b2tlbjogdG9rZW4uYWNjZXNzVG9rZW4sXG4gICAgdG9rZW5fdHlwZTogdG9rZW4udG9rZW5UeXBlLFxuICAgIC4uLih0b2tlbi5yZWZyZXNoVG9rZW4gPyB7IHJlZnJlc2hfdG9rZW46IHRva2VuLnJlZnJlc2hUb2tlbiB9IDoge30pLFxuICAgIC4uLih0b2tlbi5leHBpcnkgPyB7IGV4cGlyeTogdG9rZW4uZXhwaXJ5IH0gOiB7fSksXG4gICAgLi4uKHRva2VuLnNjb3Blcy5sZW5ndGggPyB7IHNjb3BlczogdG9rZW4uc2NvcGVzIH0gOiB7fSksXG4gIH07XG59XG5cbmZ1bmN0aW9uIGRlc2VyaWFsaXplVG9rZW4odmFsdWU6IHVua25vd24pOiBUb2tlbiB8IHVuZGVmaW5lZCB7XG4gIGlmICghdmFsdWUgfHwgdHlwZW9mIHZhbHVlICE9PSBcIm9iamVjdFwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCByZWNvcmQgPSB2YWx1ZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgY29uc3QgYWNjZXNzVG9rZW4gPSBzdHJpbmdWYWx1ZShyZWNvcmQuYWNjZXNzX3Rva2VuID8/IHJlY29yZC5hY2Nlc3NUb2tlbik7XG4gIGlmICghYWNjZXNzVG9rZW4pIHJldHVybiB1bmRlZmluZWQ7XG4gIHJldHVybiB7XG4gICAgYWNjZXNzVG9rZW4sXG4gICAgdG9rZW5UeXBlOiBzdHJpbmdWYWx1ZShyZWNvcmQudG9rZW5fdHlwZSA/PyByZWNvcmQudG9rZW5UeXBlKSA/PyBcIkJlYXJlclwiLFxuICAgIC4uLihzdHJpbmdWYWx1ZShyZWNvcmQucmVmcmVzaF90b2tlbiA/PyByZWNvcmQucmVmcmVzaFRva2VuKVxuICAgICAgPyB7IHJlZnJlc2hUb2tlbjogc3RyaW5nVmFsdWUocmVjb3JkLnJlZnJlc2hfdG9rZW4gPz8gcmVjb3JkLnJlZnJlc2hUb2tlbikgfVxuICAgICAgOiB7fSksXG4gICAgLi4uKHN0cmluZ1ZhbHVlKHJlY29yZC5leHBpcnkgPz8gcmVjb3JkLmV4cGlyZXNfYXQpXG4gICAgICA/IHsgZXhwaXJ5OiBzdHJpbmdWYWx1ZShyZWNvcmQuZXhwaXJ5ID8/IHJlY29yZC5leHBpcmVzX2F0KSB9XG4gICAgICA6IHt9KSxcbiAgICBzY29wZXM6IEFycmF5LmlzQXJyYXkocmVjb3JkLnNjb3BlcylcbiAgICAgID8gcmVjb3JkLnNjb3Blcy5maWx0ZXIoKHNjb3BlKTogc2NvcGUgaXMgc3RyaW5nID0+IHR5cGVvZiBzY29wZSA9PT0gXCJzdHJpbmdcIilcbiAgICAgIDogW10sXG4gIH07XG59XG5cbmZ1bmN0aW9uIHN0cmluZ1ZhbHVlKHZhbHVlOiB1bmtub3duKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiAmJiB2YWx1ZS50cmltKCkgPyB2YWx1ZSA6IHVuZGVmaW5lZDtcbn1cbiJdfQ==
|