@pipeline-builder/api-core 4.1.1 → 4.1.3
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/lib/middleware/auth.d.ts +19 -0
- package/lib/middleware/auth.js +45 -10
- package/lib/services/audit-spool.d.ts +2 -3
- package/lib/services/audit-spool.js +3 -4
- package/lib/services/cache-service.d.ts +59 -0
- package/lib/services/cache-service.js +165 -16
- package/lib/services/durable-event-bus.js +15 -5
- package/lib/services/env-redis.d.ts +85 -33
- package/lib/services/env-redis.js +154 -74
- package/lib/services/leader-lock.d.ts +6 -3
- package/lib/services/leader-lock.js +20 -5
- package/lib/services/quota.d.ts +44 -10
- package/lib/services/quota.js +85 -49
- package/lib/services/sse-ticket-store.d.ts +1 -1
- package/lib/services/sse-ticket-store.js +2 -2
- package/lib/services/token-revocation.d.ts +24 -1
- package/lib/services/token-revocation.js +69 -5
- package/lib/types/common.d.ts +8 -0
- package/lib/types/common.js +1 -1
- package/lib/types/permissions.d.ts +1 -1
- package/lib/types/permissions.js +2 -2
- package/lib/utils/response.d.ts +9 -1
- package/lib/utils/response.js +29 -3
- package/package.json +1 -1
|
@@ -1,44 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* The ONE place Redis connection settings are read from the environment.
|
|
3
|
+
*
|
|
4
|
+
* Two shapes, exactly one of which may be set:
|
|
5
|
+
*
|
|
6
|
+
* - **Standalone** — `REDIS_URL` (`redis://host:6379[/db]`, or `rediss://` for
|
|
7
|
+
* TLS). Credentials may sit in the URL or in `REDIS_PASSWORD`.
|
|
8
|
+
* - **HA (Sentinel)** — `REDIS_SENTINELS` (comma-separated `host:port`) plus
|
|
9
|
+
* `REDIS_SENTINEL_MASTER` (default `mymaster`) and optional
|
|
10
|
+
* `REDIS_SENTINEL_PASSWORD`. `REDIS_PASSWORD` authenticates to the data nodes.
|
|
11
|
+
* ioredis follows the master across a failover.
|
|
12
|
+
*
|
|
13
|
+
* Neither set → Redis is off, and each caller degrades to its documented
|
|
14
|
+
* no-Redis behavior.
|
|
15
|
+
*
|
|
16
|
+
* `REDIS_HOST` / `REDIS_PORT` are not read. `REDIS_HOST` is refused outright so a
|
|
17
|
+
* stale config fails at startup instead of silently running without Redis.
|
|
18
|
+
* `REDIS_PORT` can't be refused: Kubernetes injects `REDIS_PORT=tcp://<ip>:6379`
|
|
19
|
+
* into every pod in a namespace with a Service named `redis`, which is exactly
|
|
20
|
+
* why a host/port pair was unsafe to parse.
|
|
21
|
+
*
|
|
22
|
+
* Misconfiguration throws {@link RedisConfigError}. Writers and readers of the
|
|
23
|
+
* same keys (revocation, step-up, leader locks) must resolve Redis identically;
|
|
24
|
+
* a config that quietly resolved to "no Redis" on one side is how revocation
|
|
25
|
+
* once did nothing at all.
|
|
26
|
+
*/
|
|
27
|
+
/** Thrown for a Redis environment that can't be used as written. */
|
|
28
|
+
export declare class RedisConfigError extends Error {
|
|
29
|
+
constructor(message: string);
|
|
30
|
+
}
|
|
31
|
+
/** Resolved connection, independent of any client library. */
|
|
32
|
+
export type RedisConnection = {
|
|
33
|
+
mode: 'sentinel';
|
|
34
|
+
sentinels: Array<{
|
|
35
|
+
host: string;
|
|
36
|
+
port: number;
|
|
37
|
+
}>;
|
|
38
|
+
masterName: string;
|
|
39
|
+
password?: string;
|
|
40
|
+
sentinelPassword?: string;
|
|
41
|
+
} | {
|
|
42
|
+
mode: 'url';
|
|
43
|
+
url: string;
|
|
44
|
+
password?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Parse `REDIS_SENTINELS` into `{ host, port }[]`. Blank entries are skipped and
|
|
48
|
+
* a missing port means the Sentinel default 26379; a port that isn't a valid
|
|
49
|
+
* TCP port is a configuration error.
|
|
6
50
|
*/
|
|
7
51
|
export declare function parseSentinels(raw: string | undefined): Array<{
|
|
8
52
|
host: string;
|
|
9
53
|
port: number;
|
|
10
54
|
}>;
|
|
11
55
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* - Returns `null` when NONE of sentinels/url/host is set, so a caller degrades
|
|
29
|
-
* to its no-Redis fallback (run-on-every-pod / best-effort / fail-open).
|
|
30
|
-
* - FAIL-SAFE: any ioredis load or construction error is swallowed and returns
|
|
31
|
-
* `null` — Redis being unavailable must never crash the importing service.
|
|
32
|
-
* - `ioredis` is a runtime-only optional dep loaded via `createRequire`, so this
|
|
33
|
-
* module stays importable in CJS bundles and where Redis isn't installed.
|
|
34
|
-
* - `maxRetriesPerRequest: 1` + `enableOfflineQueue: false`: commands fail fast
|
|
35
|
-
* rather than queueing during an outage (callers are fail-open/best-effort).
|
|
36
|
-
* - An `error` listener is attached (ioredis auto-reconnects) so a dropped
|
|
37
|
-
* connection can't surface as an unhandled `'error'` event that crashes Node.
|
|
56
|
+
* Resolve the Redis connection from `env`, or `null` when Redis isn't
|
|
57
|
+
* configured. Throws {@link RedisConfigError} for an unusable configuration.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveRedisConnection(env?: NodeJS.ProcessEnv): RedisConnection | null;
|
|
60
|
+
/** A log-safe description of a connection (never includes credentials). */
|
|
61
|
+
export declare function describeRedisConnection(conn: RedisConnection): Record<string, unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* Construct an ioredis client for a resolved connection. `options` are merged
|
|
64
|
+
* into the client options (e.g. `{ db, maxRetriesPerRequest: null }` for BullMQ).
|
|
65
|
+
* An `error` listener is always attached so a dropped connection is logged
|
|
66
|
+
* rather than surfacing as an unhandled `'error'` event that crashes Node.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createRedisClient<T = unknown>(conn: RedisConnection, label: string, options?: Record<string, unknown>): T;
|
|
69
|
+
/**
|
|
70
|
+
* The shared application Redis client: `null` when Redis isn't configured, so
|
|
71
|
+
* the caller degrades to its no-Redis behavior.
|
|
38
72
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
73
|
+
* Commands fail fast (`maxRetriesPerRequest: 1`, no offline queue) because the
|
|
74
|
+
* request-path callers are best-effort or fail closed on their own terms — a
|
|
75
|
+
* request must not hang behind a Redis outage. That includes the moments before
|
|
76
|
+
* the first connection completes: a background caller that runs at startup must
|
|
77
|
+
* either tolerate a rejected command or wait with {@link whenRedisReady}.
|
|
41
78
|
*
|
|
42
|
-
* @
|
|
79
|
+
* Throws {@link RedisConfigError} for a bad configuration, and if Redis is
|
|
80
|
+
* configured but `ioredis` can't be loaded — both are deployment errors that
|
|
81
|
+
* must not quietly turn Redis-backed guarantees off.
|
|
43
82
|
*/
|
|
44
83
|
export declare function createEnvRedisClient<T = unknown>(label: string): T | null;
|
|
84
|
+
/** The subset of ioredis needed to wait for a connection. */
|
|
85
|
+
export interface ReadyAwareRedis {
|
|
86
|
+
status?: string;
|
|
87
|
+
once?(event: string, cb: (...args: unknown[]) => void): unknown;
|
|
88
|
+
off?(event: string, cb: (...args: unknown[]) => void): unknown;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Resolve once `client` is connected and ready for commands, or reject after
|
|
92
|
+
* `timeoutMs`. For startup work (creating a consumer group, subscribing) that
|
|
93
|
+
* otherwise races the first connection. A client without ioredis' `status`
|
|
94
|
+
* (e.g. a test double) counts as ready.
|
|
95
|
+
*/
|
|
96
|
+
export declare function whenRedisReady(client: ReadyAwareRedis, timeoutMs?: number): Promise<void>;
|
|
@@ -4,91 +4,171 @@ import { createRequire } from 'module';
|
|
|
4
4
|
import { createLogger } from '../utils/logger.js';
|
|
5
5
|
const logger = createLogger('env-redis');
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* The ONE place Redis connection settings are read from the environment.
|
|
8
|
+
*
|
|
9
|
+
* Two shapes, exactly one of which may be set:
|
|
10
|
+
*
|
|
11
|
+
* - **Standalone** — `REDIS_URL` (`redis://host:6379[/db]`, or `rediss://` for
|
|
12
|
+
* TLS). Credentials may sit in the URL or in `REDIS_PASSWORD`.
|
|
13
|
+
* - **HA (Sentinel)** — `REDIS_SENTINELS` (comma-separated `host:port`) plus
|
|
14
|
+
* `REDIS_SENTINEL_MASTER` (default `mymaster`) and optional
|
|
15
|
+
* `REDIS_SENTINEL_PASSWORD`. `REDIS_PASSWORD` authenticates to the data nodes.
|
|
16
|
+
* ioredis follows the master across a failover.
|
|
17
|
+
*
|
|
18
|
+
* Neither set → Redis is off, and each caller degrades to its documented
|
|
19
|
+
* no-Redis behavior.
|
|
20
|
+
*
|
|
21
|
+
* `REDIS_HOST` / `REDIS_PORT` are not read. `REDIS_HOST` is refused outright so a
|
|
22
|
+
* stale config fails at startup instead of silently running without Redis.
|
|
23
|
+
* `REDIS_PORT` can't be refused: Kubernetes injects `REDIS_PORT=tcp://<ip>:6379`
|
|
24
|
+
* into every pod in a namespace with a Service named `redis`, which is exactly
|
|
25
|
+
* why a host/port pair was unsafe to parse.
|
|
26
|
+
*
|
|
27
|
+
* Misconfiguration throws {@link RedisConfigError}. Writers and readers of the
|
|
28
|
+
* same keys (revocation, step-up, leader locks) must resolve Redis identically;
|
|
29
|
+
* a config that quietly resolved to "no Redis" on one side is how revocation
|
|
30
|
+
* once did nothing at all.
|
|
31
|
+
*/
|
|
32
|
+
/** Thrown for a Redis environment that can't be used as written. */
|
|
33
|
+
export class RedisConfigError extends Error {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'RedisConfigError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse `REDIS_SENTINELS` into `{ host, port }[]`. Blank entries are skipped and
|
|
41
|
+
* a missing port means the Sentinel default 26379; a port that isn't a valid
|
|
42
|
+
* TCP port is a configuration error.
|
|
11
43
|
*/
|
|
12
44
|
export function parseSentinels(raw) {
|
|
13
45
|
if (!raw)
|
|
14
46
|
return [];
|
|
15
47
|
return raw.split(',').map((s) => s.trim()).filter(Boolean).map((entry) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
48
|
+
const idx = entry.lastIndexOf(':');
|
|
49
|
+
const host = idx === -1 ? entry : entry.slice(0, idx);
|
|
50
|
+
const portText = idx === -1 ? '' : entry.slice(idx + 1);
|
|
51
|
+
const port = portText === '' ? 26379 : Number(portText);
|
|
52
|
+
if (!host || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
53
|
+
throw new RedisConfigError(`REDIS_SENTINELS entry "${entry}" is not host:port`);
|
|
54
|
+
}
|
|
55
|
+
return { host, port };
|
|
56
|
+
});
|
|
19
57
|
}
|
|
20
58
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* `REDIS_SENTINELS` + `REDIS_SENTINEL_MASTER`. The shared construction was
|
|
24
|
-
* previously duplicated across the leader-lock, audit-spool, and
|
|
25
|
-
* token-revocation env factories.
|
|
26
|
-
*
|
|
27
|
-
* Connection modes (first that matches wins):
|
|
28
|
-
* 1. SENTINEL — `REDIS_SENTINELS` set (comma-separated `host:port`): ioredis
|
|
29
|
-
* resolves the current master from the sentinels (`name` = `REDIS_SENTINEL_MASTER`,
|
|
30
|
-
* default `mymaster`) and transparently reconnects to the new master after a
|
|
31
|
-
* failover. This is how the app gets HA against a single-node Redis loss —
|
|
32
|
-
* also the shape a managed ElastiCache (cluster-mode-disabled) speaks.
|
|
33
|
-
* 2. URL — `REDIS_URL`.
|
|
34
|
-
* 3. HOST/PORT — `REDIS_HOST`(+`REDIS_PORT`).
|
|
35
|
-
*
|
|
36
|
-
* Semantics every caller relies on (kept identical across the consolidation):
|
|
37
|
-
* - Returns `null` when NONE of sentinels/url/host is set, so a caller degrades
|
|
38
|
-
* to its no-Redis fallback (run-on-every-pod / best-effort / fail-open).
|
|
39
|
-
* - FAIL-SAFE: any ioredis load or construction error is swallowed and returns
|
|
40
|
-
* `null` — Redis being unavailable must never crash the importing service.
|
|
41
|
-
* - `ioredis` is a runtime-only optional dep loaded via `createRequire`, so this
|
|
42
|
-
* module stays importable in CJS bundles and where Redis isn't installed.
|
|
43
|
-
* - `maxRetriesPerRequest: 1` + `enableOfflineQueue: false`: commands fail fast
|
|
44
|
-
* rather than queueing during an outage (callers are fail-open/best-effort).
|
|
45
|
-
* - An `error` listener is attached (ioredis auto-reconnects) so a dropped
|
|
46
|
-
* connection can't surface as an unhandled `'error'` event that crashes Node.
|
|
47
|
-
*
|
|
48
|
-
* Optional auth: `REDIS_PASSWORD` (data-node AUTH) and `REDIS_SENTINEL_PASSWORD`
|
|
49
|
-
* (sentinel AUTH, sentinel mode only) are threaded through when set.
|
|
50
|
-
*
|
|
51
|
-
* @param label short human name for this client, used in the two warn logs.
|
|
59
|
+
* Resolve the Redis connection from `env`, or `null` when Redis isn't
|
|
60
|
+
* configured. Throws {@link RedisConfigError} for an unusable configuration.
|
|
52
61
|
*/
|
|
53
|
-
export function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
inst = new RedisCtor(sentinelOpts);
|
|
77
|
-
logger.info(`Redis ${label}: Sentinel mode`, { master: sentinelOpts.name, sentinels: sentinels.length });
|
|
62
|
+
export function resolveRedisConnection(env = process.env) {
|
|
63
|
+
if (env.REDIS_HOST) {
|
|
64
|
+
throw new RedisConfigError('REDIS_HOST is not supported. Set REDIS_URL=redis://<host>:<port> (or REDIS_SENTINELS for HA) and remove REDIS_HOST/REDIS_PORT.');
|
|
65
|
+
}
|
|
66
|
+
const sentinels = parseSentinels(env.REDIS_SENTINELS);
|
|
67
|
+
const url = env.REDIS_URL?.trim();
|
|
68
|
+
const password = env.REDIS_PASSWORD || undefined;
|
|
69
|
+
if (sentinels.length > 0 && url) {
|
|
70
|
+
throw new RedisConfigError('Set REDIS_URL or REDIS_SENTINELS, not both.');
|
|
71
|
+
}
|
|
72
|
+
if (sentinels.length > 0) {
|
|
73
|
+
return {
|
|
74
|
+
mode: 'sentinel',
|
|
75
|
+
sentinels,
|
|
76
|
+
masterName: env.REDIS_SENTINEL_MASTER || 'mymaster',
|
|
77
|
+
password,
|
|
78
|
+
sentinelPassword: env.REDIS_SENTINEL_PASSWORD || undefined,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (url) {
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = new URL(url);
|
|
78
85
|
}
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
catch {
|
|
87
|
+
throw new RedisConfigError('REDIS_URL is not a valid URL (expected redis://<host>:<port>).');
|
|
81
88
|
}
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
if (parsed.protocol !== 'redis:' && parsed.protocol !== 'rediss:') {
|
|
90
|
+
throw new RedisConfigError(`REDIS_URL must use redis:// or rediss://, got ${parsed.protocol}//`);
|
|
84
91
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
92
|
+
if (!parsed.hostname)
|
|
93
|
+
throw new RedisConfigError('REDIS_URL has no host.');
|
|
94
|
+
return { mode: 'url', url, password };
|
|
88
95
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** A log-safe description of a connection (never includes credentials). */
|
|
99
|
+
export function describeRedisConnection(conn) {
|
|
100
|
+
if (conn.mode === 'sentinel') {
|
|
101
|
+
return { mode: 'sentinel', master: conn.masterName, sentinels: conn.sentinels.length };
|
|
92
102
|
}
|
|
103
|
+
const u = new URL(conn.url);
|
|
104
|
+
return { mode: 'url', host: u.hostname, port: u.port || '6379', tls: u.protocol === 'rediss:' };
|
|
105
|
+
}
|
|
106
|
+
function loadIoredis() {
|
|
107
|
+
const req = createRequire(import.meta.url);
|
|
108
|
+
const mod = req('ioredis');
|
|
109
|
+
return (mod.Redis ?? mod.default ?? mod);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Construct an ioredis client for a resolved connection. `options` are merged
|
|
113
|
+
* into the client options (e.g. `{ db, maxRetriesPerRequest: null }` for BullMQ).
|
|
114
|
+
* An `error` listener is always attached so a dropped connection is logged
|
|
115
|
+
* rather than surfacing as an unhandled `'error'` event that crashes Node.
|
|
116
|
+
*/
|
|
117
|
+
export function createRedisClient(conn, label, options = {}) {
|
|
118
|
+
const Ctor = loadIoredis();
|
|
119
|
+
const auth = conn.password ? { password: conn.password } : {};
|
|
120
|
+
const inst = conn.mode === 'sentinel'
|
|
121
|
+
? new Ctor({
|
|
122
|
+
sentinels: conn.sentinels,
|
|
123
|
+
name: conn.masterName,
|
|
124
|
+
...(conn.sentinelPassword ? { sentinelPassword: conn.sentinelPassword } : {}),
|
|
125
|
+
...auth,
|
|
126
|
+
...options,
|
|
127
|
+
})
|
|
128
|
+
: new Ctor(conn.url, { ...auth, ...options });
|
|
129
|
+
inst
|
|
130
|
+
.on('error', (e) => logger.warn(`Redis ${label} client error`, { error: e instanceof Error ? e.message : String(e) }));
|
|
131
|
+
return inst;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The shared application Redis client: `null` when Redis isn't configured, so
|
|
135
|
+
* the caller degrades to its no-Redis behavior.
|
|
136
|
+
*
|
|
137
|
+
* Commands fail fast (`maxRetriesPerRequest: 1`, no offline queue) because the
|
|
138
|
+
* request-path callers are best-effort or fail closed on their own terms — a
|
|
139
|
+
* request must not hang behind a Redis outage. That includes the moments before
|
|
140
|
+
* the first connection completes: a background caller that runs at startup must
|
|
141
|
+
* either tolerate a rejected command or wait with {@link whenRedisReady}.
|
|
142
|
+
*
|
|
143
|
+
* Throws {@link RedisConfigError} for a bad configuration, and if Redis is
|
|
144
|
+
* configured but `ioredis` can't be loaded — both are deployment errors that
|
|
145
|
+
* must not quietly turn Redis-backed guarantees off.
|
|
146
|
+
*/
|
|
147
|
+
export function createEnvRedisClient(label) {
|
|
148
|
+
const conn = resolveRedisConnection();
|
|
149
|
+
if (!conn)
|
|
150
|
+
return null;
|
|
151
|
+
const inst = createRedisClient(conn, label, { maxRetriesPerRequest: 1, enableOfflineQueue: false });
|
|
152
|
+
logger.info(`Redis ${label} client created`, describeRedisConnection(conn));
|
|
153
|
+
return inst;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Resolve once `client` is connected and ready for commands, or reject after
|
|
157
|
+
* `timeoutMs`. For startup work (creating a consumer group, subscribing) that
|
|
158
|
+
* otherwise races the first connection. A client without ioredis' `status`
|
|
159
|
+
* (e.g. a test double) counts as ready.
|
|
160
|
+
*/
|
|
161
|
+
export function whenRedisReady(client, timeoutMs = 30_000) {
|
|
162
|
+
if (client.status === undefined || client.status === 'ready' || !client.once)
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
const onReady = () => { clearTimeout(timer); resolve(); };
|
|
166
|
+
const timer = setTimeout(() => {
|
|
167
|
+
client.off?.('ready', onReady);
|
|
168
|
+
reject(new Error(`Redis not ready after ${timeoutMs}ms`));
|
|
169
|
+
}, timeoutMs);
|
|
170
|
+
timer.unref?.();
|
|
171
|
+
client.once('ready', onReady);
|
|
172
|
+
});
|
|
93
173
|
}
|
|
94
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LXJlZGlzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZpY2VzL2Vudi1yZWRpcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFdkMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRWxELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztBQUV6Qzs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQUMsR0FBdUI7SUFDcEQsSUFBSSxDQUFDLEdBQUc7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNwQixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7UUFDdkUsTUFBTSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3RDLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFFBQVEsQ0FBQyxJQUFJLElBQUksT0FBTyxFQUFFLEVBQUUsQ0FBQyxJQUFJLEtBQUssRUFBRSxDQUFDO0lBQ2hFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUM3QixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0NHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFjLEtBQWE7SUFDN0QsSUFBSSxDQUFDO1FBQ0gsTUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDOUQsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDbEMsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUM7UUFDcEMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLElBQUk7WUFBRSxPQUFPLElBQUksQ0FBQztRQUN6RCxNQUFNLEdBQUcsR0FBRyxhQUFhLENBQUMsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDM0MsTUFBTSxHQUFHLEdBQUcsR0FBRyxDQUFDLFNBQVMsQ0FHeEIsQ0FBQztRQUNGLE1BQU0sU0FBUyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssSUFBSSxHQUFHLENBQUMsT0FBTyxJQUFJLEdBQUcsQ0FBa0MsQ0FBQztRQUNyRixNQUFNLFNBQVMsR0FBNEIsRUFBRSxvQkFBb0IsRUFBRSxDQUFDLEVBQUUsa0JBQWtCLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDbEcsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWM7WUFBRSxTQUFTLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDO1FBRWhGLElBQUksSUFBTyxDQUFDO1FBQ1osSUFBSSxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3pCLGdGQUFnRjtZQUNoRixNQUFNLFlBQVksR0FBNEI7Z0JBQzVDLFNBQVM7Z0JBQ1QsSUFBSSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQXFCLElBQUksVUFBVTtnQkFDckQsR0FBRyxTQUFTO2FBQ2IsQ0FBQztZQUNGLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyx1QkFBdUI7Z0JBQUUsWUFBWSxDQUFDLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsdUJBQXVCLENBQUM7WUFDN0csSUFBSSxHQUFHLElBQUksU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDO1lBQ25DLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxLQUFLLGlCQUFpQixFQUFFLEVBQUUsTUFBTSxFQUFFLFlBQVksQ0FBQyxJQUFJLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQzNHLENBQUM7YUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFDO1lBQ2YsSUFBSSxHQUFHLElBQUksU0FBUyxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsQ0FBQztRQUN2QyxDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksR0FBRyxJQUFJLFNBQVMsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxJQUFJLE1BQU0sRUFBRSxFQUFFLENBQUMsRUFBRSxHQUFHLFNBQVMsRUFBRSxDQUFDLENBQUM7UUFDckcsQ0FBQztRQUNBLElBQTJFO2FBQ3pFLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxLQUFLLGVBQWUsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDekgsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE1BQU0sQ0FBQyxJQUFJLENBQUMseUJBQXlCLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDM0csT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbW9kdWxlJztcblxuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdlbnYtcmVkaXMnKTtcblxuLyoqXG4gKiBQYXJzZSBgUkVESVNfU0VOVElORUxTYCAoY29tbWEtc2VwYXJhdGVkIGBob3N0OnBvcnRgIGxpc3QpIGludG8gdGhlIGlvcmVkaXNcbiAqIGB7IGhvc3QsIHBvcnQgfVtdYCBzZW50aW5lbCBhcnJheS4gQmxhbmsvZ2FyYmFnZSBlbnRyaWVzIGFyZSBza2lwcGVkOyBhXG4gKiBtaXNzaW5nIHBvcnQgZGVmYXVsdHMgdG8gdGhlIFNlbnRpbmVsIGRlZmF1bHQgMjYzNzkuIFJldHVybnMgYW4gZW1wdHkgYXJyYXlcbiAqIHdoZW4gdGhlIGVudiBpcyB1bnNldC9lbXB0eSAo4oaSIG5vbi1zZW50aW5lbCBtb2RlKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlU2VudGluZWxzKHJhdzogc3RyaW5nIHwgdW5kZWZpbmVkKTogQXJyYXk8eyBob3N0OiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9PiB7XG4gIGlmICghcmF3KSByZXR1cm4gW107XG4gIHJldHVybiByYXcuc3BsaXQoJywnKS5tYXAoKHMpID0+IHMudHJpbSgpKS5maWx0ZXIoQm9vbGVhbikubWFwKChlbnRyeSkgPT4ge1xuICAgIGNvbnN0IFtob3N0LCBwb3J0XSA9IGVudHJ5LnNwbGl0KCc6Jyk7XG4gICAgcmV0dXJuIHsgaG9zdCwgcG9ydDogcGFyc2VJbnQocG9ydCA/PyAnMjYzNzknLCAxMCkgfHwgMjYzNzkgfTtcbiAgfSkuZmlsdGVyKChzKSA9PiAhIXMuaG9zdCk7XG59XG5cbi8qKlxuICogQ29uc3RydWN0IGFuIGlvcmVkaXMgY2xpZW50IGZyb20gdGhlIHN0YW5kYXJkIGBSRURJU19VUkxgIG9yXG4gKiBgUkVESVNfSE9TVGAvYFJFRElTX1BPUlRgIGVudiDigJQgT1IsIGZvciBIQSwgZnJvbSBSZWRpcyBTZW50aW5lbCB2aWFcbiAqIGBSRURJU19TRU5USU5FTFNgICsgYFJFRElTX1NFTlRJTkVMX01BU1RFUmAuIFRoZSBzaGFyZWQgY29uc3RydWN0aW9uIHdhc1xuICogcHJldmlvdXNseSBkdXBsaWNhdGVkIGFjcm9zcyB0aGUgbGVhZGVyLWxvY2ssIGF1ZGl0LXNwb29sLCBhbmRcbiAqIHRva2VuLXJldm9jYXRpb24gZW52IGZhY3Rvcmllcy5cbiAqXG4gKiBDb25uZWN0aW9uIG1vZGVzIChmaXJzdCB0aGF0IG1hdGNoZXMgd2lucyk6XG4gKiAxLiBTRU5USU5FTCDigJQgYFJFRElTX1NFTlRJTkVMU2Agc2V0IChjb21tYS1zZXBhcmF0ZWQgYGhvc3Q6cG9ydGApOiBpb3JlZGlzXG4gKiAgICByZXNvbHZlcyB0aGUgY3VycmVudCBtYXN0ZXIgZnJvbSB0aGUgc2VudGluZWxzIChgbmFtZWAgPSBgUkVESVNfU0VOVElORUxfTUFTVEVSYCxcbiAqICAgIGRlZmF1bHQgYG15bWFzdGVyYCkgYW5kIHRyYW5zcGFyZW50bHkgcmVjb25uZWN0cyB0byB0aGUgbmV3IG1hc3RlciBhZnRlciBhXG4gKiAgICBmYWlsb3Zlci4gVGhpcyBpcyBob3cgdGhlIGFwcCBnZXRzIEhBIGFnYWluc3QgYSBzaW5nbGUtbm9kZSBSZWRpcyBsb3NzIOKAlFxuICogICAgYWxzbyB0aGUgc2hhcGUgYSBtYW5hZ2VkIEVsYXN0aUNhY2hlIChjbHVzdGVyLW1vZGUtZGlzYWJsZWQpIHNwZWFrcy5cbiAqIDIuIFVSTCDigJQgYFJFRElTX1VSTGAuXG4gKiAzLiBIT1NUL1BPUlQg4oCUIGBSRURJU19IT1NUYCgrYFJFRElTX1BPUlRgKS5cbiAqXG4gKiBTZW1hbnRpY3MgZXZlcnkgY2FsbGVyIHJlbGllcyBvbiAoa2VwdCBpZGVudGljYWwgYWNyb3NzIHRoZSBjb25zb2xpZGF0aW9uKTpcbiAqIC0gUmV0dXJucyBgbnVsbGAgd2hlbiBOT05FIG9mIHNlbnRpbmVscy91cmwvaG9zdCBpcyBzZXQsIHNvIGEgY2FsbGVyIGRlZ3JhZGVzXG4gKiAgIHRvIGl0cyBuby1SZWRpcyBmYWxsYmFjayAocnVuLW9uLWV2ZXJ5LXBvZCAvIGJlc3QtZWZmb3J0IC8gZmFpbC1vcGVuKS5cbiAqIC0gRkFJTC1TQUZFOiBhbnkgaW9yZWRpcyBsb2FkIG9yIGNvbnN0cnVjdGlvbiBlcnJvciBpcyBzd2FsbG93ZWQgYW5kIHJldHVybnNcbiAqICAgYG51bGxgIOKAlCBSZWRpcyBiZWluZyB1bmF2YWlsYWJsZSBtdXN0IG5ldmVyIGNyYXNoIHRoZSBpbXBvcnRpbmcgc2VydmljZS5cbiAqIC0gYGlvcmVkaXNgIGlzIGEgcnVudGltZS1vbmx5IG9wdGlvbmFsIGRlcCBsb2FkZWQgdmlhIGBjcmVhdGVSZXF1aXJlYCwgc28gdGhpc1xuICogICBtb2R1bGUgc3RheXMgaW1wb3J0YWJsZSBpbiBDSlMgYnVuZGxlcyBhbmQgd2hlcmUgUmVkaXMgaXNuJ3QgaW5zdGFsbGVkLlxuICogLSBgbWF4UmV0cmllc1BlclJlcXVlc3Q6IDFgICsgYGVuYWJsZU9mZmxpbmVRdWV1ZTogZmFsc2VgOiBjb21tYW5kcyBmYWlsIGZhc3RcbiAqICAgcmF0aGVyIHRoYW4gcXVldWVpbmcgZHVyaW5nIGFuIG91dGFnZSAoY2FsbGVycyBhcmUgZmFpbC1vcGVuL2Jlc3QtZWZmb3J0KS5cbiAqIC0gQW4gYGVycm9yYCBsaXN0ZW5lciBpcyBhdHRhY2hlZCAoaW9yZWRpcyBhdXRvLXJlY29ubmVjdHMpIHNvIGEgZHJvcHBlZFxuICogICBjb25uZWN0aW9uIGNhbid0IHN1cmZhY2UgYXMgYW4gdW5oYW5kbGVkIGAnZXJyb3InYCBldmVudCB0aGF0IGNyYXNoZXMgTm9kZS5cbiAqXG4gKiBPcHRpb25hbCBhdXRoOiBgUkVESVNfUEFTU1dPUkRgIChkYXRhLW5vZGUgQVVUSCkgYW5kIGBSRURJU19TRU5USU5FTF9QQVNTV09SRGBcbiAqIChzZW50aW5lbCBBVVRILCBzZW50aW5lbCBtb2RlIG9ubHkpIGFyZSB0aHJlYWRlZCB0aHJvdWdoIHdoZW4gc2V0LlxuICpcbiAqIEBwYXJhbSBsYWJlbCBzaG9ydCBodW1hbiBuYW1lIGZvciB0aGlzIGNsaWVudCwgdXNlZCBpbiB0aGUgdHdvIHdhcm4gbG9ncy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUVudlJlZGlzQ2xpZW50PFQgPSB1bmtub3duPihsYWJlbDogc3RyaW5nKTogVCB8IG51bGwge1xuICB0cnkge1xuICAgIGNvbnN0IHNlbnRpbmVscyA9IHBhcnNlU2VudGluZWxzKHByb2Nlc3MuZW52LlJFRElTX1NFTlRJTkVMUyk7XG4gICAgY29uc3QgdXJsID0gcHJvY2Vzcy5lbnYuUkVESVNfVVJMO1xuICAgIGNvbnN0IGhvc3QgPSBwcm9jZXNzLmVudi5SRURJU19IT1NUO1xuICAgIGlmIChzZW50aW5lbHMubGVuZ3RoID09PSAwICYmICF1cmwgJiYgIWhvc3QpIHJldHVybiBudWxsO1xuICAgIGNvbnN0IHJlcSA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKTtcbiAgICBjb25zdCBtb2QgPSByZXEoJ2lvcmVkaXMnKSBhcyB7XG4gICAgICBSZWRpcz86IG5ldyAoLi4uYXJnczogdW5rbm93bltdKSA9PiB1bmtub3duO1xuICAgICAgZGVmYXVsdD86IG5ldyAoLi4uYXJnczogdW5rbm93bltdKSA9PiB1bmtub3duO1xuICAgIH07XG4gICAgY29uc3QgUmVkaXNDdG9yID0gKG1vZC5SZWRpcyA/PyBtb2QuZGVmYXVsdCA/PyBtb2QpIGFzIG5ldyAoLi4uYXJnczogdW5rbm93bltdKSA9PiBUO1xuICAgIGNvbnN0IHJlZGlzT3B0czogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7IG1heFJldHJpZXNQZXJSZXF1ZXN0OiAxLCBlbmFibGVPZmZsaW5lUXVldWU6IGZhbHNlIH07XG4gICAgaWYgKHByb2Nlc3MuZW52LlJFRElTX1BBU1NXT1JEKSByZWRpc09wdHMucGFzc3dvcmQgPSBwcm9jZXNzLmVudi5SRURJU19QQVNTV09SRDtcblxuICAgIGxldCBpbnN0OiBUO1xuICAgIGlmIChzZW50aW5lbHMubGVuZ3RoID4gMCkge1xuICAgICAgLy8gU2VudGluZWwgbW9kZTogaW9yZWRpcyB0cmFja3MgdGhlIG1hc3RlciB2aWEgdGhlIHNlbnRpbmVscyArIGF1dG8tZmFpbHMtb3Zlci5cbiAgICAgIGNvbnN0IHNlbnRpbmVsT3B0czogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7XG4gICAgICAgIHNlbnRpbmVscyxcbiAgICAgICAgbmFtZTogcHJvY2Vzcy5lbnYuUkVESVNfU0VOVElORUxfTUFTVEVSIHx8ICdteW1hc3RlcicsXG4gICAgICAgIC4uLnJlZGlzT3B0cyxcbiAgICAgIH07XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuUkVESVNfU0VOVElORUxfUEFTU1dPUkQpIHNlbnRpbmVsT3B0cy5zZW50aW5lbFBhc3N3b3JkID0gcHJvY2Vzcy5lbnYuUkVESVNfU0VOVElORUxfUEFTU1dPUkQ7XG4gICAgICBpbnN0ID0gbmV3IFJlZGlzQ3RvcihzZW50aW5lbE9wdHMpO1xuICAgICAgbG9nZ2VyLmluZm8oYFJlZGlzICR7bGFiZWx9OiBTZW50aW5lbCBtb2RlYCwgeyBtYXN0ZXI6IHNlbnRpbmVsT3B0cy5uYW1lLCBzZW50aW5lbHM6IHNlbnRpbmVscy5sZW5ndGggfSk7XG4gICAgfSBlbHNlIGlmICh1cmwpIHtcbiAgICAgIGluc3QgPSBuZXcgUmVkaXNDdG9yKHVybCwgcmVkaXNPcHRzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaW5zdCA9IG5ldyBSZWRpc0N0b3IoeyBob3N0LCBwb3J0OiBwYXJzZUludChwcm9jZXNzLmVudi5SRURJU19QT1JUID8/ICc2Mzc5JywgMTApLCAuLi5yZWRpc09wdHMgfSk7XG4gICAgfVxuICAgIChpbnN0IGFzIHVua25vd24gYXMgeyBvbjogKGV2dDogc3RyaW5nLCBjYjogKGU6IHVua25vd24pID0+IHZvaWQpID0+IHZvaWQgfSlcbiAgICAgIC5vbignZXJyb3InLCAoZSkgPT4gbG9nZ2VyLndhcm4oYFJlZGlzICR7bGFiZWx9IGNsaWVudCBlcnJvcmAsIHsgZXJyb3I6IGUgaW5zdGFuY2VvZiBFcnJvciA/IGUubWVzc2FnZSA6IFN0cmluZyhlKSB9KSk7XG4gICAgcmV0dXJuIGluc3Q7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGxvZ2dlci53YXJuKGBSZWRpcyB1bmF2YWlsYWJsZSBmb3IgJHtsYWJlbH1gLCB7IGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfSk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cbiJdfQ==
|
|
174
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LXJlZGlzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlcnZpY2VzL2Vudi1yZWRpcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFdkMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRWxELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztBQUV6Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUVILG9FQUFvRTtBQUNwRSxNQUFNLE9BQU8sZ0JBQWlCLFNBQVEsS0FBSztJQUN6QyxZQUFZLE9BQWU7UUFDekIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxrQkFBa0IsQ0FBQztJQUNqQyxDQUFDO0NBQ0Y7QUFpQkQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQUMsR0FBdUI7SUFDcEQsSUFBSSxDQUFDLEdBQUc7UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUNwQixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7UUFDdkUsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQyxNQUFNLElBQUksR0FBRyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDdEQsTUFBTSxRQUFR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QUFDLE1BQU0sQ0FBQyxJQUFJO1FBQUUsT0FBTyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDdkcsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsRUFBRTtRQUNyQyxNQUFNLE9BQU8sR0FBRyxHQUFHLEVBQUUsR0FBRyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMxRCxNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO1lBQzVCLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDL0IsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLHlCQUF5QixTQUFTLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDNUQsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ2QsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDaEIsTUFBTSxDQUFDLElBQUssQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDakMsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgfSBmcm9tICdtb2R1bGUnO1xuXG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ2Vudi1yZWRpcycpO1xuXG4vKipcbiAqIFRoZSBPTkUgcGxhY2UgUmVkaXMgY29ubmVjdGlvbiBzZXR0aW5ncyBhcmUgcmVhZCBmcm9tIHRoZSBlbnZpcm9ubWVudC5cbiAqXG4gKiBUd28gc2hhcGVzLCBleGFjdGx5IG9uZSBvZiB3aGljaCBtYXkgYmUgc2V0OlxuICpcbiAqIC0gKipTdGFuZGFsb25lKiog4oCUIGBSRURJU19VUkxgIChgcmVkaXM6Ly9ob3N0OjYzNzlbL2RiXWAsIG9yIGByZWRpc3M6Ly9gIGZvclxuICogICBUTFMpLiBDcmVkZW50aWFscyBtYXkgc2l0IGluIHRoZSBVUkwgb3IgaW4gYFJFRElTX1BBU1NXT1JEYC5cbiAqIC0gKipIQSAoU2VudGluZWwpKiog4oCUIGBSRURJU19TRU5USU5FTFNgIChjb21tYS1zZXBhcmF0ZWQgYGhvc3Q6cG9ydGApIHBsdXNcbiAqICAgYFJFRElTX1NFTlRJTkVMX01BU1RFUmAgKGRlZmF1bHQgYG15bWFzdGVyYCkgYW5kIG9wdGlvbmFsXG4gKiAgIGBSRURJU19TRU5USU5FTF9QQVNTV09SRGAuIGBSRURJU19QQVNTV09SRGAgYXV0aGVudGljYXRlcyB0byB0aGUgZGF0YSBub2Rlcy5cbiAqICAgaW9yZWRpcyBmb2xsb3dzIHRoZSBtYXN0ZXIgYWNyb3NzIGEgZmFpbG92ZXIuXG4gKlxuICogTmVpdGhlciBzZXQg4oaSIFJlZGlzIGlzIG9mZiwgYW5kIGVhY2ggY2FsbGVyIGRlZ3JhZGVzIHRvIGl0cyBkb2N1bWVudGVkXG4gKiBuby1SZWRpcyBiZWhhdmlvci5cbiAqXG4gKiBgUkVESVNfSE9TVGAgLyBgUkVESVNfUE9SVGAgYXJlIG5vdCByZWFkLiBgUkVESVNfSE9TVGAgaXMgcmVmdXNlZCBvdXRyaWdodCBzbyBhXG4gKiBzdGFsZSBjb25maWcgZmFpbHMgYXQgc3RhcnR1cCBpbnN0ZWFkIG9mIHNpbGVudGx5IHJ1bm5pbmcgd2l0aG91dCBSZWRpcy5cbiAqIGBSRURJU19QT1JUYCBjYW4ndCBiZSByZWZ1c2VkOiBLdWJlcm5ldGVzIGluamVjdHMgYFJFRElTX1BPUlQ9dGNwOi8vPGlwPjo2Mzc5YFxuICogaW50byBldmVyeSBwb2QgaW4gYSBuYW1lc3BhY2Ugd2l0aCBhIFNlcnZpY2UgbmFtZWQgYHJlZGlzYCwgd2hpY2ggaXMgZXhhY3RseVxuICogd2h5IGEgaG9zdC9wb3J0IHBhaXIgd2FzIHVuc2FmZSB0byBwYXJzZS5cbiAqXG4gKiBNaXNjb25maWd1cmF0aW9uIHRocm93cyB7QGxpbmsgUmVkaXNDb25maWdFcnJvcn0uIFdyaXRlcnMgYW5kIHJlYWRlcnMgb2YgdGhlXG4gKiBzYW1lIGtleXMgKHJldm9jYXRpb24sIHN0ZXAtdXAsIGxlYWRlciBsb2NrcykgbXVzdCByZXNvbHZlIFJlZGlzIGlkZW50aWNhbGx5O1xuICogYSBjb25maWcgdGhhdCBxdWlldGx5IHJlc29sdmVkIHRvIFwibm8gUmVkaXNcIiBvbiBvbmUgc2lkZSBpcyBob3cgcmV2b2NhdGlvblxuICogb25jZSBkaWQgbm90aGluZyBhdCBhbGwuXG4gKi9cblxuLyoqIFRocm93biBmb3IgYSBSZWRpcyBlbnZpcm9ubWVudCB0aGF0IGNhbid0IGJlIHVzZWQgYXMgd3JpdHRlbi4gKi9cbmV4cG9ydCBjbGFzcyBSZWRpc0NvbmZpZ0Vycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlOiBzdHJpbmcpIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnUmVkaXNDb25maWdFcnJvcic7XG4gIH1cbn1cblxuLyoqIFJlc29sdmVkIGNvbm5lY3Rpb24sIGluZGVwZW5kZW50IG9mIGFueSBjbGllbnQgbGlicmFyeS4gKi9cbmV4cG9ydCB0eXBlIFJlZGlzQ29ubmVjdGlvbiA9XG4gIHwge1xuICAgIG1vZGU6ICdzZW50aW5lbCc7XG4gICAgc2VudGluZWxzOiBBcnJheTx7IGhvc3Q6IHN0cmluZzsgcG9ydDogbnVtYmVyIH0+O1xuICAgIG1hc3Rlck5hbWU6IHN0cmluZztcbiAgICBwYXNzd29yZD86IHN0cmluZztcbiAgICBzZW50aW5lbFBhc3N3b3JkPzogc3RyaW5nO1xuICB9XG4gIHwge1xuICAgIG1vZGU6ICd1cmwnO1xuICAgIHVybDogc3RyaW5nO1xuICAgIHBhc3N3b3JkPzogc3RyaW5nO1xuICB9O1xuXG4vKipcbiAqIFBhcnNlIGBSRURJU19TRU5USU5FTFNgIGludG8gYHsgaG9zdCwgcG9ydCB9W11gLiBCbGFuayBlbnRyaWVzIGFyZSBza2lwcGVkIGFuZFxuICogYSBtaXNzaW5nIHBvcnQgbWVhbnMgdGhlIFNlbnRpbmVsIGRlZmF1bHQgMjYzNzk7IGEgcG9ydCB0aGF0IGlzbid0IGEgdmFsaWRcbiAqIFRDUCBwb3J0IGlzIGEgY29uZmlndXJhdGlvbiBlcnJvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlU2VudGluZWxzKHJhdzogc3RyaW5nIHwgdW5kZWZpbmVkKTogQXJyYXk8eyBob3N0OiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9PiB7XG4gIGlmICghcmF3KSByZXR1cm4gW107XG4gIHJldHVybiByYXcuc3BsaXQoJywnKS5tYXAoKHMpID0+IHMudHJpbSgpKS5maWx0ZXIoQm9vbGVhbikubWFwKChlbnRyeSkgPT4ge1xuICAgIGNvbnN0IGlkeCA9IGVudHJ5Lmxhc3RJbmRleE9mKCc6Jyk7XG4gICAgY29uc3QgaG9zdCA9IGlkeCA9PT0gLTEgPyBlbnRyeSA6IGVudHJ5LnNsaWNlKDAsIGlkeCk7XG4gICAgY29uc3QgcG9ydFRleHQgPSBpZHggPT09IC0xID8gJycgOiBlbnRyeS5zbGljZShpZHggKyAxKTtcbiAgICBjb25zdCBwb3J0ID0gcG9ydFRleHQgPT09ICcnID8gMjYzNzkgOiBOdW1iZXIocG9ydFRleHQpO1xuICAgIGlmICghaG9zdCB8fCAhTnVtYmVyLmlzSW50ZWdlcihwb3J0KSB8fCBwb3J0IDwgMSB8fCBwb3J0ID4gNjU1MzUpIHtcbiAgICAgIHRocm93IG5ldyBSZWRpc0NvbmZpZ0Vycm9yKGBSRURJU19TRU5USU5FTFMgZW50cnkgXCIke2VudHJ5fVwiIGlzIG5vdCBob3N0OnBvcnRgKTtcbiAgICB9XG4gICAgcmV0dXJuIHsgaG9zdCwgcG9ydCB9O1xuICB9KTtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIHRoZSBSZWRpcyBjb25uZWN0aW9uIGZyb20gYGVudmAsIG9yIGBudWxsYCB3aGVuIFJlZGlzIGlzbid0XG4gKiBjb25maWd1cmVkLiBUaHJvd3Mge0BsaW5rIFJlZGlzQ29uZmlnRXJyb3J9IGZvciBhbiB1bnVzYWJsZSBjb25maWd1cmF0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVJlZGlzQ29ubmVjdGlvbihlbnY6IE5vZGVKUy5Qcm9jZXNzRW52ID0gcHJvY2Vzcy5lbnYpOiBSZWRpc0Nvbm5lY3Rpb24gfCBudWxsIHtcbiAgaWYgKGVudi5SRURJU19IT1NUKSB7XG4gICAgdGhyb3cgbmV3IFJlZGlzQ29uZmlnRXJyb3IoXG4gICAgICAnUkVESVNfSE9TVCBpcyBub3Qgc3VwcG9ydGVkLiBTZXQgUkVESVNfVVJMPXJlZGlzOi8vPGhvc3Q+Ojxwb3J0PiAob3IgUkVESVNfU0VOVElORUxTIGZvciBIQSkgYW5kIHJlbW92ZSBSRURJU19IT1NUL1JFRElTX1BPUlQuJyxcbiAgICApO1xuICB9XG5cbiAgY29uc3Qgc2VudGluZWxzID0gcGFyc2VTZW50aW5lbHMoZW52LlJFRElTX1NFTlRJTkVMUyk7XG4gIGNvbnN0IHVybCA9IGVudi5SRURJU19VUkw/LnRyaW0oKTtcbiAgY29uc3QgcGFzc3dvcmQgPSBlbnYuUkVESVNfUEFTU1dPUkQgfHwgdW5kZWZpbmVkO1xuXG4gIGlmIChzZW50aW5lbHMubGVuZ3RoID4gMCAmJiB1cmwpIHtcbiAgICB0aHJvdyBuZXcgUmVkaXNDb25maWdFcnJvcignU2V0IFJFRElTX1VSTCBvciBSRURJU19TRU5USU5FTFMsIG5vdCBib3RoLicpO1xuICB9XG5cbiAgaWYgKHNlbnRpbmVscy5sZW5ndGggPiAwKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG1vZGU6ICdzZW50aW5lbCcsXG4gICAgICBzZW50aW5lbHMsXG4gICAgICBtYXN0ZXJOYW1lOiBlbnYuUkVESVNfU0VOVElORUxfTUFTVEVSIHx8ICdteW1hc3RlcicsXG4gICAgICBwYXNzd29yZCxcbiAgICAgIHNlbnRpbmVsUGFzc3dvcmQ6IGVudi5SRURJU19TRU5USU5FTF9QQVNTV09SRCB8fCB1bmRlZmluZWQsXG4gICAgfTtcbiAgfVxuXG4gIGlmICh1cmwpIHtcbiAgICBsZXQgcGFyc2VkOiBVUkw7XG4gICAgdHJ5IHtcbiAgICAgIHBhcnNlZCA9IG5ldyBVUkwodXJsKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIHRocm93IG5ldyBSZWRpc0NvbmZpZ0Vycm9yKCdSRURJU19VUkwgaXMgbm90IGEgdmFsaWQgVVJMIChleHBlY3RlZCByZWRpczovLzxob3N0Pjo8cG9ydD4pLicpO1xuICAgIH1cbiAgICBpZiAocGFyc2VkLnByb3RvY29sICE9PSAncmVkaXM6JyAmJiBwYXJzZWQucHJvdG9jb2wgIT09ICdyZWRpc3M6Jykge1xuICAgICAgdGhyb3cgbmV3IFJlZGlzQ29uZmlnRXJyb3IoYFJFRElTX1VSTCBtdXN0IHVzZSByZWRpczovLyBvciByZWRpc3M6Ly8sIGdvdCAke3BhcnNlZC5wcm90b2NvbH0vL2ApO1xuICAgIH1cbiAgICBpZiAoIXBhcnNlZC5ob3N0bmFtZSkgdGhyb3cgbmV3IFJlZGlzQ29uZmlnRXJyb3IoJ1JFRElTX1VSTCBoYXMgbm8gaG9zdC4nKTtcbiAgICByZXR1cm4geyBtb2RlOiAndXJsJywgdXJsLCBwYXNzd29yZCB9O1xuICB9XG5cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKiBBIGxvZy1zYWZlIGRlc2NyaXB0aW9uIG9mIGEgY29ubmVjdGlvbiAobmV2ZXIgaW5jbHVkZXMgY3JlZGVudGlhbHMpLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlc2NyaWJlUmVkaXNDb25uZWN0aW9uKGNvbm46IFJlZGlzQ29ubmVjdGlvbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgaWYgKGNvbm4ubW9kZSA9PT0gJ3NlbnRpbmVsJykge1xuICAgIHJldHVybiB7IG1vZGU6ICdzZW50aW5lbCcsIG1hc3RlcjogY29ubi5tYXN0ZXJOYW1lLCBzZW50aW5lbHM6IGNvbm4uc2VudGluZWxzLmxlbmd0aCB9O1xuICB9XG4gIGNvbnN0IHUgPSBuZXcgVVJMKGNvbm4udXJsKTtcbiAgcmV0dXJuIHsgbW9kZTogJ3VybCcsIGhvc3Q6IHUuaG9zdG5hbWUsIHBvcnQ6IHUucG9ydCB8fCAnNjM3OScsIHRsczogdS5wcm90b2NvbCA9PT0gJ3JlZGlzczonIH07XG59XG5cbnR5cGUgUmVkaXNDdG9yPFQ+ID0gbmV3ICguLi5hcmdzOiB1bmtub3duW10pID0+IFQ7XG5cbmZ1bmN0aW9uIGxvYWRJb3JlZGlzPFQ+KCk6IFJlZGlzQ3RvcjxUPiB7XG4gIGNvbnN0IHJlcSA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKTtcbiAgY29uc3QgbW9kID0gcmVxKCdpb3JlZGlzJykgYXMgeyBSZWRpcz86IFJlZGlzQ3RvcjxUPjsgZGVmYXVsdD86IFJlZGlzQ3RvcjxUPiB9O1xuICByZXR1cm4gKG1vZC5SZWRpcyA/PyBtb2QuZGVmYXVsdCA/PyBtb2QpIGFzIFJlZGlzQ3RvcjxUPjtcbn1cblxuLyoqXG4gKiBDb25zdHJ1Y3QgYW4gaW9yZWRpcyBjbGllbnQgZm9yIGEgcmVzb2x2ZWQgY29ubmVjdGlvbi4gYG9wdGlvbnNgIGFyZSBtZXJnZWRcbiAqIGludG8gdGhlIGNsaWVudCBvcHRpb25zIChlLmcuIGB7IGRiLCBtYXhSZXRyaWVzUGVyUmVxdWVzdDogbnVsbCB9YCBmb3IgQnVsbE1RKS5cbiAqIEFuIGBlcnJvcmAgbGlzdGVuZXIgaXMgYWx3YXlzIGF0dGFjaGVkIHNvIGEgZHJvcHBlZCBjb25uZWN0aW9uIGlzIGxvZ2dlZFxuICogcmF0aGVyIHRoYW4gc3VyZmFjaW5nIGFzIGFuIHVuaGFuZGxlZCBgJ2Vycm9yJ2AgZXZlbnQgdGhhdCBjcmFzaGVzIE5vZGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWRpc0NsaWVudDxUID0gdW5rbm93bj4oXG4gIGNvbm46IFJlZGlzQ29ubmVjdGlvbixcbiAgbGFiZWw6IHN0cmluZyxcbiAgb3B0aW9uczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fSxcbik6IFQge1xuICBjb25zdCBDdG9yID0gbG9hZElvcmVkaXM8VD4oKTtcbiAgY29uc3QgYXV0aCA9IGNvbm4ucGFzc3dvcmQgPyB7IHBhc3N3b3JkOiBjb25uLnBhc3N3b3JkIH0gOiB7fTtcbiAgY29uc3QgaW5zdCA9IGNvbm4ubW9kZSA9PT0gJ3NlbnRpbmVsJ1xuICAgID8gbmV3IEN0b3Ioe1xuICAgICAgc2VudGluZWxzOiBjb25uLnNlbnRpbmVscyxcbiAgICAgIG5hbWU6IGNvbm4ubWFzdGVyTmFtZSxcbiAgICAgIC4uLihjb25uLnNlbnRpbmVsUGFzc3dvcmQgPyB7IHNlbnRpbmVsUGFzc3dvcmQ6IGNvbm4uc2VudGluZWxQYXNzd29yZCB9IDoge30pLFxuICAgICAgLi4uYXV0aCxcbiAgICAgIC4uLm9wdGlvbnMsXG4gICAgfSlcbiAgICA6IG5ldyBDdG9yKGNvbm4udXJsLCB7IC4uLmF1dGgsIC4uLm9wdGlvbnMgfSk7XG4gIChpbnN0IGFzIHVua25vd24gYXMgeyBvbjogKGV2dDogc3RyaW5nLCBjYjogKGU6IHVua25vd24pID0+IHZvaWQpID0+IHZvaWQgfSlcbiAgICAub24oJ2Vycm9yJywgKGUpID0+IGxvZ2dlci53YXJuKGBSZWRpcyAke2xhYmVsfSBjbGllbnQgZXJyb3JgLCB7IGVycm9yOiBlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiBTdHJpbmcoZSkgfSkpO1xuICByZXR1cm4gaW5zdDtcbn1cblxuLyoqXG4gKiBUaGUgc2hhcmVkIGFwcGxpY2F0aW9uIFJlZGlzIGNsaWVudDogYG51bGxgIHdoZW4gUmVkaXMgaXNuJ3QgY29uZmlndXJlZCwgc29cbiAqIHRoZSBjYWxsZXIgZGVncmFkZXMgdG8gaXRzIG5vLVJlZGlzIGJlaGF2aW9yLlxuICpcbiAqIENvbW1hbmRzIGZhaWwgZmFzdCAoYG1heFJldHJpZXNQZXJSZXF1ZXN0OiAxYCwgbm8gb2ZmbGluZSBxdWV1ZSkgYmVjYXVzZSB0aGVcbiAqIHJlcXVlc3QtcGF0aCBjYWxsZXJzIGFyZSBiZXN0LWVmZm9ydCBvciBmYWlsIGNsb3NlZCBvbiB0aGVpciBvd24gdGVybXMg4oCUIGFcbiAqIHJlcXVlc3QgbXVzdCBub3QgaGFuZyBiZWhpbmQgYSBSZWRpcyBvdXRhZ2UuIFRoYXQgaW5jbHVkZXMgdGhlIG1vbWVudHMgYmVmb3JlXG4gKiB0aGUgZmlyc3QgY29ubmVjdGlvbiBjb21wbGV0ZXM6IGEgYmFja2dyb3VuZCBjYWxsZXIgdGhhdCBydW5zIGF0IHN0YXJ0dXAgbXVzdFxuICogZWl0aGVyIHRvbGVyYXRlIGEgcmVqZWN0ZWQgY29tbWFuZCBvciB3YWl0IHdpdGgge0BsaW5rIHdoZW5SZWRpc1JlYWR5fS5cbiAqXG4gKiBUaHJvd3Mge0BsaW5rIFJlZGlzQ29uZmlnRXJyb3J9IGZvciBhIGJhZCBjb25maWd1cmF0aW9uLCBhbmQgaWYgUmVkaXMgaXNcbiAqIGNvbmZpZ3VyZWQgYnV0IGBpb3JlZGlzYCBjYW4ndCBiZSBsb2FkZWQg4oCUIGJvdGggYXJlIGRlcGxveW1lbnQgZXJyb3JzIHRoYXRcbiAqIG11c3Qgbm90IHF1aWV0bHkgdHVybiBSZWRpcy1iYWNrZWQgZ3VhcmFudGVlcyBvZmYuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFbnZSZWRpc0NsaWVudDxUID0gdW5rbm93bj4obGFiZWw6IHN0cmluZyk6IFQgfCBudWxsIHtcbiAgY29uc3QgY29ubiA9IHJlc29sdmVSZWRpc0Nvbm5lY3Rpb24oKTtcbiAgaWYgKCFjb25uKSByZXR1cm4gbnVsbDtcbiAgY29uc3QgaW5zdCA9IGNyZWF0ZVJlZGlzQ2xpZW50PFQ+KGNvbm4sIGxhYmVsLCB7IG1heFJldHJpZXNQZXJSZXF1ZXN0OiAxLCBlbmFibGVPZmZsaW5lUXVldWU6IGZhbHNlIH0pO1xuICBsb2dnZXIuaW5mbyhgUmVkaXMgJHtsYWJlbH0gY2xpZW50IGNyZWF0ZWRgLCBkZXNjcmliZVJlZGlzQ29ubmVjdGlvbihjb25uKSk7XG4gIHJldHVybiBpbnN0O1xufVxuXG4vKiogVGhlIHN1YnNldCBvZiBpb3JlZGlzIG5lZWRlZCB0byB3YWl0IGZvciBhIGNvbm5lY3Rpb24uICovXG5leHBvcnQgaW50ZXJmYWNlIFJlYWR5QXdhcmVSZWRpcyB7XG4gIHN0YXR1cz86IHN0cmluZztcbiAgb25jZT8oZXZlbnQ6IHN0cmluZywgY2I6ICguLi5hcmdzOiB1bmtub3duW10pID0+IHZvaWQpOiB1bmtub3duO1xuICBvZmY/KGV2ZW50OiBzdHJpbmcsIGNiOiAoLi4uYXJnczogdW5rbm93bltdKSA9PiB2b2lkKTogdW5rbm93bjtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIG9uY2UgYGNsaWVudGAgaXMgY29ubmVjdGVkIGFuZCByZWFkeSBmb3IgY29tbWFuZHMsIG9yIHJlamVjdCBhZnRlclxuICogYHRpbWVvdXRNc2AuIEZvciBzdGFydHVwIHdvcmsgKGNyZWF0aW5nIGEgY29uc3VtZXIgZ3JvdXAsIHN1YnNjcmliaW5nKSB0aGF0XG4gKiBvdGhlcndpc2UgcmFjZXMgdGhlIGZpcnN0IGNvbm5lY3Rpb24uIEEgY2xpZW50IHdpdGhvdXQgaW9yZWRpcycgYHN0YXR1c2BcbiAqIChlLmcuIGEgdGVzdCBkb3VibGUpIGNvdW50cyBhcyByZWFkeS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdoZW5SZWRpc1JlYWR5KGNsaWVudDogUmVhZHlBd2FyZVJlZGlzLCB0aW1lb3V0TXMgPSAzMF8wMDApOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKGNsaWVudC5zdGF0dXMgPT09IHVuZGVmaW5lZCB8fCBjbGllbnQuc3RhdHVzID09PSAncmVhZHknIHx8ICFjbGllbnQub25jZSkgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGNvbnN0IG9uUmVhZHkgPSAoKSA9PiB7IGNsZWFyVGltZW91dCh0aW1lcik7IHJlc29sdmUoKTsgfTtcbiAgICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgY2xpZW50Lm9mZj8uKCdyZWFkeScsIG9uUmVhZHkpO1xuICAgICAgcmVqZWN0KG5ldyBFcnJvcihgUmVkaXMgbm90IHJlYWR5IGFmdGVyICR7dGltZW91dE1zfW1zYCkpO1xuICAgIH0sIHRpbWVvdXRNcyk7XG4gICAgdGltZXIudW5yZWY/LigpO1xuICAgIGNsaWVudC5vbmNlISgncmVhZHknLCBvblJlYWR5KTtcbiAgfSk7XG59XG4iXX0=
|
|
@@ -30,12 +30,15 @@ export declare function closeLeaderLock(redis: LockRedis | null | undefined): Pr
|
|
|
30
30
|
* released on completion, but only if we still own it — a slower predecessor
|
|
31
31
|
* never deletes a successor's lock. `ttlMs` should comfortably exceed one run.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
33
|
+
* If Redis can't be reached the run is skipped (returns false) rather than
|
|
34
|
+
* rejecting — callers are timers, and a Redis blip must not crash the process.
|
|
35
|
+
*
|
|
36
|
+
* @returns true if we held the lock and ran `fn`; false if another holder did or
|
|
37
|
+
* the lock couldn't be taken.
|
|
34
38
|
*/
|
|
35
39
|
export declare function withLeaderLock(redis: LockRedis, key: string, ttlMs: number, fn: () => Promise<void>): Promise<boolean>;
|
|
36
40
|
/**
|
|
37
|
-
* A `LockRedis` backed by
|
|
38
|
-
* `REDIS_HOST`) — the leader-lock counterpart to `createEnvRedisAuditSpool` /
|
|
41
|
+
* A `LockRedis` backed by the shared env Redis client (see env-redis.ts) — the leader-lock counterpart to `createEnvRedisAuditSpool` /
|
|
39
42
|
* `createEnvRedisTokenRevocationStore`, for a service that has no BullMQ client to
|
|
40
43
|
* borrow. Returns `null` when Redis isn't configured, so a caller can degrade to
|
|
41
44
|
* running on every pod (the atomic guards it wraps must still make that safe).
|
|
@@ -38,11 +38,27 @@ const RELEASE_IF_OWNER = 'if redis.call("get", KEYS[1]) == ARGV[1] then return r
|
|
|
38
38
|
* released on completion, but only if we still own it — a slower predecessor
|
|
39
39
|
* never deletes a successor's lock. `ttlMs` should comfortably exceed one run.
|
|
40
40
|
*
|
|
41
|
-
*
|
|
41
|
+
* If Redis can't be reached the run is skipped (returns false) rather than
|
|
42
|
+
* rejecting — callers are timers, and a Redis blip must not crash the process.
|
|
43
|
+
*
|
|
44
|
+
* @returns true if we held the lock and ran `fn`; false if another holder did or
|
|
45
|
+
* the lock couldn't be taken.
|
|
42
46
|
*/
|
|
43
47
|
export async function withLeaderLock(redis, key, ttlMs, fn) {
|
|
44
48
|
const token = randomUUID();
|
|
45
|
-
|
|
49
|
+
let acquired;
|
|
50
|
+
try {
|
|
51
|
+
acquired = await redis.set(key, token, 'PX', ttlMs, 'NX');
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
// Redis unreachable (including before the first connection completes): no
|
|
55
|
+
// pod can prove it's the leader, so nobody runs this window. Skipping is
|
|
56
|
+
// the safe side — running here could duplicate destructive work across pods.
|
|
57
|
+
lockLogger.warn('Leader lock unavailable; skipping this run', {
|
|
58
|
+
key, error: err instanceof Error ? err.message : String(err),
|
|
59
|
+
});
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
46
62
|
if (acquired !== 'OK')
|
|
47
63
|
return false;
|
|
48
64
|
try {
|
|
@@ -68,8 +84,7 @@ export async function withLeaderLock(redis, key, ttlMs, fn) {
|
|
|
68
84
|
return true;
|
|
69
85
|
}
|
|
70
86
|
/**
|
|
71
|
-
* A `LockRedis` backed by
|
|
72
|
-
* `REDIS_HOST`) — the leader-lock counterpart to `createEnvRedisAuditSpool` /
|
|
87
|
+
* A `LockRedis` backed by the shared env Redis client (see env-redis.ts) — the leader-lock counterpart to `createEnvRedisAuditSpool` /
|
|
73
88
|
* `createEnvRedisTokenRevocationStore`, for a service that has no BullMQ client to
|
|
74
89
|
* borrow. Returns `null` when Redis isn't configured, so a caller can degrade to
|
|
75
90
|
* running on every pod (the atomic guards it wraps must still make that safe).
|
|
@@ -82,4 +97,4 @@ export function createEnvRedisLock() {
|
|
|
82
97
|
lockLogger.info('Redis leader-lock client initialized');
|
|
83
98
|
return inst;
|
|
84
99
|
}
|
|
85
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
100
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGVhZGVyLWxvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvbGVhZGVyLWxvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUVsRCxNQUFNLFVBQVUsR0FBRyxZQUFZLENBQUMsYUFBYSxDQUFDLENBQUM7QUFvQi9DOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGVBQWUsQ0FBQyxLQUFtQztJQUN2RSxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU87SUFDbkIsSUFBSSxDQUFDO1FBQ0gsSUFBSSxPQUFPLEtBQUssQ0FBQyxJQUFJLEtBQUssVUFBVSxFQUFFLENBQUM7WUFBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUFDLE9BQU87UUFBQyxDQUFDO0lBQ3ZFLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCw2QkFBNkI7SUFDL0IsQ0FBQztJQUNELElBQUksQ0FBQztRQUFDLEtBQUssQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO0lBQUMsQ0FBQztJQUFDLE1BQU0sQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUM7QUFDM0QsQ0FBQztBQUVEO2lFQUNpRTtBQUNqRSxNQUFNLGdCQUFnQixHQUNwQixtR0FBbUcsQ0FBQztBQUV0Rzs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsY0FBYyxDQUNsQyxLQUFnQixFQUNoQixHQUFXLEVBQ1gsS0FBYSxFQUNiLEVBQXVCO0lBRXZCLE1BQU0sS0FBSyxHQUFHLFVBQVUsRUFBRSxDQUFDO0lBQzNCLElBQUksUUFBaUIsQ0FBQztJQUN0QixJQUFJLENBQUM7UUFDSCxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLDBFQUEwRTtRQUMxRSx5RUFBeUU7UUFDekUsNkVBQTZFO1FBQzdFLFVBQVUsQ0FBQyxJQUFJLENBQUMsNENBQTRDLEVBQUU7WUFDNUQsR0FBRyxFQUFFLEtBQUssRUFBRSxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDO1NBQzdELENBQUMsQ0FBQztRQUNILE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELElBQUksUUFBUSxLQUFLLElBQUk7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUVwQyxJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsRUFBRSxDQUFDO0lBQ2IsQ0FBQztZQUFTLENBQUM7UUFDVCwwRUFBMEU7UUFDMUUsd0VBQXdFO1FBQ3hFLDRFQUE0RTtRQUM1RSxxREFBcUQ7UUFDckQsSUFBSSxDQUFDO1lBQ0gsSUFBSSxPQUFPLEtBQUssQ0FBQyxJQUFJLEtBQUssVUFBVSxFQUFFLENBQUM7Z0JBQ3JDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3BELENBQUM7aUJBQU0sSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFLLEtBQUssRUFBRSxDQUFDO2dCQUM1QyxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDdkIsQ0FBQztRQUNILENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxvREFBb0Q7UUFDdEQsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQjtJQUNoQyxNQUFNLElBQUksR0FBRyxvQkFBb0IsQ0FBWSxhQUFhLENBQUMsQ0FBQztJQUM1RCxJQUFJLElBQUk7UUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLHNDQUFzQyxDQUFDLENBQUM7SUFDbEUsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IHJhbmRvbVVVSUQgfSBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IHsgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICcuL2Vudi1yZWRpcy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5jb25zdCBsb2NrTG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdsZWFkZXItbG9jaycpO1xuXG4vKipcbiAqIE1pbmltYWwgUmVkaXMgY2xpZW50IHN1cmZhY2UgbmVlZGVkIGZvciBhIGxlYWRlciBsb2NrIChhIHN1YnNldCBvZiBpb3JlZGlzKS5cbiAqIGBzZXRgIGlzIHZhcmlhZGljIHNvIGNhbGxlcnMgY2FuIHBhc3MgYCdQWCcsIHR0bCwgJ05YJ2AuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTG9ja1JlZGlzIHtcbiAgc2V0KGtleTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nLCAuLi5hcmdzOiB1bmtub3duW10pOiBQcm9taXNlPHVua25vd24+O1xuICBnZXQoa2V5OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZyB8IG51bGw+O1xuICBkZWwoLi4ua2V5czogc3RyaW5nW10pOiBQcm9taXNlPG51bWJlcj47XG4gIC8qKiBPcHRpb25hbCBMdWEgZXZhbCAoaW9yZWRpcyBoYXMgaXQpLiBXaGVuIHByZXNlbnQsIHJlbGVhc2UgdXNlcyBhbiBhdG9taWNcbiAgICogIGNvbXBhcmUtYW5kLWRlbGV0ZSBzbyBhIGxhcHNlZCBob2xkZXIgY2FuJ3QgZnJlZSBhIHN1Y2Nlc3NvcidzIGxvY2suICovXG4gIGV2YWw/KHNjcmlwdDogc3RyaW5nLCBudW1LZXlzOiBudW1iZXIsIC4uLmFyZ3M6IHVua25vd25bXSk6IFByb21pc2U8dW5rbm93bj47XG4gIC8qKiBHcmFjZWZ1bCBjbG9zZSAoaW9yZWRpcyBgcXVpdGApIOKAlCBjYWxsZWQgb24gc2h1dGRvd24gc28gdGhlIGNvbm5lY3Rpb25cbiAgICogIGRvZXNuJ3Qga2VlcCB0aGUgZXZlbnQgbG9vcCBhbGl2ZS4gKi9cbiAgcXVpdD8oKTogUHJvbWlzZTx1bmtub3duPjtcbiAgLyoqIEltbWVkaWF0ZSBjbG9zZSAoaW9yZWRpcyBgZGlzY29ubmVjdGApIOKAlCB0aGUgZmFsbGJhY2sgaWYgYHF1aXRgIGZhaWxzLiAqL1xuICBkaXNjb25uZWN0PygpOiB2b2lkO1xufVxuXG4vKipcbiAqIEJlc3QtZWZmb3J0IGNsb3NlIG9mIGEgbGVhZGVyLWxvY2sgUmVkaXMgY2xpZW50IG9uIHNodXRkb3duLiBQcmVmZXJzIGdyYWNlZnVsXG4gKiBgcXVpdGA7IGZhbGxzIGJhY2sgdG8gYGRpc2Nvbm5lY3RgLiBOby1vcCBmb3IgYSBudWxsIGNsaWVudCAvIG9uZSB3aXRob3V0XG4gKiBlaXRoZXIgbWV0aG9kLiBOZXZlciB0aHJvd3MuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbG9zZUxlYWRlckxvY2socmVkaXM6IExvY2tSZWRpcyB8IG51bGwgfCB1bmRlZmluZWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKCFyZWRpcykgcmV0dXJuO1xuICB0cnkge1xuICAgIGlmICh0eXBlb2YgcmVkaXMucXVpdCA9PT0gJ2Z1bmN0aW9uJykgeyBhd2FpdCByZWRpcy5xdWl0KCk7IHJldHVybjsgfVxuICB9IGNhdGNoIHtcbiAgICAvLyBmYWxsIHRocm91Z2ggdG8gZGlzY29ubmVjdFxuICB9XG4gIHRyeSB7IHJlZGlzLmRpc2Nvbm5lY3Q/LigpOyB9IGNhdGNoIHsgLyogYmVzdC1lZmZvcnQgKi8gfVxufVxuXG4vKiogQXRvbWljIFwiZGVsZXRlIG9ubHkgaWYgSSBzdGlsbCBvd24gaXRcIiDigJQgY2xvc2VzIHRoZSBnZXQtdGhlbi1kZWwgcmFjZSB3aGVyZVxuICogIHRoZSBsb2NrIGV4cGlyZXMgYW5kIGlzIHJlLWFjcXVpcmVkIGJldHdlZW4gdGhlIHR3byBjYWxscy4gKi9cbmNvbnN0IFJFTEVBU0VfSUZfT1dORVIgPVxuICAnaWYgcmVkaXMuY2FsbChcImdldFwiLCBLRVlTWzFdKSA9PSBBUkdWWzFdIHRoZW4gcmV0dXJuIHJlZGlzLmNhbGwoXCJkZWxcIiwgS0VZU1sxXSkgZWxzZSByZXR1cm4gMCBlbmQnO1xuXG4vKipcbiAqIFJ1biBgZm5gIG9ubHkgaWYgdGhpcyBwcm9jZXNzIHdpbnMgYSBzaG9ydC1saXZlZCBkaXN0cmlidXRlZCBsb2NrIOKAlCB0aGVcbiAqIGFjcm9zcy1wb2RzIFwic2luZ2xlIHJ1bm5lclwiIGd1YXJkIGZvciBwZXJpb2RpYyBqb2JzIChkaWdlc3QgZmx1c2hlcywgZXRjLikuXG4gKlxuICogVXNlcyBgU0VUIGtleSB0b2tlbiBOWCBQWCB0dGxgLCBzbyBleGFjdGx5IG9uZSBob2xkZXIgcnVucyBwZXIgd2luZG93IGFuZCB0aGVcbiAqIGxvY2sgYXV0by1leHBpcmVzIGlmIHRoYXQgaG9sZGVyIGRpZXMgbWlkLXJ1biAobm8gc3R1Y2sgbG9jaykuIFRoZSBsb2NrIGlzXG4gKiByZWxlYXNlZCBvbiBjb21wbGV0aW9uLCBidXQgb25seSBpZiB3ZSBzdGlsbCBvd24gaXQg4oCUIGEgc2xvd2VyIHByZWRlY2Vzc29yXG4gKiBuZXZlciBkZWxldGVzIGEgc3VjY2Vzc29yJ3MgbG9jay4gYHR0bE1zYCBzaG91bGQgY29tZm9ydGFibHkgZXhjZWVkIG9uZSBydW4uXG4gKlxuICogSWYgUmVkaXMgY2FuJ3QgYmUgcmVhY2hlZCB0aGUgcnVuIGlzIHNraXBwZWQgKHJldHVybnMgZmFsc2UpIHJhdGhlciB0aGFuXG4gKiByZWplY3Rpbmcg4oCUIGNhbGxlcnMgYXJlIHRpbWVycywgYW5kIGEgUmVkaXMgYmxpcCBtdXN0IG5vdCBjcmFzaCB0aGUgcHJvY2Vzcy5cbiAqXG4gKiBAcmV0dXJucyB0cnVlIGlmIHdlIGhlbGQgdGhlIGxvY2sgYW5kIHJhbiBgZm5gOyBmYWxzZSBpZiBhbm90aGVyIGhvbGRlciBkaWQgb3JcbiAqICAgdGhlIGxvY2sgY291bGRuJ3QgYmUgdGFrZW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3aXRoTGVhZGVyTG9jayhcbiAgcmVkaXM6IExvY2tSZWRpcyxcbiAga2V5OiBzdHJpbmcsXG4gIHR0bE1zOiBudW1iZXIsXG4gIGZuOiAoKSA9PiBQcm9taXNlPHZvaWQ+LFxuKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGNvbnN0IHRva2VuID0gcmFuZG9tVVVJRCgpO1xuICBsZXQgYWNxdWlyZWQ6IHVua25vd247XG4gIHRyeSB7XG4gICAgYWNxdWlyZWQgPSBhd2FpdCByZWRpcy5zZXQoa2V5LCB0b2tlbiwgJ1BYJywgdHRsTXMsICdOWCcpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICAvLyBSZWRpcyB1bnJlYWNoYWJsZSAoaW5jbHVkaW5nIGJlZm9yZSB0aGUgZmlyc3QgY29ubmVjdGlvbiBjb21wbGV0ZXMpOiBub1xuICAgIC8vIHBvZCBjYW4gcHJvdmUgaXQncyB0aGUgbGVhZGVyLCBzbyBub2JvZHkgcnVucyB0aGlzIHdpbmRvdy4gU2tpcHBpbmcgaXNcbiAgICAvLyB0aGUgc2FmZSBzaWRlIOKAlCBydW5uaW5nIGhlcmUgY291bGQgZHVwbGljYXRlIGRlc3RydWN0aXZlIHdvcmsgYWNyb3NzIHBvZHMuXG4gICAgbG9ja0xvZ2dlci53YXJuKCdMZWFkZXIgbG9jayB1bmF2YWlsYWJsZTsgc2tpcHBpbmcgdGhpcyBydW4nLCB7XG4gICAgICBrZXksIGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVyciksXG4gICAgfSk7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGlmIChhY3F1aXJlZCAhPT0gJ09LJykgcmV0dXJuIGZhbHNlO1xuXG4gIHRyeSB7XG4gICAgYXdhaXQgZm4oKTtcbiAgfSBmaW5hbGx5IHtcbiAgICAvLyBSZWxlYXNlIG9ubHkgaWYgdGhlIGxvY2sgaXMgc3RpbGwgb3VycyDigJQgaWYgb3VyIHJ1biBvdmVycmFuIHRoZSBUVEwgYW5kXG4gICAgLy8gYW5vdGhlciBob2xkZXIgdG9vayBvdmVyLCByZWxlYXNpbmcgaGVyZSB3b3VsZCBmcmVlIHRoZWlyIGxvY2sgZWFybHkuXG4gICAgLy8gUHJlZmVyIGFuIGF0b21pYyBDQVMgKEx1YSk7IGZhbGwgYmFjayB0byBnZXQtdGhlbi1kZWwgZm9yIGNsaWVudHMgd2l0aG91dFxuICAgIC8vIGV2YWwgKHRoZSBzbWFsbCBub24tYXRvbWljIHdpbmRvdyBpcyBUVEwtYm91bmRlZCkuXG4gICAgdHJ5IHtcbiAgICAgIGlmICh0eXBlb2YgcmVkaXMuZXZhbCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBhd2FpdCByZWRpcy5ldmFsKFJFTEVBU0VfSUZfT1dORVIsIDEsIGtleSwgdG9rZW4pO1xuICAgICAgfSBlbHNlIGlmICgoYXdhaXQgcmVkaXMuZ2V0KGtleSkpID09PSB0b2tlbikge1xuICAgICAgICBhd2FpdCByZWRpcy5kZWwoa2V5KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIEJlc3QtZWZmb3J0OiB0aGUgVFRMIGV4cGlyZXMgdGhlIGxvY2sgcmVnYXJkbGVzcy5cbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogQSBgTG9ja1JlZGlzYCBiYWNrZWQgYnkgdGhlIHNoYXJlZCBlbnYgUmVkaXMgY2xpZW50IChzZWUgZW52LXJlZGlzLnRzKSDigJQgdGhlIGxlYWRlci1sb2NrIGNvdW50ZXJwYXJ0IHRvIGBjcmVhdGVFbnZSZWRpc0F1ZGl0U3Bvb2xgIC9cbiAqIGBjcmVhdGVFbnZSZWRpc1Rva2VuUmV2b2NhdGlvblN0b3JlYCwgZm9yIGEgc2VydmljZSB0aGF0IGhhcyBubyBCdWxsTVEgY2xpZW50IHRvXG4gKiBib3Jyb3cuIFJldHVybnMgYG51bGxgIHdoZW4gUmVkaXMgaXNuJ3QgY29uZmlndXJlZCwgc28gYSBjYWxsZXIgY2FuIGRlZ3JhZGUgdG9cbiAqIHJ1bm5pbmcgb24gZXZlcnkgcG9kICh0aGUgYXRvbWljIGd1YXJkcyBpdCB3cmFwcyBtdXN0IHN0aWxsIG1ha2UgdGhhdCBzYWZlKS5cbiAqIFRoZSBjbGllbnQgaXMgYSByZWFsIGlvcmVkaXMgaW5zdGFuY2U7IGlvcmVkaXMgZXhwb3NlcyBgc2V0YC9gZ2V0YC9gZGVsYC9gZXZhbGAsXG4gKiBzYXRpc2Z5aW5nIGBMb2NrUmVkaXNgIGluY2x1ZGluZyB0aGUgYXRvbWljIENBUyByZWxlYXNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRW52UmVkaXNMb2NrKCk6IExvY2tSZWRpcyB8IG51bGwge1xuICBjb25zdCBpbnN0ID0gY3JlYXRlRW52UmVkaXNDbGllbnQ8TG9ja1JlZGlzPignbGVhZGVyLWxvY2snKTtcbiAgaWYgKGluc3QpIGxvY2tMb2dnZXIuaW5mbygnUmVkaXMgbGVhZGVyLWxvY2sgY2xpZW50IGluaXRpYWxpemVkJyk7XG4gIHJldHVybiBpbnN0O1xufVxuIl19
|