@pipeline-builder/api-core 4.1.2 → 4.1.4

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.
Files changed (81) hide show
  1. package/README.md +3 -3
  2. package/lib/errors/app-errors.d.ts +0 -4
  3. package/lib/errors/app-errors.js +1 -8
  4. package/lib/helpers/restore-helpers.d.ts +3 -3
  5. package/lib/helpers/restore-helpers.js +4 -4
  6. package/lib/helpers/sse-helpers.d.ts +8 -1
  7. package/lib/helpers/sse-helpers.js +11 -3
  8. package/lib/middleware/auth.d.ts +174 -43
  9. package/lib/middleware/auth.js +384 -179
  10. package/lib/middleware/index.d.ts +1 -0
  11. package/lib/middleware/index.js +2 -1
  12. package/lib/middleware/route-table.d.ts +104 -0
  13. package/lib/middleware/route-table.js +230 -0
  14. package/lib/middleware/step-up.d.ts +48 -16
  15. package/lib/middleware/step-up.js +74 -36
  16. package/lib/services/api-key-exchange.d.ts +25 -0
  17. package/lib/services/api-key-exchange.js +219 -0
  18. package/lib/services/audit-spool.d.ts +2 -3
  19. package/lib/services/audit-spool.js +3 -4
  20. package/lib/services/cache-service.d.ts +65 -21
  21. package/lib/services/cache-service.js +173 -78
  22. package/lib/services/compliance-event-subscriber.d.ts +5 -4
  23. package/lib/services/compliance-event-subscriber.js +8 -6
  24. package/lib/services/durable-event-bus.d.ts +22 -39
  25. package/lib/services/durable-event-bus.js +109 -47
  26. package/lib/services/env-redis.d.ts +114 -35
  27. package/lib/services/env-redis.js +215 -73
  28. package/lib/services/http-client.d.ts +10 -15
  29. package/lib/services/http-client.js +32 -27
  30. package/lib/services/index.d.ts +3 -0
  31. package/lib/services/index.js +4 -1
  32. package/lib/services/jwks-cache.d.ts +102 -0
  33. package/lib/services/jwks-cache.js +222 -0
  34. package/lib/services/leader-lock.d.ts +6 -3
  35. package/lib/services/leader-lock.js +20 -5
  36. package/lib/services/quota.d.ts +44 -10
  37. package/lib/services/quota.js +95 -51
  38. package/lib/services/remote-audit-client.d.ts +1 -1
  39. package/lib/services/remote-audit-client.js +32 -1
  40. package/lib/services/scheduler.d.ts +2 -3
  41. package/lib/services/scheduler.js +2 -2
  42. package/lib/services/service-boot.d.ts +4 -2
  43. package/lib/services/service-boot.js +7 -3
  44. package/lib/services/service-keys.d.ts +83 -0
  45. package/lib/services/service-keys.js +354 -0
  46. package/lib/services/sse-ticket-store.d.ts +55 -22
  47. package/lib/services/sse-ticket-store.js +122 -79
  48. package/lib/services/token-revocation.d.ts +1 -1
  49. package/lib/services/token-revocation.js +31 -19
  50. package/lib/testing/route-coverage.d.ts +64 -0
  51. package/lib/testing/route-coverage.js +183 -0
  52. package/lib/testing/service-tokens.d.ts +66 -0
  53. package/lib/testing/service-tokens.js +137 -0
  54. package/lib/testing/tier-mock.d.ts +2 -2
  55. package/lib/testing/tier-mock.js +3 -3
  56. package/lib/testing/user-tokens.d.ts +75 -0
  57. package/lib/testing/user-tokens.js +115 -0
  58. package/lib/types/catalog-metadata.d.ts +0 -2
  59. package/lib/types/catalog-metadata.js +1 -3
  60. package/lib/types/common.d.ts +103 -4
  61. package/lib/types/common.js +11 -1
  62. package/lib/types/error-codes.d.ts +15 -0
  63. package/lib/types/error-codes.js +22 -1
  64. package/lib/types/permissions.d.ts +35 -1
  65. package/lib/types/permissions.js +70 -2
  66. package/lib/utils/api-key.d.ts +34 -0
  67. package/lib/utils/api-key.js +75 -0
  68. package/lib/utils/env.d.ts +0 -2
  69. package/lib/utils/env.js +1 -6
  70. package/lib/utils/index.d.ts +3 -0
  71. package/lib/utils/index.js +4 -1
  72. package/lib/utils/jwk.d.ts +98 -0
  73. package/lib/utils/jwk.js +139 -0
  74. package/lib/utils/logger.d.ts +4 -1
  75. package/lib/utils/logger.js +1 -22
  76. package/lib/utils/secret-encryption.d.ts +7 -2
  77. package/lib/utils/secret-encryption.js +47 -4
  78. package/lib/utils/secret-rotation.d.ts +17 -0
  79. package/lib/utils/secret-rotation.js +54 -0
  80. package/lib/validation/common-schemas.js +5 -4
  81. package/package.json +20 -1
@@ -1,37 +1,16 @@
1
- /**
2
- * Durable, at-least-once event backbone (Redis Streams).
3
- *
4
- * The platform's cross-service signalling today is point-to-point HTTP plus
5
- * polling reconcilers plus an IN-PROCESS best-effort emitter ({@link entityEvents}).
6
- * There is no durable bus, so a signal produced on one pod is either delivered
7
- * synchronously over HTTP (coupling + retry-storm risk) or lost if the consumer
8
- * is momentarily down (the reconcilers exist to paper over exactly that).
9
- *
10
- * This is that missing bus, as a reusable primitive:
11
- * - **publish(topic, payload)** → `XADD` onto a bounded (`MAXLEN ~`) per-topic
12
- * stream. Durable across restarts, shared by all replicas.
13
- * - **subscribe({topic, group, consumer, handler})** → a `XREADGROUP` consumer.
14
- * Consumer GROUPS give at-least-once: each group gets every message once, a
15
- * message stays pending until the handler `XACK`s it, and `XAUTOCLAIM`
16
- * reclaims messages stranded by a crashed consumer. Multiple groups on one
17
- * topic = independent fan-out (e.g. quota-sync AND audit both react).
18
- *
19
- * FAIL-SAFE + OPT-IN, exactly like the audit spool: {@link createEnvRedisDurableEventBus}
20
- * returns `null` when Redis isn't configured, so a service without it simply
21
- * doesn't use the bus (keeps today's behavior). A publish failure is dropped-with-
22
- * -metric, never thrown; a handler throw leaves the message un-acked for redelivery.
23
- *
24
- * Migrating a specific reconciler (e.g. billing→quota entitlement sync) onto this
25
- * is deliberately follow-on work — this lands the backbone + an additive entity-
26
- * event bridge; the reconcilers stay as the belt-and-suspenders backstop until a
27
- * flow is fully cut over.
28
- */
29
- import type { EntityEventSubscriber } from './entity-events.js';
30
- /** A delivered event: its stream id, topic, and decoded payload. */
1
+ /** A delivered event: its stream id, topic, publish time, and decoded payload. */
31
2
  export interface EventEnvelope<T = unknown> {
32
3
  /** Redis Stream entry id (`<ms>-<seq>`) — also the idempotency handle. */
33
4
  id: string;
34
5
  topic: string;
6
+ /**
7
+ * When the event was published (the Redis server time embedded in the stream
8
+ * id). A redelivery keeps its ORIGINAL publish time, so a consumer applying
9
+ * last-writer-wins state (e.g. an entitlement sync) can order events by it
10
+ * instead of by delivery time and refuse to let a stale retry overwrite a
11
+ * newer state.
12
+ */
13
+ publishedAt: Date;
35
14
  payload: T;
36
15
  }
37
16
  export interface SubscribeOptions<T = unknown> {
@@ -49,6 +28,11 @@ export interface SubscribeOptions<T = unknown> {
49
28
  blockMs?: number;
50
29
  /** A pending message idle this long (ms) is reclaimed from a dead consumer (default 60000). */
51
30
  minIdleMs?: number;
31
+ /**
32
+ * Deliveries after which an un-acked message is dead-lettered to
33
+ * `evt:<topic>:dlq` and acked instead of being redelivered again (default 10).
34
+ */
35
+ maxDeliveries?: number;
52
36
  }
53
37
  export interface EventSubscription {
54
38
  /** Stop the consumer loop. Resolves once the in-flight read returns. */
@@ -71,7 +55,15 @@ export interface RedisStreamClient {
71
55
  xreadgroup(...args: (string | number)[]): Promise<unknown>;
72
56
  xack(key: string, group: string, ...ids: string[]): Promise<number>;
73
57
  xautoclaim(...args: (string | number)[]): Promise<unknown>;
58
+ xpending(key: string, group: string, ...args: (string | number)[]): Promise<unknown>;
59
+ xrange(key: string, start: string, end: string, ...args: (string | number)[]): Promise<unknown>;
60
+ /** A new connection with the same options — each subscriber reads on its own. */
61
+ duplicate(): RedisStreamClient;
62
+ quit?(): Promise<unknown>;
63
+ on?(event: 'error', cb: (err: unknown) => void): unknown;
74
64
  }
65
+ /** Dead-letter stream for a topic. */
66
+ export declare function deadLetterStreamKey(topic: string): string;
75
67
  /** Build a durable event bus over a Redis Streams client. */
76
68
  export declare function createRedisDurableEventBus(redis: RedisStreamClient, opts?: {
77
69
  maxLen?: number;
@@ -85,12 +77,3 @@ export declare function createRedisDurableEventBus(redis: RedisStreamClient, opt
85
77
  export declare function createEnvRedisDurableEventBus(opts?: {
86
78
  maxLen?: number;
87
79
  }): DurableEventBus | null;
88
- /** Topic the entity-event bridge publishes to. */
89
- export declare const ENTITY_EVENT_TOPIC = "entity-events";
90
- /**
91
- * An {@link EntityEventSubscriber} that forwards each entity event to the durable
92
- * bus. `bus.publish` is fail-safe (drops-with-metric, never throws), matching the
93
- * emitter's fire-and-forget contract. Note `EntityEvent.timestamp` (a Date)
94
- * serializes to an ISO string on the wire — a consumer re-hydrates as needed.
95
- */
96
- export declare function createEntityEventBusPublisher(bus: DurableEventBus): EntityEventSubscriber;
@@ -1,5 +1,32 @@
1
1
  // Copyright 2026 Pipeline Builder Contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Durable, at-least-once event backbone (Redis Streams).
5
+ *
6
+ * The platform's cross-service signalling today is point-to-point HTTP plus
7
+ * polling reconcilers plus an IN-PROCESS best-effort emitter ({@link entityEvents}).
8
+ * There is no durable bus, so a signal produced on one pod is either delivered
9
+ * synchronously over HTTP (coupling + retry-storm risk) or lost if the consumer
10
+ * is momentarily down (the reconcilers exist to paper over exactly that).
11
+ *
12
+ * This is that missing bus, as a reusable primitive:
13
+ * - **publish(topic, payload)** → `XADD` onto a bounded (`MAXLEN ~`) per-topic
14
+ * stream. Durable across restarts, shared by all replicas.
15
+ * - **subscribe({topic, group, consumer, handler})** → a `XREADGROUP` consumer
16
+ * on its OWN connection (`duplicate()`), so a blocking read never delays a
17
+ * publish. Consumer GROUPS give at-least-once: each group gets every message
18
+ * once, a message stays pending until the handler `XACK`s it, and `XAUTOCLAIM`
19
+ * reclaims messages stranded by a crashed consumer. A message that has been
20
+ * delivered `maxDeliveries` times without an ack is moved to a dead-letter
21
+ * stream (`evt:<topic>:dlq`) and acked, so a poison message can't redeliver
22
+ * forever. Multiple groups on one topic = independent fan-out.
23
+ *
24
+ * FAIL-SAFE + OPT-IN, exactly like the audit spool: {@link createEnvRedisDurableEventBus}
25
+ * returns `null` when Redis isn't configured, so a service without it simply
26
+ * doesn't use the bus (keeps today's behavior). A publish failure is dropped-with-
27
+ * -metric, never thrown; a handler throw leaves the message un-acked for redelivery.
28
+ *
29
+ */
3
30
  import { createEnvRedisClient } from './env-redis.js';
4
31
  import { createLogger } from '../utils/logger.js';
5
32
  import { emitCounter } from '../utils/metric-emitter.js';
@@ -8,12 +35,22 @@ const DEFAULT_MAXLEN = 10_000;
8
35
  const DEFAULT_BATCH = 16;
9
36
  const DEFAULT_BLOCK_MS = 5_000;
10
37
  const DEFAULT_MIN_IDLE_MS = 60_000;
38
+ const DEFAULT_MAX_DELIVERIES = 10;
11
39
  function errMsg(err) {
12
40
  return err instanceof Error ? err.message : String(err);
13
41
  }
14
42
  function streamKey(topic) {
15
43
  return `evt:${topic}`;
16
44
  }
45
+ /** Dead-letter stream for a topic. */
46
+ export function deadLetterStreamKey(topic) {
47
+ return `evt:${topic}:dlq`;
48
+ }
49
+ /** Publish time from a stream id (`<ms>-<seq>`); epoch 0 for an unparseable id. */
50
+ function publishedAtFromId(id) {
51
+ const ms = Number(id.split('-')[0]);
52
+ return new Date(Number.isFinite(ms) ? ms : 0);
53
+ }
17
54
  /** True when the error is Redis' "group already exists" (idempotent create). */
18
55
  function isBusyGroup(err) {
19
56
  return errMsg(err).includes('BUSYGROUP');
@@ -46,15 +83,16 @@ function parseEntries(topic, entries) {
46
83
  // A missing `d` field (an entry not written by publish()) OR a corrupt JSON
47
84
  // payload both yield an undefined-payload envelope so `deliver()` ACKs it —
48
85
  // a poison message must never wedge the group by staying perpetually pending.
86
+ const publishedAt = publishedAtFromId(id);
49
87
  if (raw === undefined) {
50
- out.push({ id, topic, payload: undefined });
88
+ out.push({ id, topic, publishedAt, payload: undefined });
51
89
  continue;
52
90
  }
53
91
  try {
54
- out.push({ id, topic, payload: JSON.parse(raw) });
92
+ out.push({ id, topic, publishedAt, payload: JSON.parse(raw) });
55
93
  }
56
94
  catch {
57
- out.push({ id, topic, payload: undefined });
95
+ out.push({ id, topic, publishedAt, payload: undefined });
58
96
  }
59
97
  }
60
98
  return out;
@@ -80,11 +118,18 @@ export function createRedisDurableEventBus(redis, opts = {}) {
80
118
  subscribe(subOpts) {
81
119
  const { topic, group, consumer, handler } = subOpts;
82
120
  const key = streamKey(topic);
121
+ const dlqKey = deadLetterStreamKey(topic);
83
122
  const batch = Math.max(1, subOpts.batchSize ?? DEFAULT_BATCH);
84
123
  const blockMs = Math.max(0, subOpts.blockMs ?? DEFAULT_BLOCK_MS);
85
124
  const minIdle = Math.max(0, subOpts.minIdleMs ?? DEFAULT_MIN_IDLE_MS);
125
+ const maxDeliveries = Math.max(1, subOpts.maxDeliveries ?? DEFAULT_MAX_DELIVERIES);
126
+ // `XREADGROUP BLOCK` holds its connection for up to `blockMs`; on the
127
+ // shared client every publish queued behind it. Read on a dedicated one.
128
+ const reader = redis.duplicate();
129
+ // A duplicated ioredis connection doesn't inherit listeners; without one a
130
+ // connection error is an unhandled 'error' event that crashes the process.
131
+ reader.on?.('error', (err) => logger.warn('Event bus reader connection error', { topic, group, error: errMsg(err) }));
86
132
  let stopped = false;
87
- let started = false;
88
133
  const deliver = async (envs) => {
89
134
  for (const env of envs) {
90
135
  try {
@@ -92,7 +137,7 @@ export function createRedisDurableEventBus(redis, opts = {}) {
92
137
  await handler(env);
93
138
  // Ack on success OR on an undefined (corrupt) payload — a poison
94
139
  // message must not block the group forever.
95
- await redis.xack(key, group, env.id);
140
+ await reader.xack(key, group, env.id);
96
141
  emitCounter('event_bus_delivered_total', { topic, group });
97
142
  }
98
143
  catch (err) {
@@ -104,23 +149,67 @@ export function createRedisDurableEventBus(redis, opts = {}) {
104
149
  }
105
150
  }
106
151
  };
152
+ /**
153
+ * Move every idle pending message that has already been delivered
154
+ * `maxDeliveries` times to the dead-letter stream and ack it. The entry
155
+ * keeps its payload plus where it came from, so an operator can inspect or
156
+ * replay it. If copying fails the message is left pending (retried next
157
+ * pass) — it is never acked without being preserved.
158
+ */
159
+ const deadLetterExhausted = async () => {
160
+ const pending = await reader.xpending(key, group, 'IDLE', minIdle, '-', '+', batch);
161
+ if (!Array.isArray(pending))
162
+ return;
163
+ for (const p of pending) {
164
+ if (!Array.isArray(p) || p.length < 4)
165
+ continue;
166
+ const id = String(p[0]);
167
+ const deliveries = Number(p[3]);
168
+ if (!(deliveries >= maxDeliveries))
169
+ continue;
170
+ const entries = await reader.xrange(key, id, id);
171
+ const fields = Array.isArray(entries) && Array.isArray(entries[0]) && Array.isArray(entries[0][1]) ? entries[0][1] : [];
172
+ let raw = '';
173
+ for (let i = 0; i + 1 < fields.length; i += 2) {
174
+ if (String(fields[i]) === 'd') {
175
+ raw = String(fields[i + 1]);
176
+ break;
177
+ }
178
+ }
179
+ await reader.xadd(dlqKey, 'MAXLEN', '~', maxLen, '*', 'd', raw, 'sourceId', id, 'group', group, 'deliveries', deliveries);
180
+ await reader.xack(key, group, id);
181
+ emitCounter('event_bus_dead_lettered_total', { topic, group });
182
+ logger.error('Event exceeded max deliveries; moved to dead-letter stream', {
183
+ topic, group, id, deliveries, deadLetterStream: dlqKey,
184
+ });
185
+ }
186
+ };
187
+ // The consumer group must exist before XREADGROUP can succeed. Creating it
188
+ // is retried inside the loop: at startup the client may not have connected
189
+ // yet, and a single failed attempt used to leave the consumer reading a
190
+ // group that was never created — forever.
191
+ let groupReady = false;
107
192
  const ensureGroup = async () => {
108
193
  try {
109
194
  // MKSTREAM so the stream need not exist yet; '$' = only new messages.
110
- await redis.xgroup('CREATE', key, group, '$', 'MKSTREAM');
195
+ await reader.xgroup('CREATE', key, group, '$', 'MKSTREAM');
196
+ groupReady = true;
111
197
  }
112
198
  catch (err) {
113
- if (!isBusyGroup(err)) {
114
- logger.warn('Event bus group create failed', { topic, group, error: errMsg(err) });
115
- }
199
+ if (!isBusyGroup(err))
200
+ throw err;
201
+ groupReady = true;
116
202
  }
117
203
  };
118
204
  const loop = async () => {
119
- await ensureGroup();
120
205
  while (!stopped) {
121
206
  try {
122
- // 1) Reclaim messages stranded by a crashed consumer in this group.
123
- const claimed = await redis.xautoclaim(key, group, consumer, minIdle, '0', 'COUNT', batch);
207
+ if (!groupReady)
208
+ await ensureGroup();
209
+ // 1) Dead-letter messages that have exhausted their deliveries, then
210
+ // reclaim the rest stranded by a failed/crashed consumer.
211
+ await deadLetterExhausted();
212
+ const claimed = await reader.xautoclaim(key, group, consumer, minIdle, '0', 'COUNT', batch);
124
213
  // XAUTOCLAIM reply: [nextCursor, entries, deletedIds]
125
214
  if (Array.isArray(claimed) && claimed.length >= 2) {
126
215
  await deliver(parseEntries(topic, claimed[1]));
@@ -128,7 +217,7 @@ export function createRedisDurableEventBus(redis, opts = {}) {
128
217
  if (stopped)
129
218
  break;
130
219
  // 2) Read new, never-delivered messages for this group ('>').
131
- const res = await redis.xreadgroup('GROUP', group, consumer, 'COUNT', batch, 'BLOCK', blockMs, 'STREAMS', key, '>');
220
+ const res = await reader.xreadgroup('GROUP', group, consumer, 'COUNT', batch, 'BLOCK', blockMs, 'STREAMS', key, '>');
132
221
  // XREADGROUP reply: [[streamKey, entries]] | null (on block timeout)
133
222
  if (Array.isArray(res) && res.length > 0 && Array.isArray(res[0]) && res[0].length >= 2) {
134
223
  await deliver(parseEntries(topic, res[0][1]));
@@ -137,18 +226,17 @@ export function createRedisDurableEventBus(redis, opts = {}) {
137
226
  catch (err) {
138
227
  if (stopped)
139
228
  break;
229
+ // The stream or group was deleted out from under us — recreate it.
230
+ if (/NOGROUP/i.test(errMsg(err)))
231
+ groupReady = false;
140
232
  logger.warn('Event bus consumer loop error; backing off', { topic, group, error: errMsg(err) });
141
233
  // Back off so a persistent Redis error doesn't hot-spin.
142
234
  await new Promise((r) => setTimeout(r, 1000));
143
235
  }
144
236
  }
145
237
  };
146
- let loopPromise = Promise.resolve();
147
- if (!started) {
148
- started = true;
149
- loopPromise = loop();
150
- logger.info('Event bus consumer started', { topic, group, consumer });
151
- }
238
+ const loopPromise = loop();
239
+ logger.info('Event bus consumer started', { topic, group, consumer, maxDeliveries });
152
240
  return {
153
241
  async stop() {
154
242
  stopped = true;
@@ -156,6 +244,7 @@ export function createRedisDurableEventBus(redis, opts = {}) {
156
244
  // down its DB) doesn't race a message still being processed. Bounded by
157
245
  // the XREADGROUP BLOCK window. Never rejects — the loop swallows errors.
158
246
  await loopPromise.catch(() => undefined);
247
+ await Promise.resolve(reader.quit?.()).catch(() => undefined);
159
248
  },
160
249
  };
161
250
  },
@@ -174,31 +263,4 @@ export function createEnvRedisDurableEventBus(opts = {}) {
174
263
  logger.info('Redis durable event bus initialized');
175
264
  return createRedisDurableEventBus(inst, opts);
176
265
  }
177
- // ---------------------------------------------------------------------------
178
- // Entity-event bridge (additive, opt-in)
179
- //
180
- // Lets a service give its in-process {@link entityEvents} emitter a DURABLE,
181
- // cross-pod delivery leg WITHOUT changing existing behavior: register the
182
- // returned subscriber and every entity mutation is ALSO published to the bus.
183
- // A consumer elsewhere (`bus.subscribe`) then reacts at-least-once across pods
184
- // and restarts. The in-process subscribers keep firing exactly as before, so
185
- // this is purely additive — no double-processing unless a service intentionally
186
- // runs a bus consumer for the same reaction. Kept here (not in entity-events.ts)
187
- // so that module stays infrastructure-free per its own contract.
188
- // ---------------------------------------------------------------------------
189
- /** Topic the entity-event bridge publishes to. */
190
- export const ENTITY_EVENT_TOPIC = 'entity-events';
191
- /**
192
- * An {@link EntityEventSubscriber} that forwards each entity event to the durable
193
- * bus. `bus.publish` is fail-safe (drops-with-metric, never throws), matching the
194
- * emitter's fire-and-forget contract. Note `EntityEvent.timestamp` (a Date)
195
- * serializes to an ISO string on the wire — a consumer re-hydrates as needed.
196
- */
197
- export function createEntityEventBusPublisher(bus) {
198
- return {
199
- async onEntityEvent(event) {
200
- await bus.publish(ENTITY_EVENT_TOPIC, event);
201
- },
202
- };
203
- }
204
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHVyYWJsZS1ldmVudC1idXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvZHVyYWJsZS1ldmVudC1idXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQWdDdEMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUV6RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsbUJBQW1CLENBQUMsQ0FBQztBQW9EakQsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDO0FBQzlCLE1BQU0sYUFBYSxHQUFHLEVBQUUsQ0FBQztBQUN6QixNQUFNLGdCQUFnQixHQUFHLEtBQUssQ0FBQztBQUMvQixNQUFNLG1CQUFtQixHQUFHLE1BQU0sQ0FBQztBQUVuQyxTQUFTLE1BQU0sQ0FBQyxHQUFZO0lBQzFCLE9BQU8sR0FBRyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQzFELENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxLQUFhO0lBQzlCLE9BQU8sT0FBTyxLQUFLLEVBQUUsQ0FBQztBQUN4QixDQUFDO0FBRUQsZ0ZBQWdGO0FBQ2hGLFNBQVMsV0FBVyxDQUFDLEdBQVk7SUFDL0IsT0FBTyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBQzNDLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQVMsWUFBWSxDQUFJLEtBQWEsRUFBRSxPQUFnQjtJQUN0RCxNQUFNLEdBQUcsR0FBdUIsRUFBRSxDQUFDO0lBQ25DLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9B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QUMsTUFBTSxrQkFBa0IsR0FBRyxlQUFlLENBQUM7QUFFbEQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsNkJBQTZCLENBQUMsR0FBb0I7SUFDaEUsT0FBTztRQUNMLEtBQUssQ0FBQyxhQUFhLENBQUMsS0FBa0I7WUFDcEMsTUFBTSxHQUFHLENBQUMsT0FBTyxDQUFDLGtCQUFrQixFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQy9DLENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIER1cmFibGUsIGF0LWxlYXN0LW9uY2UgZXZlbnQgYmFja2JvbmUgKFJlZGlzIFN0cmVhbXMpLlxuICpcbiAqIFRoZSBwbGF0Zm9ybSdzIGNyb3NzLXNlcnZpY2Ugc2lnbmFsbGluZyB0b2RheSBpcyBwb2ludC10by1wb2ludCBIVFRQIHBsdXNcbiAqIHBvbGxpbmcgcmVjb25jaWxlcnMgcGx1cyBhbiBJTi1QUk9DRVNTIGJlc3QtZWZmb3J0IGVtaXR0ZXIgKHtAbGluayBlbnRpdHlFdmVudHN9KS5cbiAqIFRoZXJlIGlzIG5vIGR1cmFibGUgYnVzLCBzbyBhIHNpZ25hbCBwcm9kdWNlZCBvbiBvbmUgcG9kIGlzIGVpdGhlciBkZWxpdmVyZWRcbiAqIHN5bmNocm9ub3VzbHkgb3ZlciBIVFRQIChjb3VwbGluZyArIHJldHJ5LXN0b3JtIHJpc2spIG9yIGxvc3QgaWYgdGhlIGNvbnN1bWVyXG4gKiBpcyBtb21lbnRhcmlseSBkb3duICh0aGUgcmVjb25jaWxlcnMgZXhpc3QgdG8gcGFwZXIgb3ZlciBleGFjdGx5IHRoYXQpLlxuICpcbiAqIFRoaXMgaXMgdGhhdCBtaXNzaW5nIGJ1cywgYXMgYSByZXVzYWJsZSBwcmltaXRpdmU6XG4gKiAgLSAqKnB1Ymxpc2godG9waWMsIHBheWxvYWQpKiog4oaSIGBYQUREYCBvbnRvIGEgYm91bmRlZCAoYE1BWExFTiB+YCkgcGVyLXRvcGljXG4gKiAgICBzdHJlYW0uIER1cmFibGUgYWNyb3NzIHJlc3RhcnRzLCBzaGFyZWQgYnkgYWxsIHJlcGxpY2FzLlxuICogIC0gKipzdWJzY3JpYmUoe3RvcGljLCBncm91cCwgY29uc3VtZXIsIGhhbmRsZXJ9KSoqIOKGkiBhIGBYUkVBREdST1VQYCBjb25zdW1lci5cbiAqICAgIENvbnN1bWVyIEdST1VQUyBnaXZlIGF0LWxlYXN0LW9uY2U6IGVhY2ggZ3JvdXAgZ2V0cyBldmVyeSBtZXNzYWdlIG9uY2UsIGFcbiAqICAgIG1lc3NhZ2Ugc3RheXMgcGVuZGluZyB1bnRpbCB0aGUgaGFuZGxlciBgWEFDS2BzIGl0LCBhbmQgYFhBVVRPQ0xBSU1gXG4gKiAgICByZWNsYWltcyBtZXNzYWdlcyBzdHJhbmRlZCBieSBhIGNyYXNoZWQgY29uc3VtZXIuIE11bHRpcGxlIGdyb3VwcyBvbiBvbmVcbiAqICAgIHRvcGljID0gaW5kZXBlbmRlbnQgZmFuLW91dCAoZS5nLiBxdW90YS1zeW5jIEFORCBhdWRpdCBib3RoIHJlYWN0KS5cbiAqXG4gKiBGQUlMLVNBRkUgKyBPUFQtSU4sIGV4YWN0bHkgbGlrZSB0aGUgYXVkaXQgc3Bvb2w6IHtAbGluayBjcmVhdGVFbnZSZWRpc0R1cmFibGVFdmVudEJ1c31cbiAqIHJldHVybnMgYG51bGxgIHdoZW4gUmVkaXMgaXNuJ3QgY29uZmlndXJlZCwgc28gYSBzZXJ2aWNlIHdpdGhvdXQgaXQgc2ltcGx5XG4gKiBkb2Vzbid0IHVzZSB0aGUgYnVzIChrZWVwcyB0b2RheSdzIGJlaGF2aW9yKS4gQSBwdWJsaXNoIGZhaWx1cmUgaXMgZHJvcHBlZC13aXRoLVxuICogLW1ldHJpYywgbmV2ZXIgdGhyb3duOyBhIGhhbmRsZXIgdGhyb3cgbGVhdmVzIHRoZSBtZXNzYWdlIHVuLWFja2VkIGZvciByZWRlbGl2ZXJ5LlxuICpcbiAqIE1pZ3JhdGluZyBhIHNwZWNpZmljIHJlY29uY2lsZXIgKGUuZy4gYmlsbGluZ+KGknF1b3RhIGVudGl0bGVtZW50IHN5bmMpIG9udG8gdGhpc1xuICogaXMgZGVsaWJlcmF0ZWx5IGZvbGxvdy1vbiB3b3JrIOKAlCB0aGlzIGxhbmRzIHRoZSBiYWNrYm9uZSArIGFuIGFkZGl0aXZlIGVudGl0eS1cbiAqIGV2ZW50IGJyaWRnZTsgdGhlIHJlY29uY2lsZXJzIHN0YXkgYXMgdGhlIGJlbHQtYW5kLXN1c3BlbmRlcnMgYmFja3N0b3AgdW50aWwgYVxuICogZmxvdyBpcyBmdWxseSBjdXQgb3Zlci5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IEVudGl0eUV2ZW50LCBFbnRpdHlFdmVudFN1YnNjcmliZXIgfSBmcm9tICcuL2VudGl0eS1ldmVudHMuanMnO1xuaW1wb3J0IHsgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICcuL2Vudi1yZWRpcy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgZW1pdENvdW50ZXIgfSBmcm9tICcuLi91dGlscy9tZXRyaWMtZW1pdHRlci5qcyc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignZHVyYWJsZS1ldmVudC1idXMnKTtcblxuLyoqIEEgZGVsaXZlcmVkIGV2ZW50OiBpdHMgc3RyZWFtIGlkLCB0b3BpYywgYW5kIGRlY29kZWQgcGF5bG9hZC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRXZlbnRFbnZlbG9wZTxUID0gdW5rbm93bj4ge1xuICAvKiogUmVkaXMgU3RyZWFtIGVudHJ5IGlkIChgPG1zPi08c2VxPmApIOKAlCBhbHNvIHRoZSBpZGVtcG90ZW5jeSBoYW5kbGUuICovXG4gIGlkOiBzdHJpbmc7XG4gIHRvcGljOiBzdHJpbmc7XG4gIHBheWxvYWQ6IFQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3Vic2NyaWJlT3B0aW9uczxUID0gdW5rbm93bj4ge1xuICAvKiogVG9waWMgKHN0cmVhbSkgdG8gY29uc3VtZS4gKi9cbiAgdG9waWM6IHN0cmluZztcbiAgLyoqIENvbnN1bWVyIGdyb3VwIOKAlCBvbmUgZGVsaXZlcnkgcGVyIGdyb3VwOyBkaXN0aW5jdCBncm91cHMgZmFuIG91dC4gKi9cbiAgZ3JvdXA6IHN0cmluZztcbiAgLyoqIFRoaXMgY29uc3VtZXIncyBpZCB3aXRoaW4gdGhlIGdyb3VwIChlLmcuIGAke3NlcnZpY2V9LSR7cGlkfWApLiAqL1xuICBjb25zdW1lcjogc3RyaW5nO1xuICAvKiogQXN5bmMgaGFuZGxlci4gUmVzb2x2ZXMg4oeSIHRoZSBtZXNzYWdlIGlzIGFja2VkOyB0aHJvd3Mg4oeSIGxlZnQgcGVuZGluZyBmb3IgcmVkZWxpdmVyeS4gKi9cbiAgaGFuZGxlcjogKGVudjogRXZlbnRFbnZlbG9wZTxUPikgPT4gUHJvbWlzZTx2b2lkPjtcbiAgLyoqIE1heCBtZXNzYWdlcyBwZXIgcmVhZCAoZGVmYXVsdCAxNikuICovXG4gIGJhdGNoU2l6ZT86IG51bWJlcjtcbiAgLyoqIFhSRUFER1JPVVAgYmxvY2sgdGltZSBpbiBtcyAoZGVmYXVsdCA1MDAwKS4gKi9cbiAgYmxvY2tNcz86IG51bWJlcjtcbiAgLyoqIEEgcGVuZGluZyBtZXNzYWdlIGlkbGUgdGhpcyBsb25nIChtcykgaXMgcmVjbGFpbWVkIGZyb20gYSBkZWFkIGNvbnN1bWVyIChkZWZhdWx0IDYwMDAwKS4gKi9cbiAgbWluSWRsZU1zPzogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEV2ZW50U3Vic2NyaXB0aW9uIHtcbiAgLyoqIFN0b3AgdGhlIGNvbnN1bWVyIGxvb3AuIFJlc29sdmVzIG9uY2UgdGhlIGluLWZsaWdodCByZWFkIHJldHVybnMuICovXG4gIHN0b3AoKTogUHJvbWlzZTx2b2lkPjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBEdXJhYmxlRXZlbnRCdXMge1xuICAvKiogUHVibGlzaCBhIHBheWxvYWQgdG8gYSB0b3BpYy4gUmV0dXJucyB0aGUgc3RyZWFtIGlkLCBvciBudWxsIG9uIGZhaWx1cmUgKGRyb3BwZWQsIG5ldmVyIHRocm93cykuICovXG4gIHB1Ymxpc2g8VD4odG9waWM6IHN0cmluZywgcGF5bG9hZDogVCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIC8qKiBTdGFydCBhIGNvbnN1bWVyLWdyb3VwIHJlYWRlciBmb3IgYSB0b3BpYy4gRmlyZS1hbmQtZm9yZ2V0IGxvb3A7IGNhbGwgYHN0b3AoKWAgdG8gZW5kIGl0LiAqL1xuICBzdWJzY3JpYmU8VD4ob3B0czogU3Vic2NyaWJlT3B0aW9uczxUPik6IEV2ZW50U3Vic2NyaXB0aW9uO1xufVxuXG4vKipcbiAqIFRoZSBzdWJzZXQgb2YgaW9yZWRpcycgU3RyZWFtIEFQSSB0aGUgYnVzIHVzZXMuIEtlcHQgbWluaW1hbCBzbyBhIHRlc3QgY2FuXG4gKiBzdXBwbHkgYW4gaW4tbWVtb3J5IGZha2UgYW5kIHNvIG1lcmVseSBpbXBvcnRpbmcgdGhpcyBuZXZlciBoYXJkLWRlcGVuZHMgb25cbiAqIGlvcmVkaXMgKGNvbnN0cnVjdGVkIHZpYSB0aGUgc2hhcmVkIGVudiBoZWxwZXIpLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlZGlzU3RyZWFtQ2xpZW50IHtcbiAgeGFkZChrZXk6IHN0cmluZywgLi4uYXJnczogKHN0cmluZyB8IG51bWJlcilbXSk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIHhncm91cCguLi5hcmdzOiAoc3RyaW5nIHwgbnVtYmVyKVtdKTogUHJvbWlzZTx1bmtub3duPjtcbiAgeHJlYWRncm91cCguLi5hcmdzOiAoc3RyaW5nIHwgbnVtYmVyKVtdKTogUHJvbWlzZTx1bmtub3duPjtcbiAgeGFjayhrZXk6IHN0cmluZywgZ3JvdXA6IHN0cmluZywgLi4uaWRzOiBzdHJpbmdbXSk6IFByb21pc2U8bnVtYmVyPjtcbiAgeGF1dG9jbGFpbSguLi5hcmdzOiAoc3RyaW5nIHwgbnVtYmVyKVtdKTogUHJvbWlzZTx1bmtub3duPjtcbn1cblxuY29uc3QgREVGQVVMVF9NQVhMRU4gPSAxMF8wMDA7XG5jb25zdCBERUZBVUxUX0JBVENIID0gMTY7XG5jb25zdCBERUZBVUxUX0JMT0NLX01TID0gNV8wMDA7XG5jb25zdCBERUZBVUxUX01JTl9JRExFX01TID0gNjBfMDAwO1xuXG5mdW5jdGlvbiBlcnJNc2coZXJyOiB1bmtub3duKTogc3RyaW5nIHtcbiAgcmV0dXJuIGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKTtcbn1cblxuZnVuY3Rpb24gc3RyZWFtS2V5KHRvcGljOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYGV2dDoke3RvcGljfWA7XG59XG5cbi8qKiBUcnVlIHdoZW4gdGhlIGVycm9yIGlzIFJlZGlzJyBcImdyb3VwIGFscmVhZHkgZXhpc3RzXCIgKGlkZW1wb3RlbnQgY3JlYXRlKS4gKi9cbmZ1bmN0aW9uIGlzQnVzeUdyb3VwKGVycjogdW5rbm93bik6IGJvb2xlYW4ge1xuICByZXR1cm4gZXJyTXNnKGVycikuaW5jbHVkZXMoJ0JVU1lHUk9VUCcpO1xufVxuXG4vKipcbiAqIFBhcnNlIHRoZSBuZXN0ZWQgYXJyYXkgc2hhcGUgWFJFQURHUk9VUCAvIFhBVVRPQ0xBSU0gcmV0dXJuIGludG8gZmxhdCBlbnZlbG9wZXMuXG4gKiBYUkVBREdST1VQOiBgW1tzdHJlYW1LZXksIFtbaWQsIFtmLCB2LCAuLi5dXSwgLi4uXV1dYC5cbiAqIFhBVVRPQ0xBSU06IGBbbmV4dEN1cnNvciwgW1tpZCwgW2YsIHYsIC4uLl1dLCAuLi5dLCBkZWxldGVkSWRzXWAuXG4gKiBXZSBzdG9yZSB0aGUgSlNPTiBwYXlsb2FkIHVuZGVyIGEgc2luZ2xlIGZpZWxkIGBkYC5cbiAqL1xuZnVuY3Rpb24gcGFyc2VFbnRyaWVzPFQ+KHRvcGljOiBzdHJpbmcsIGVudHJpZXM6IHVua25vd24pOiBFdmVudEVudmVsb3BlPFQ+W10ge1xuICBjb25zdCBvdXQ6IEV2ZW50RW52ZWxvcGU8VD5bXSA9IFtdO1xuICBpZiAoIUFycmF5LmlzQXJyYXkoZW50cmllcykpIHJldHVybiBvdXQ7XG4gIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgIGlmICghQXJyYXkuaXNBcnJheShlbnRyeSkgfHwgZW50cnkubGVuZ3RoIDwgMikgY29udGludWU7XG4gICAgY29uc3QgaWQgPSBTdHJpbmcoZW50cnlbMF0pO1xuICAgIGNvbnN0IGZpZWxkcyA9IGVudHJ5WzFdO1xuICAgIGlmICghQXJyYXkuaXNBcnJheShmaWVsZHMpKSBjb250aW51ZTtcbiAgICAvLyBmaWVsZHMgaXMgW25hbWUsIHZhbHVlLCBuYW1lLCB2YWx1ZSwgLi4uXTsgZmluZCAnZCcuXG4gICAgbGV0IHJhdzogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIGZvciAobGV0IGkgPSAwOyBpICsgMSA8IGZpZWxkcy5sZW5ndGg7IGkgKz0gMikge1xuICAgICAgaWYgKFN0cmluZyhmaWVsZHNbaV0pID09PSAnZCcpIHsgcmF3ID0gU3RyaW5nKGZpZWxkc1tpICsgMV0pOyBicmVhazsgfVxuICAgIH1cbiAgICAvLyBBIG1pc3NpbmcgYGRgIGZpZWxkIChhbiBlbnRyeSBub3Qgd3JpdHRlbiBieSBwdWJsaXNoKCkpIE9SIGEgY29ycnVwdCBKU09OXG4gICAgLy8gcGF5bG9hZCBib3RoIHlpZWxkIGFuIHVuZGVmaW5lZC1wYXlsb2FkIGVudmVsb3BlIHNvIGBkZWxpdmVyKClgIEFDS3MgaXQg4oCUXG4gICAgLy8gYSBwb2lzb24gbWVzc2FnZSBtdXN0IG5ldmVyIHdlZGdlIHRoZSBncm91cCBieSBzdGF5aW5nIHBlcnBldHVhbGx5IHBlbmRpbmcuXG4gICAgaWYgKHJhdyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBvdXQucHVzaCh7IGlkLCB0b3BpYywgcGF5bG9hZDogdW5kZWZpbmVkIGFzIHVua25vd24gYXMgVCB9KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgb3V0LnB1c2goeyBpZCwgdG9waWMsIHBheWxvYWQ6IEpTT04ucGFyc2UocmF3KSBhcyBUIH0pO1xuICAgIH0gY2F0Y2gge1xuICAgICAgb3V0LnB1c2goeyBpZCwgdG9waWMsIHBheWxvYWQ6IHVuZGVmaW5lZCBhcyB1bmtub3duIGFzIFQgfSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBvdXQ7XG59XG5cbi8qKiBCdWlsZCBhIGR1cmFibGUgZXZlbnQgYnVzIG92ZXIgYSBSZWRpcyBTdHJlYW1zIGNsaWVudC4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWRpc0R1cmFibGVFdmVudEJ1cyhcbiAgcmVkaXM6IFJlZGlzU3RyZWFtQ2xpZW50LFxuICBvcHRzOiB7IG1heExlbj86IG51bWJlciB9ID0ge30sXG4pOiBEdXJhYmxlRXZlbnRCdXMge1xuICBjb25zdCBtYXhMZW4gPSBNYXRoLm1heCgxLCBvcHRzLm1heExlbiA/PyBERUZBVUxUX01BWExFTik7XG5cbiAgcmV0dXJuIHtcbiAgICBhc3luYyBwdWJsaXNoPFQ+KHRvcGljOiBzdHJpbmcsIHBheWxvYWQ6IFQpOiBQcm9taXNlPHN0cmluZyB8IG51bGw+IHtcbiAgICAgIGNvbnN0IGtleSA9IHN0cmVhbUtleSh0b3BpYyk7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBNQVhMRU4gfiA8bj4gYm91bmRzIHRoZSBzdHJlYW0gYXBwcm94aW1hdGVseSAoY2hlYXApOyBgKmAgPSBzZXJ2ZXIgaWQuXG4gICAgICAgIGNvbnN0IGlkID0gYXdhaXQgcmVkaXMueGFkZChrZXksICdNQVhMRU4nLCAnficsIG1heExlbiwgJyonLCAnZCcsIEpTT04uc3RyaW5naWZ5KHBheWxvYWQpKTtcbiAgICAgICAgZW1pdENvdW50ZXIoJ2V2ZW50X2J1c19wdWJsaXNoZWRfdG90YWwnLCB7IHRvcGljIH0pO1xuICAgICAgICByZXR1cm4gaWQ7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgZW1pdENvdW50ZXIoJ2V2ZW50X2J1c19wdWJsaXNoX2ZhaWxlZF90b3RhbCcsIHsgdG9waWMgfSk7XG4gICAgICAgIGxvZ2dlci53YXJuKCdFdmVudCBidXMgcHVibGlzaCBmYWlsZWQgKGV2ZW50IGRyb3BwZWQpJywgeyB0b3BpYywgZXJyb3I6IGVyck1zZyhlcnIpIH0pO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICB9LFxuXG4gICAgc3Vic2NyaWJlPFQ+KHN1Yk9wdHM6IFN1YnNjcmliZU9wdGlvbnM8VD4pOiBFdmVudFN1YnNjcmlwdGlvbiB7XG4gICAgICBjb25zdCB7IHRvcGljLCBncm91cCwgY29uc3VtZXIsIGhhbmRsZXIgfSA9IHN1Yk9wdHM7XG4gICAgICBjb25zdCBrZXkgPSBzdHJlYW1LZXkodG9waWMpO1xuICAgICAgY29uc3QgYmF0Y2ggPSBNYXRoLm1heCgxLCBzdWJPcHRzLmJhdGNoU2l6ZSA/PyBERUZBVUxUX0JBVENIKTtcbiAgICAgIGNvbnN0IGJsb2NrTXMgPSBNYXRoLm1heCgwLCBzdWJPcHRzLmJsb2NrTXMgPz8gREVGQVVMVF9CTE9DS19NUyk7XG4gICAgICBjb25zdCBtaW5JZGxlID0gTWF0aC5tYXgoMCwgc3ViT3B0cy5taW5JZGxlTXMgPz8gREVGQVVMVF9NSU5fSURMRV9NUyk7XG5cbiAgICAgIGxldCBzdG9wcGVkID0gZmFsc2U7XG4gICAgICBsZXQgc3RhcnRlZCA9IGZhbHNlO1xuXG4gICAgICBjb25zdCBkZWxpdmVyID0gYXN5bmMgKGVudnM6IEV2ZW50RW52ZWxvcGU8VD5bXSk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBmb3IgKGNvbnN0IGVudiBvZiBlbnZzKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGlmIChlbnYucGF5bG9hZCAhPT0gdW5kZWZpbmVkKSBhd2FpdCBoYW5kbGVyKGVudik7XG4gICAgICAgICAgICAvLyBBY2sgb24gc3VjY2VzcyBPUiBvbiBhbiB1bmRlZmluZWQgKGNvcnJ1cHQpIHBheWxvYWQg4oCUIGEgcG9pc29uXG4gICAgICAgICAgICAvLyBtZXNzYWdlIG11c3Qgbm90IGJsb2NrIHRoZSBncm91cCBmb3JldmVyLlxuICAgICAgICAgICAgYXdhaXQgcmVkaXMueGFjayhrZXksIGdyb3VwLCBlbnYuaWQpO1xuICAgICAgICAgICAgZW1pdENvdW50ZXIoJ2V2ZW50X2J1c19kZWxpdmVyZWRfdG90YWwnLCB7IHRvcGljLCBncm91cCB9KTtcbiAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIC8vIExlYXZlIGl0IHBlbmRpbmcgKG5vIFhBQ0spIOKAlCBYQVVUT0NMQUlNIHJlZGVsaXZlcnMgaXQgYWZ0ZXIgbWluSWRsZS5cbiAgICAgICAgICAgIGVtaXRDb3VudGVyKCdldmVudF9idXNfaGFuZGxlcl9mYWlsZWRfdG90YWwnLCB7IHRvcGljLCBncm91cCB9KTtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKCdFdmVudCBoYW5kbGVyIGZhaWxlZDsgbGVhdmluZyBtZXNzYWdlIHBlbmRpbmcgZm9yIHJlZGVsaXZlcnknLCB7XG4gICAgICAgICAgICAgIHRvcGljLCBncm91cCwgaWQ6IGVudi5pZCwgZXJyb3I6IGVyck1zZyhlcnIpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICBjb25zdCBlbnN1cmVHcm91cCA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAvLyBNS1NUUkVBTSBzbyB0aGUgc3RyZWFtIG5lZWQgbm90IGV4aXN0IHlldDsgJyQnID0gb25seSBuZXcgbWVzc2FnZXMuXG4gICAgICAgICAgYXdhaXQgcmVkaXMueGdyb3VwKCdDUkVBVEUnLCBrZXksIGdyb3VwLCAnJCcsICdNS1NUUkVBTScpO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBpZiAoIWlzQnVzeUdyb3VwKGVycikpIHtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKCdFdmVudCBidXMgZ3JvdXAgY3JlYXRlIGZhaWxlZCcsIHsgdG9waWMsIGdyb3VwLCBlcnJvcjogZXJyTXNnKGVycikgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICBjb25zdCBsb29wID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBhd2FpdCBlbnN1cmVHcm91cCgpO1xuICAgICAgICB3aGlsZSAoIXN0b3BwZWQpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gMSkgUmVjbGFpbSBtZXNzYWdlcyBzdHJhbmRlZCBieSBhIGNyYXNoZWQgY29uc3VtZXIgaW4gdGhpcyBncm91cC5cbiAgICAgICAgICAgIGNvbnN0IGNsYWltZWQgPSBhd2FpdCByZWRpcy54YXV0b2NsYWltKGtleSwgZ3JvdXAsIGNvbnN1bWVyLCBtaW5JZGxlLCAnMCcsICdDT1VOVCcsIGJhdGNoKTtcbiAgICAgICAgICAgIC8vIFhBVVRPQ0xBSU0gcmVwbHk6IFtuZXh0Q3Vyc29yLCBlbnRyaWVzLCBkZWxldGVkSWRzXVxuICAgICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoY2xhaW1lZCkgJiYgY2xhaW1lZC5sZW5ndGggPj0gMikge1xuICAgICAgICAgICAgICBhd2FpdCBkZWxpdmVyKHBhcnNlRW50cmllczxUPih0b3BpYywgY2xhaW1lZFsxXSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHN0b3BwZWQpIGJyZWFrO1xuXG4gICAgICAgICAgICAvLyAyKSBSZWFkIG5ldywgbmV2ZXItZGVsaXZlcmVkIG1lc3NhZ2VzIGZvciB0aGlzIGdyb3VwICgnPicpLlxuICAgICAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgcmVkaXMueHJlYWRncm91cChcbiAgICAgICAgICAgICAgJ0dST1VQJywgZ3JvdXAsIGNvbnN1bWVyLCAnQ09VTlQnLCBiYXRjaCwgJ0JMT0NLJywgYmxvY2tNcywgJ1NUUkVBTVMnLCBrZXksICc+JyxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICAvLyBYUkVBREdST1VQIHJlcGx5OiBbW3N0cmVhbUtleSwgZW50cmllc11dIHwgbnVsbCAob24gYmxvY2sgdGltZW91dClcbiAgICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KHJlcykgJiYgcmVzLmxlbmd0aCA+IDAgJiYgQXJyYXkuaXNBcnJheShyZXNbMF0pICYmIHJlc1swXS5sZW5ndGggPj0gMikge1xuICAgICAgICAgICAgICBhd2FpdCBkZWxpdmVyKHBhcnNlRW50cmllczxUPih0b3BpYywgcmVzWzBdWzFdKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICBpZiAoc3RvcHBlZCkgYnJlYWs7XG4gICAgICAgICAgICBsb2dnZXIud2FybignRXZlbnQgYnVzIGNvbnN1bWVyIGxvb3AgZXJyb3I7IGJhY2tpbmcgb2ZmJywgeyB0b3BpYywgZ3JvdXAsIGVycm9yOiBlcnJNc2coZXJyKSB9KTtcbiAgICAgICAgICAgIC8vIEJhY2sgb2ZmIHNvIGEgcGVyc2lzdGVudCBSZWRpcyBlcnJvciBkb2Vzbid0IGhvdC1zcGluLlxuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHNldFRpbWVvdXQociwgMTAwMCkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgbGV0IGxvb3BQcm9taXNlID0gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICBpZiAoIXN0YXJ0ZWQpIHtcbiAgICAgICAgc3RhcnRlZCA9IHRydWU7XG4gICAgICAgIGxvb3BQcm9taXNlID0gbG9vcCgpO1xuICAgICAgICBsb2dnZXIuaW5mbygnRXZlbnQgYnVzIGNvbnN1bWVyIHN0YXJ0ZWQnLCB7IHRvcGljLCBncm91cCwgY29uc3VtZXIgfSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGFzeW5jIHN0b3AoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgICAgc3RvcHBlZCA9IHRydWU7XG4gICAgICAgICAgLy8gQXdhaXQgdGhlIGluLWZsaWdodCByZWFkL2hhbmRsZXIgc28gYSBjYWxsZXIgKGUuZy4gYSBzZXJ2aWNlIHNodXR0aW5nXG4gICAgICAgICAgLy8gZG93biBpdHMgREIpIGRvZXNuJ3QgcmFjZSBhIG1lc3NhZ2Ugc3RpbGwgYmVpbmcgcHJvY2Vzc2VkLiBCb3VuZGVkIGJ5XG4gICAgICAgICAgLy8gdGhlIFhSRUFER1JPVVAgQkxPQ0sgd2luZG93LiBOZXZlciByZWplY3RzIOKAlCB0aGUgbG9vcCBzd2FsbG93cyBlcnJvcnMuXG4gICAgICAgICAgYXdhaXQgbG9vcFByb21pc2UuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIGR1cmFibGUgZXZlbnQgYnVzIGZyb20gdGhlIGFtYmllbnQgUmVkaXMgZW52IHZpYSB0aGUgc2hhcmVkXG4gKiBgY3JlYXRlRW52UmVkaXNDbGllbnRgLiBSZXR1cm5zIGBudWxsYCB3aGVuIFJlZGlzIGlzIG5vdCBjb25maWd1cmVkIOKAlCB0aGVcbiAqIGNhbGxlciB0aGVuIHJ1bnMgd2l0aG91dCBhIGJ1cyAodG9kYXkncyBIVFRQL3JlY29uY2lsZXIgYmVoYXZpb3IpLCBuZXZlclxuICogY3Jhc2hpbmcuIE9wdCBpbiB3aXRoIGEgc2luZ2xlIGJvb3QgbGluZSB3aGVyZSBhIHNlcnZpY2Ugd2FudHMgZHVyYWJsZSBldmVudHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFbnZSZWRpc0R1cmFibGVFdmVudEJ1cyhvcHRzOiB7IG1heExlbj86IG51bWJlciB9ID0ge30pOiBEdXJhYmxlRXZlbnRCdXMgfCBudWxsIHtcbiAgY29uc3QgaW5zdCA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PFJlZGlzU3RyZWFtQ2xpZW50PignZXZlbnQtYnVzJyk7XG4gIGlmICghaW5zdCkgcmV0dXJuIG51bGw7XG4gIGxvZ2dlci5pbmZvKCdSZWRpcyBkdXJhYmxlIGV2ZW50IGJ1cyBpbml0aWFsaXplZCcpO1xuICByZXR1cm4gY3JlYXRlUmVkaXNEdXJhYmxlRXZlbnRCdXMoaW5zdCwgb3B0cyk7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRW50aXR5LWV2ZW50IGJyaWRnZSAoYWRkaXRpdmUsIG9wdC1pbilcbi8vXG4vLyBMZXRzIGEgc2VydmljZSBnaXZlIGl0cyBpbi1wcm9jZXNzIHtAbGluayBlbnRpdHlFdmVudHN9IGVtaXR0ZXIgYSBEVVJBQkxFLFxuLy8gY3Jvc3MtcG9kIGRlbGl2ZXJ5IGxlZyBXSVRIT1VUIGNoYW5naW5nIGV4aXN0aW5nIGJlaGF2aW9yOiByZWdpc3RlciB0aGVcbi8vIHJldHVybmVkIHN1YnNjcmliZXIgYW5kIGV2ZXJ5IGVudGl0eSBtdXRhdGlvbiBpcyBBTFNPIHB1Ymxpc2hlZCB0byB0aGUgYnVzLlxuLy8gQSBjb25zdW1lciBlbHNld2hlcmUgKGBidXMuc3Vic2NyaWJlYCkgdGhlbiByZWFjdHMgYXQtbGVhc3Qtb25jZSBhY3Jvc3MgcG9kc1xuLy8gYW5kIHJlc3RhcnRzLiBUaGUgaW4tcHJvY2VzcyBzdWJzY3JpYmVycyBrZWVwIGZpcmluZyBleGFjdGx5IGFzIGJlZm9yZSwgc29cbi8vIHRoaXMgaXMgcHVyZWx5IGFkZGl0aXZlIOKAlCBubyBkb3VibGUtcHJvY2Vzc2luZyB1bmxlc3MgYSBzZXJ2aWNlIGludGVudGlvbmFsbHlcbi8vIHJ1bnMgYSBidXMgY29uc3VtZXIgZm9yIHRoZSBzYW1lIHJlYWN0aW9uLiBLZXB0IGhlcmUgKG5vdCBpbiBlbnRpdHktZXZlbnRzLnRzKVxuLy8gc28gdGhhdCBtb2R1bGUgc3RheXMgaW5mcmFzdHJ1Y3R1cmUtZnJlZSBwZXIgaXRzIG93biBjb250cmFjdC5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4vKiogVG9waWMgdGhlIGVudGl0eS1ldmVudCBicmlkZ2UgcHVibGlzaGVzIHRvLiAqL1xuZXhwb3J0IGNvbnN0IEVOVElUWV9FVkVOVF9UT1BJQyA9ICdlbnRpdHktZXZlbnRzJztcblxuLyoqXG4gKiBBbiB7QGxpbmsgRW50aXR5RXZlbnRTdWJzY3JpYmVyfSB0aGF0IGZvcndhcmRzIGVhY2ggZW50aXR5IGV2ZW50IHRvIHRoZSBkdXJhYmxlXG4gKiBidXMuIGBidXMucHVibGlzaGAgaXMgZmFpbC1zYWZlIChkcm9wcy13aXRoLW1ldHJpYywgbmV2ZXIgdGhyb3dzKSwgbWF0Y2hpbmcgdGhlXG4gKiBlbWl0dGVyJ3MgZmlyZS1hbmQtZm9yZ2V0IGNvbnRyYWN0LiBOb3RlIGBFbnRpdHlFdmVudC50aW1lc3RhbXBgIChhIERhdGUpXG4gKiBzZXJpYWxpemVzIHRvIGFuIElTTyBzdHJpbmcgb24gdGhlIHdpcmUg4oCUIGEgY29uc3VtZXIgcmUtaHlkcmF0ZXMgYXMgbmVlZGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRW50aXR5RXZlbnRCdXNQdWJsaXNoZXIoYnVzOiBEdXJhYmxlRXZlbnRCdXMpOiBFbnRpdHlFdmVudFN1YnNjcmliZXIge1xuICByZXR1cm4ge1xuICAgIGFzeW5jIG9uRW50aXR5RXZlbnQoZXZlbnQ6IEVudGl0eUV2ZW50KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICBhd2FpdCBidXMucHVibGlzaChFTlRJVFlfRVZFTlRfVE9QSUMsIGV2ZW50KTtcbiAgICB9LFxuICB9O1xufVxuIl19
266
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHVyYWJsZS1ldmVudC1idXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvZHVyYWJsZS1ldmVudC1idXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EwQkc7QUFFSCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRXpELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0FBdUVqRCxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUM7QUFDOUIsTUFBTSxhQUFhLEdBQUcsRUFBRSxDQUFDO0FBQ3pCLE1BQU0sZ0JBQWdCLEdBQUcsS0FBSyxDQUFDO0FBQy9CLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSxDQUFDO0FBQ25DLE1BQU0sc0JBQXNCLEdBQUcsRUFBRSxDQUFDO0FBRWxDLFNBQVMsTUFBTSxDQUFDLEdBQVk7SUFDMUIsT0FBTyxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDMUQsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLEtBQWE7SUFDOUIsT0FBTyxPQUFPLEtBQUssRUFBRSxDQUFDO0FBQ3hCLENBQUM7QUFFRCxzQ0FBc0M7QUFDdEMsTUFBTSxVQUFVLG1CQUFtQixDQUFDLEtBQWE7SUFDL0MsT0FBTyxPQUFPLEtBQUssTUFBTSxDQUFDO0FBQzVCLENBQUM7QUFFRCxtRkFBbUY7QUFDbkYsU0FBUyxpQkFBaUIsQ0FBQyxFQUFVO0lBQ25DLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxFQUFF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RyxJQUFJLEVBQUUsQ0FBQztZQUMzQixNQUFNLENBQUMsSUFBSSxDQUFDLDRCQUE0QixFQUFFLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQztZQUVyRixPQUFPO2dCQUNMLEtBQUssQ0FBQyxJQUFJO29CQUNSLE9BQU8sR0FBRyxJQUFJLENBQUM7b0JBQ2Ysd0VBQXdFO29CQUN4RSx3RUFBd0U7b0JBQ3hFLHlFQUF5RTtvQkFDekUsTUFBTSxXQUFXLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO29CQUN6QyxNQUFNLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUM7Z0JBQ2hFLENBQUM7YUFDRixDQUFDO1FBQ0osQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsNkJBQTZCLENBQUMsSUFBSSxHQUF3QixFQUFFO0lBQzFFLE1BQU0sSUFBSSxHQUFHLG9CQUFvQixDQUFvQixXQUFXLENBQUMsQ0FBQztJQUNsRSxJQUFJLENBQUMsSUFBSTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3ZCLE1BQU0sQ0FBQyxJQUFJLENBQUMscUNBQXFDLENBQUMsQ0FBQztJQUNuRCxPQUFPLDBCQUEwQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztBQUNoRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogRHVyYWJsZSwgYXQtbGVhc3Qtb25jZSBldmVudCBiYWNrYm9uZSAoUmVkaXMgU3RyZWFtcykuXG4gKlxuICogVGhlIHBsYXRmb3JtJ3MgY3Jvc3Mtc2VydmljZSBzaWduYWxsaW5nIHRvZGF5IGlzIHBvaW50LXRvLXBvaW50IEhUVFAgcGx1c1xuICogcG9sbGluZyByZWNvbmNpbGVycyBwbHVzIGFuIElOLVBST0NFU1MgYmVzdC1lZmZvcnQgZW1pdHRlciAoe0BsaW5rIGVudGl0eUV2ZW50c30pLlxuICogVGhlcmUgaXMgbm8gZHVyYWJsZSBidXMsIHNvIGEgc2lnbmFsIHByb2R1Y2VkIG9uIG9uZSBwb2QgaXMgZWl0aGVyIGRlbGl2ZXJlZFxuICogc3luY2hyb25vdXNseSBvdmVyIEhUVFAgKGNvdXBsaW5nICsgcmV0cnktc3Rvcm0gcmlzaykgb3IgbG9zdCBpZiB0aGUgY29uc3VtZXJcbiAqIGlzIG1vbWVudGFyaWx5IGRvd24gKHRoZSByZWNvbmNpbGVycyBleGlzdCB0byBwYXBlciBvdmVyIGV4YWN0bHkgdGhhdCkuXG4gKlxuICogVGhpcyBpcyB0aGF0IG1pc3NpbmcgYnVzLCBhcyBhIHJldXNhYmxlIHByaW1pdGl2ZTpcbiAqICAtICoqcHVibGlzaCh0b3BpYywgcGF5bG9hZCkqKiDihpIgYFhBRERgIG9udG8gYSBib3VuZGVkIChgTUFYTEVOIH5gKSBwZXItdG9waWNcbiAqICAgIHN0cmVhbS4gRHVyYWJsZSBhY3Jvc3MgcmVzdGFydHMsIHNoYXJlZCBieSBhbGwgcmVwbGljYXMuXG4gKiAgLSAqKnN1YnNjcmliZSh7dG9waWMsIGdyb3VwLCBjb25zdW1lciwgaGFuZGxlcn0pKiog4oaSIGEgYFhSRUFER1JPVVBgIGNvbnN1bWVyXG4gKiAgICBvbiBpdHMgT1dOIGNvbm5lY3Rpb24gKGBkdXBsaWNhdGUoKWApLCBzbyBhIGJsb2NraW5nIHJlYWQgbmV2ZXIgZGVsYXlzIGFcbiAqICAgIHB1Ymxpc2guIENvbnN1bWVyIEdST1VQUyBnaXZlIGF0LWxlYXN0LW9uY2U6IGVhY2ggZ3JvdXAgZ2V0cyBldmVyeSBtZXNzYWdlXG4gKiAgICBvbmNlLCBhIG1lc3NhZ2Ugc3RheXMgcGVuZGluZyB1bnRpbCB0aGUgaGFuZGxlciBgWEFDS2BzIGl0LCBhbmQgYFhBVVRPQ0xBSU1gXG4gKiAgICByZWNsYWltcyBtZXNzYWdlcyBzdHJhbmRlZCBieSBhIGNyYXNoZWQgY29uc3VtZXIuIEEgbWVzc2FnZSB0aGF0IGhhcyBiZWVuXG4gKiAgICBkZWxpdmVyZWQgYG1heERlbGl2ZXJpZXNgIHRpbWVzIHdpdGhvdXQgYW4gYWNrIGlzIG1vdmVkIHRvIGEgZGVhZC1sZXR0ZXJcbiAqICAgIHN0cmVhbSAoYGV2dDo8dG9waWM+OmRscWApIGFuZCBhY2tlZCwgc28gYSBwb2lzb24gbWVzc2FnZSBjYW4ndCByZWRlbGl2ZXJcbiAqICAgIGZvcmV2ZXIuIE11bHRpcGxlIGdyb3VwcyBvbiBvbmUgdG9waWMgPSBpbmRlcGVuZGVudCBmYW4tb3V0LlxuICpcbiAqIEZBSUwtU0FGRSArIE9QVC1JTiwgZXhhY3RseSBsaWtlIHRoZSBhdWRpdCBzcG9vbDoge0BsaW5rIGNyZWF0ZUVudlJlZGlzRHVyYWJsZUV2ZW50QnVzfVxuICogcmV0dXJucyBgbnVsbGAgd2hlbiBSZWRpcyBpc24ndCBjb25maWd1cmVkLCBzbyBhIHNlcnZpY2Ugd2l0aG91dCBpdCBzaW1wbHlcbiAqIGRvZXNuJ3QgdXNlIHRoZSBidXMgKGtlZXBzIHRvZGF5J3MgYmVoYXZpb3IpLiBBIHB1Ymxpc2ggZmFpbHVyZSBpcyBkcm9wcGVkLXdpdGgtXG4gKiAtbWV0cmljLCBuZXZlciB0aHJvd247IGEgaGFuZGxlciB0aHJvdyBsZWF2ZXMgdGhlIG1lc3NhZ2UgdW4tYWNrZWQgZm9yIHJlZGVsaXZlcnkuXG4gKlxuICovXG5cbmltcG9ydCB7IGNyZWF0ZUVudlJlZGlzQ2xpZW50IH0gZnJvbSAnLi9lbnYtcmVkaXMuanMnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IGVtaXRDb3VudGVyIH0gZnJvbSAnLi4vdXRpbHMvbWV0cmljLWVtaXR0ZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ2R1cmFibGUtZXZlbnQtYnVzJyk7XG5cbi8qKiBBIGRlbGl2ZXJlZCBldmVudDogaXRzIHN0cmVhbSBpZCwgdG9waWMsIHB1Ymxpc2ggdGltZSwgYW5kIGRlY29kZWQgcGF5bG9hZC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRXZlbnRFbnZlbG9wZTxUID0gdW5rbm93bj4ge1xuICAvKiogUmVkaXMgU3RyZWFtIGVudHJ5IGlkIChgPG1zPi08c2VxPmApIOKAlCBhbHNvIHRoZSBpZGVtcG90ZW5jeSBoYW5kbGUuICovXG4gIGlkOiBzdHJpbmc7XG4gIHRvcGljOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBXaGVuIHRoZSBldmVudCB3YXMgcHVibGlzaGVkICh0aGUgUmVkaXMgc2VydmVyIHRpbWUgZW1iZWRkZWQgaW4gdGhlIHN0cmVhbVxuICAgKiBpZCkuIEEgcmVkZWxpdmVyeSBrZWVwcyBpdHMgT1JJR0lOQUwgcHVibGlzaCB0aW1lLCBzbyBhIGNvbnN1bWVyIGFwcGx5aW5nXG4gICAqIGxhc3Qtd3JpdGVyLXdpbnMgc3RhdGUgKGUuZy4gYW4gZW50aXRsZW1lbnQgc3luYykgY2FuIG9yZGVyIGV2ZW50cyBieSBpdFxuICAgKiBpbnN0ZWFkIG9mIGJ5IGRlbGl2ZXJ5IHRpbWUgYW5kIHJlZnVzZSB0byBsZXQgYSBzdGFsZSByZXRyeSBvdmVyd3JpdGUgYVxuICAgKiBuZXdlciBzdGF0ZS5cbiAgICovXG4gIHB1Ymxpc2hlZEF0OiBEYXRlO1xuICBwYXlsb2FkOiBUO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFN1YnNjcmliZU9wdGlvbnM8VCA9IHVua25vd24+IHtcbiAgLyoqIFRvcGljIChzdHJlYW0pIHRvIGNvbnN1bWUuICovXG4gIHRvcGljOiBzdHJpbmc7XG4gIC8qKiBDb25zdW1lciBncm91cCDigJQgb25lIGRlbGl2ZXJ5IHBlciBncm91cDsgZGlzdGluY3QgZ3JvdXBzIGZhbiBvdXQuICovXG4gIGdyb3VwOiBzdHJpbmc7XG4gIC8qKiBUaGlzIGNvbnN1bWVyJ3MgaWQgd2l0aGluIHRoZSBncm91cCAoZS5nLiBgJHtzZXJ2aWNlfS0ke3BpZH1gKS4gKi9cbiAgY29uc3VtZXI6IHN0cmluZztcbiAgLyoqIEFzeW5jIGhhbmRsZXIuIFJlc29sdmVzIOKHkiB0aGUgbWVzc2FnZSBpcyBhY2tlZDsgdGhyb3dzIOKHkiBsZWZ0IHBlbmRpbmcgZm9yIHJlZGVsaXZlcnkuICovXG4gIGhhbmRsZXI6IChlbnY6IEV2ZW50RW52ZWxvcGU8VD4pID0+IFByb21pc2U8dm9pZD47XG4gIC8qKiBNYXggbWVzc2FnZXMgcGVyIHJlYWQgKGRlZmF1bHQgMTYpLiAqL1xuICBiYXRjaFNpemU/OiBudW1iZXI7XG4gIC8qKiBYUkVBREdST1VQIGJsb2NrIHRpbWUgaW4gbXMgKGRlZmF1bHQgNTAwMCkuICovXG4gIGJsb2NrTXM/OiBudW1iZXI7XG4gIC8qKiBBIHBlbmRpbmcgbWVzc2FnZSBpZGxlIHRoaXMgbG9uZyAobXMpIGlzIHJlY2xhaW1lZCBmcm9tIGEgZGVhZCBjb25zdW1lciAoZGVmYXVsdCA2MDAwMCkuICovXG4gIG1pbklkbGVNcz86IG51bWJlcjtcbiAgLyoqXG4gICAqIERlbGl2ZXJpZXMgYWZ0ZXIgd2hpY2ggYW4gdW4tYWNrZWQgbWVzc2FnZSBpcyBkZWFkLWxldHRlcmVkIHRvXG4gICAqIGBldnQ6PHRvcGljPjpkbHFgIGFuZCBhY2tlZCBpbnN0ZWFkIG9mIGJlaW5nIHJlZGVsaXZlcmVkIGFnYWluIChkZWZhdWx0IDEwKS5cbiAgICovXG4gIG1heERlbGl2ZXJpZXM/OiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRXZlbnRTdWJzY3JpcHRpb24ge1xuICAvKiogU3RvcCB0aGUgY29uc3VtZXIgbG9vcC4gUmVzb2x2ZXMgb25jZSB0aGUgaW4tZmxpZ2h0IHJlYWQgcmV0dXJucy4gKi9cbiAgc3RvcCgpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIER1cmFibGVFdmVudEJ1cyB7XG4gIC8qKiBQdWJsaXNoIGEgcGF5bG9hZCB0byBhIHRvcGljLiBSZXR1cm5zIHRoZSBzdHJlYW0gaWQsIG9yIG51bGwgb24gZmFpbHVyZSAoZHJvcHBlZCwgbmV2ZXIgdGhyb3dzKS4gKi9cbiAgcHVibGlzaDxUPih0b3BpYzogc3RyaW5nLCBwYXlsb2FkOiBUKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbiAgLyoqIFN0YXJ0IGEgY29uc3VtZXItZ3JvdXAgcmVhZGVyIGZvciBhIHRvcGljLiBGaXJlLWFuZC1mb3JnZXQgbG9vcDsgY2FsbCBgc3RvcCgpYCB0byBlbmQgaXQuICovXG4gIHN1YnNjcmliZTxUPihvcHRzOiBTdWJzY3JpYmVPcHRpb25zPFQ+KTogRXZlbnRTdWJzY3JpcHRpb247XG59XG5cbi8qKlxuICogVGhlIHN1YnNldCBvZiBpb3JlZGlzJyBTdHJlYW0gQVBJIHRoZSBidXMgdXNlcy4gS2VwdCBtaW5pbWFsIHNvIGEgdGVzdCBjYW5cbiAqIHN1cHBseSBhbiBpbi1tZW1vcnkgZmFrZSBhbmQgc28gbWVyZWx5IGltcG9ydGluZyB0aGlzIG5ldmVyIGhhcmQtZGVwZW5kcyBvblxuICogaW9yZWRpcyAoY29uc3RydWN0ZWQgdmlhIHRoZSBzaGFyZWQgZW52IGhlbHBlcikuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVkaXNTdHJlYW1DbGllbnQge1xuICB4YWRkKGtleTogc3RyaW5nLCAuLi5hcmdzOiAoc3RyaW5nIHwgbnVtYmVyKVtdKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbiAgeGdyb3VwKC4uLmFyZ3M6IChzdHJpbmcgfCBudW1iZXIpW10pOiBQcm9taXNlPHVua25vd24+O1xuICB4cmVhZGdyb3VwKC4uLmFyZ3M6IChzdHJpbmcgfCBudW1iZXIpW10pOiBQcm9taXNlPHVua25vd24+O1xuICB4YWNrKGtleTogc3RyaW5nLCBncm91cDogc3RyaW5nLCAuLi5pZHM6IHN0cmluZ1tdKTogUHJvbWlzZTxudW1iZXI+O1xuICB4YXV0b2NsYWltKC4uLmFyZ3M6IChzdHJpbmcgfCBudW1iZXIpW10pOiBQcm9taXNlPHVua25vd24+O1xuICB4cGVuZGluZyhrZXk6IHN0cmluZywgZ3JvdXA6IHN0cmluZywgLi4uYXJnczogKHN0cmluZyB8IG51bWJlcilbXSk6IFByb21pc2U8dW5rbm93bj47XG4gIHhyYW5nZShrZXk6IHN0cmluZywgc3RhcnQ6IHN0cmluZywgZW5kOiBzdHJpbmcsIC4uLmFyZ3M6IChzdHJpbmcgfCBudW1iZXIpW10pOiBQcm9taXNlPHVua25vd24+O1xuICAvKiogQSBuZXcgY29ubmVjdGlvbiB3aXRoIHRoZSBzYW1lIG9wdGlvbnMg4oCUIGVhY2ggc3Vic2NyaWJlciByZWFkcyBvbiBpdHMgb3duLiAqL1xuICBkdXBsaWNhdGUoKTogUmVkaXNTdHJlYW1DbGllbnQ7XG4gIHF1aXQ/KCk6IFByb21pc2U8dW5rbm93bj47XG4gIG9uPyhldmVudDogJ2Vycm9yJywgY2I6IChlcnI6IHVua25vd24pID0+IHZvaWQpOiB1bmtub3duO1xufVxuXG5jb25zdCBERUZBVUxUX01BWExFTiA9IDEwXzAwMDtcbmNvbnN0IERFRkFVTFRfQkFUQ0ggPSAxNjtcbmNvbnN0IERFRkFVTFRfQkxPQ0tfTVMgPSA1XzAwMDtcbmNvbnN0IERFRkFVTFRfTUlOX0lETEVfTVMgPSA2MF8wMDA7XG5jb25zdCBERUZBVUxUX01BWF9ERUxJVkVSSUVTID0gMTA7XG5cbmZ1bmN0aW9uIGVyck1zZyhlcnI6IHVua25vd24pOiBzdHJpbmcge1xuICByZXR1cm4gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpO1xufVxuXG5mdW5jdGlvbiBzdHJlYW1LZXkodG9waWM6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgZXZ0OiR7dG9waWN9YDtcbn1cblxuLyoqIERlYWQtbGV0dGVyIHN0cmVhbSBmb3IgYSB0b3BpYy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWFkTGV0dGVyU3RyZWFtS2V5KHRvcGljOiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gYGV2dDoke3RvcGljfTpkbHFgO1xufVxuXG4vKiogUHVibGlzaCB0aW1lIGZyb20gYSBzdHJlYW0gaWQgKGA8bXM+LTxzZXE+YCk7IGVwb2NoIDAgZm9yIGFuIHVucGFyc2VhYmxlIGlkLiAqL1xuZnVuY3Rpb24gcHVibGlzaGVkQXRGcm9tSWQoaWQ6IHN0cmluZyk6IERhdGUge1xuICBjb25zdCBtcyA9IE51bWJlcihpZC5zcGxpdCgnLScpWzBdKTtcbiAgcmV0dXJuIG5ldyBEYXRlKE51bWJlci5pc0Zpbml0ZShtcykgPyBtcyA6IDApO1xufVxuXG4vKiogVHJ1ZSB3aGVuIHRoZSBlcnJvciBpcyBSZWRpcycgXCJncm91cCBhbHJlYWR5IGV4aXN0c1wiIChpZGVtcG90ZW50IGNyZWF0ZSkuICovXG5mdW5jdGlvbiBpc0J1c3lHcm91cChlcnI6IHVua25vd24pOiBib29sZWFuIHtcbiAgcmV0dXJuIGVyck1zZyhlcnIpLmluY2x1ZGVzKCdCVVNZR1JPVVAnKTtcbn1cblxuLyoqXG4gKiBQYXJzZSB0aGUgbmVzdGVkIGFycmF5IHNoYXBlIFhSRUFER1JPVVAgLyBYQVVUT0NMQUlNIHJldHVybiBpbnRvIGZsYXQgZW52ZWxvcGVzLlxuICogWFJFQURHUk9VUDogYFtbc3RyZWFtS2V5LCBbW2lkLCBbZiwgdiwgLi4uXV0sIC4uLl1dXWAuXG4gKiBYQVVUT0NMQUlNOiBgW25leHRDdXJzb3IsIFtbaWQsIFtmLCB2LCAuLi5dXSwgLi4uXSwgZGVsZXRlZElkc11gLlxuICogV2Ugc3RvcmUgdGhlIEpTT04gcGF5bG9hZCB1bmRlciBhIHNpbmdsZSBmaWVsZCBgZGAuXG4gKi9cbmZ1bmN0aW9uIHBhcnNlRW50cmllczxUPih0b3BpYzogc3RyaW5nLCBlbnRyaWVzOiB1bmtub3duKTogRXZlbnRFbnZlbG9wZTxUPltdIHtcbiAgY29uc3Qgb3V0OiBFdmVudEVudmVsb3BlPFQ+W10gPSBbXTtcbiAgaWYgKCFBcnJheS5pc0FycmF5KGVudHJpZXMpKSByZXR1cm4gb3V0O1xuICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkoZW50cnkpIHx8IGVudHJ5Lmxlbmd0aCA8IDIpIGNvbnRpbnVlO1xuICAgIGNvbnN0IGlkID0gU3RyaW5nKGVudHJ5WzBdKTtcbiAgICBjb25zdCBmaWVsZHMgPSBlbnRyeVsxXTtcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkoZmllbGRzKSkgY29udGludWU7XG4gICAgLy8gZmllbGRzIGlzIFtuYW1lLCB2YWx1ZSwgbmFtZSwgdmFsdWUsIC4uLl07IGZpbmQgJ2QnLlxuICAgIGxldCByYXc6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICBmb3IgKGxldCBpID0gMDsgaSArIDEgPCBmaWVsZHMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgIGlmIChTdHJpbmcoZmllbGRzW2ldKSA9PT0gJ2QnKSB7IHJhdyA9IFN0cmluZyhmaWVsZHNbaSArIDFdKTsgYnJlYWs7IH1cbiAgICB9XG4gICAgLy8gQSBtaXNzaW5nIGBkYCBmaWVsZCAoYW4gZW50cnkgbm90IHdyaXR0ZW4gYnkgcHVibGlzaCgpKSBPUiBhIGNvcnJ1cHQgSlNPTlxuICAgIC8vIHBheWxvYWQgYm90aCB5aWVsZCBhbiB1bmRlZmluZWQtcGF5bG9hZCBlbnZlbG9wZSBzbyBgZGVsaXZlcigpYCBBQ0tzIGl0IOKAlFxuICAgIC8vIGEgcG9pc29uIG1lc3NhZ2UgbXVzdCBuZXZlciB3ZWRnZSB0aGUgZ3JvdXAgYnkgc3RheWluZyBwZXJwZXR1YWxseSBwZW5kaW5nLlxuICAgIGNvbnN0IHB1Ymxpc2hlZEF0ID0gcHVibGlzaGVkQXRGcm9tSWQoaWQpO1xuICAgIGlmIChyYXcgPT09IHVuZGVmaW5lZCkge1xuICAgICAgb3V0LnB1c2goeyBpZCwgdG9waWMsIHB1Ymxpc2hlZEF0LCBwYXlsb2FkOiB1bmRlZmluZWQgYXMgdW5rbm93biBhcyBUIH0pO1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBvdXQucHVzaCh7IGlkLCB0b3BpYywgcHVibGlzaGVkQXQsIHBheWxvYWQ6IEpTT04ucGFyc2UocmF3KSBhcyBUIH0pO1xuICAgIH0gY2F0Y2gge1xuICAgICAgb3V0LnB1c2goeyBpZCwgdG9waWMsIHB1Ymxpc2hlZEF0LCBwYXlsb2FkOiB1bmRlZmluZWQgYXMgdW5rbm93biBhcyBUIH0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gb3V0O1xufVxuXG4vKiogQnVpbGQgYSBkdXJhYmxlIGV2ZW50IGJ1cyBvdmVyIGEgUmVkaXMgU3RyZWFtcyBjbGllbnQuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUmVkaXNEdXJhYmxlRXZlbnRCdXMoXG4gIHJlZGlzOiBSZWRpc1N0cmVhbUNsaWVudCxcbiAgb3B0czogeyBtYXhMZW4/OiBudW1iZXIgfSA9IHt9LFxuKTogRHVyYWJsZUV2ZW50QnVzIHtcbiAgY29uc3QgbWF4TGVuID0gTWF0aC5tYXgoMSwgb3B0cy5tYXhMZW4gPz8gREVGQVVMVF9NQVhMRU4pO1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgcHVibGlzaDxUPih0b3BpYzogc3RyaW5nLCBwYXlsb2FkOiBUKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgICBjb25zdCBrZXkgPSBzdHJlYW1LZXkodG9waWMpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gTUFYTEVOIH4gPG4+IGJvdW5kcyB0aGUgc3RyZWFtIGFwcHJveGltYXRlbHkgKGNoZWFwKTsgYCpgID0gc2VydmVyIGlkLlxuICAgICAgICBjb25zdCBpZCA9IGF3YWl0IHJlZGlzLnhhZGQoa2V5LCAnTUFYTEVOJywgJ34nLCBtYXhMZW4sICcqJywgJ2QnLCBKU09OLnN0cmluZ2lmeShwYXlsb2FkKSk7XG4gICAgICAgIGVtaXRDb3VudGVyKCdldmVudF9idXNfcHVibGlzaGVkX3RvdGFsJywgeyB0b3BpYyB9KTtcbiAgICAgICAgcmV0dXJuIGlkO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGVtaXRDb3VudGVyKCdldmVudF9idXNfcHVibGlzaF9mYWlsZWRfdG90YWwnLCB7IHRvcGljIH0pO1xuICAgICAgICBsb2dnZXIud2FybignRXZlbnQgYnVzIHB1Ymxpc2ggZmFpbGVkIChldmVudCBkcm9wcGVkKScsIHsgdG9waWMsIGVycm9yOiBlcnJNc2coZXJyKSB9KTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgfSxcblxuICAgIHN1YnNjcmliZTxUPihzdWJPcHRzOiBTdWJzY3JpYmVPcHRpb25zPFQ+KTogRXZlbnRTdWJzY3JpcHRpb24ge1xuICAgICAgY29uc3QgeyB0b3BpYywgZ3JvdXAsIGNvbnN1bWVyLCBoYW5kbGVyIH0gPSBzdWJPcHRzO1xuICAgICAgY29uc3Qga2V5ID0gc3RyZWFtS2V5KHRvcGljKTtcbiAgICAgIGNvbnN0IGRscUtleSA9IGRlYWRMZXR0ZXJTdHJlYW1LZXkodG9waWMpO1xuICAgICAgY29uc3QgYmF0Y2ggPSBNYXRoLm1heCgxLCBzdWJPcHRzLmJhdGNoU2l6ZSA/PyBERUZBVUxUX0JBVENIKTtcbiAgICAgIGNvbnN0IGJsb2NrTXMgPSBNYXRoLm1heCgwLCBzdWJPcHRzLmJsb2NrTXMgPz8gREVGQVVMVF9CTE9DS19NUyk7XG4gICAgICBjb25zdCBtaW5JZGxlID0gTWF0aC5tYXgoMCwgc3ViT3B0cy5taW5JZGxlTXMgPz8gREVGQVVMVF9NSU5fSURMRV9NUyk7XG4gICAgICBjb25zdCBtYXhEZWxpdmVyaWVzID0gTWF0aC5tYXgoMSwgc3ViT3B0cy5tYXhEZWxpdmVyaWVzID8/IERFRkFVTFRfTUFYX0RFTElWRVJJRVMpO1xuXG4gICAgICAvLyBgWFJFQURHUk9VUCBCTE9DS2AgaG9sZHMgaXRzIGNvbm5lY3Rpb24gZm9yIHVwIHRvIGBibG9ja01zYDsgb24gdGhlXG4gICAgICAvLyBzaGFyZWQgY2xpZW50IGV2ZXJ5IHB1Ymxpc2ggcXVldWVkIGJlaGluZCBpdC4gUmVhZCBvbiBhIGRlZGljYXRlZCBvbmUuXG4gICAgICBjb25zdCByZWFkZXIgPSByZWRpcy5kdXBsaWNhdGUoKTtcbiAgICAgIC8vIEEgZHVwbGljYXRlZCBpb3JlZGlzIGNvbm5lY3Rpb24gZG9lc24ndCBpbmhlcml0IGxpc3RlbmVyczsgd2l0aG91dCBvbmUgYVxuICAgICAgLy8gY29ubmVjdGlvbiBlcnJvciBpcyBhbiB1bmhhbmRsZWQgJ2Vycm9yJyBldmVudCB0aGF0IGNyYXNoZXMgdGhlIHByb2Nlc3MuXG4gICAgICByZWFkZXIub24/LignZXJyb3InLCAoZXJyKSA9PiBsb2dnZXIud2FybignRXZlbnQgYnVzIHJlYWRlciBjb25uZWN0aW9uIGVycm9yJywgeyB0b3BpYywgZ3JvdXAsIGVycm9yOiBlcnJNc2coZXJyKSB9KSk7XG5cbiAgICAgIGxldCBzdG9wcGVkID0gZmFsc2U7XG5cbiAgICAgIGNvbnN0IGRlbGl2ZXIgPSBhc3luYyAoZW52czogRXZlbnRFbnZlbG9wZTxUPltdKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICAgIGZvciAoY29uc3QgZW52IG9mIGVudnMpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKGVudi5wYXlsb2FkICE9PSB1bmRlZmluZWQpIGF3YWl0IGhhbmRsZXIoZW52KTtcbiAgICAgICAgICAgIC8vIEFjayBvbiBzdWNjZXNzIE9SIG9uIGFuIHVuZGVmaW5lZCAoY29ycnVwdCkgcGF5bG9hZCDigJQgYSBwb2lzb25cbiAgICAgICAgICAgIC8vIG1lc3NhZ2UgbXVzdCBub3QgYmxvY2sgdGhlIGdyb3VwIGZvcmV2ZXIuXG4gICAgICAgICAgICBhd2FpdCByZWFkZXIueGFjayhrZXksIGdyb3VwLCBlbnYuaWQpO1xuICAgICAgICAgICAgZW1pdENvdW50ZXIoJ2V2ZW50X2J1c19kZWxpdmVyZWRfdG90YWwnLCB7IHRvcGljLCBncm91cCB9KTtcbiAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIC8vIExlYXZlIGl0IHBlbmRpbmcgKG5vIFhBQ0spIOKAlCBYQVVUT0NMQUlNIHJlZGVsaXZlcnMgaXQgYWZ0ZXIgbWluSWRsZS5cbiAgICAgICAgICAgIGVtaXRDb3VudGVyKCdldmVudF9idXNfaGFuZGxlcl9mYWlsZWRfdG90YWwnLCB7IHRvcGljLCBncm91cCB9KTtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKCdFdmVudCBoYW5kbGVyIGZhaWxlZDsgbGVhdmluZyBtZXNzYWdlIHBlbmRpbmcgZm9yIHJlZGVsaXZlcnknLCB7XG4gICAgICAgICAgICAgIHRvcGljLCBncm91cCwgaWQ6IGVudi5pZCwgZXJyb3I6IGVyck1zZyhlcnIpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICAvKipcbiAgICAgICAqIE1vdmUgZXZlcnkgaWRsZSBwZW5kaW5nIG1lc3NhZ2UgdGhhdCBoYXMgYWxyZWFkeSBiZWVuIGRlbGl2ZXJlZFxuICAgICAgICogYG1heERlbGl2ZXJpZXNgIHRpbWVzIHRvIHRoZSBkZWFkLWxldHRlciBzdHJlYW0gYW5kIGFjayBpdC4gVGhlIGVudHJ5XG4gICAgICAgKiBrZWVwcyBpdHMgcGF5bG9hZCBwbHVzIHdoZXJlIGl0IGNhbWUgZnJvbSwgc28gYW4gb3BlcmF0b3IgY2FuIGluc3BlY3Qgb3JcbiAgICAgICAqIHJlcGxheSBpdC4gSWYgY29weWluZyBmYWlscyB0aGUgbWVzc2FnZSBpcyBsZWZ0IHBlbmRpbmcgKHJldHJpZWQgbmV4dFxuICAgICAgICogcGFzcykg4oCUIGl0IGlzIG5ldmVyIGFja2VkIHdpdGhvdXQgYmVpbmcgcHJlc2VydmVkLlxuICAgICAgICovXG4gICAgICBjb25zdCBkZWFkTGV0dGVyRXhoYXVzdGVkID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBjb25zdCBwZW5kaW5nID0gYXdhaXQgcmVhZGVyLnhwZW5kaW5nKGtleSwgZ3JvdXAsICdJRExFJywgbWluSWRsZSwgJy0nLCAnKycsIGJhdGNoKTtcbiAgICAgICAgaWYgKCFBcnJheS5pc0FycmF5KHBlbmRpbmcpKSByZXR1cm47XG4gICAgICAgIGZvciAoY29uc3QgcCBvZiBwZW5kaW5nKSB7XG4gICAgICAgICAgaWYgKCFBcnJheS5pc0FycmF5KHApIHx8IHAubGVuZ3RoIDwgNCkgY29udGludWU7XG4gICAgICAgICAgY29uc3QgaWQgPSBTdHJpbmcocFswXSk7XG4gICAgICAgICAgY29uc3QgZGVsaXZlcmllcyA9IE51bWJlcihwWzNdKTtcbiAgICAgICAgICBpZiAoIShkZWxpdmVyaWVzID49IG1heERlbGl2ZXJpZXMpKSBjb250aW51ZTtcbiAgICAgICAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgcmVhZGVyLnhyYW5nZShrZXksIGlkLCBpZCk7XG4gICAgICAgICAgY29uc3QgZmllbGRzID0gQXJyYXkuaXNBcnJheShlbnRyaWVzKSAmJiBBcnJheS5pc0FycmF5KGVudHJpZXNbMF0pICYmIEFycmF5LmlzQXJyYXkoZW50cmllc1swXVsxXSkgPyBlbnRyaWVzWzBdWzFdIDogW107XG4gICAgICAgICAgbGV0IHJhdyA9ICcnO1xuICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpICsgMSA8IGZpZWxkcy5sZW5ndGg7IGkgKz0gMikge1xuICAgICAgICAgICAgaWYgKFN0cmluZyhmaWVsZHNbaV0pID09PSAnZCcpIHsgcmF3ID0gU3RyaW5nKGZpZWxkc1tpICsgMV0pOyBicmVhazsgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBhd2FpdCByZWFkZXIueGFkZChcbiAgICAgICAgICAgIGRscUtleSwgJ01BWExFTicsICd+JywgbWF4TGVuLCAnKicsXG4gICAgICAgICAgICAnZCcsIHJhdywgJ3NvdXJjZUlkJywgaWQsICdncm91cCcsIGdyb3VwLCAnZGVsaXZlcmllcycsIGRlbGl2ZXJpZXMsXG4gICAgICAgICAgKTtcbiAgICAgICAgICBhd2FpdCByZWFkZXIueGFjayhrZXksIGdyb3VwLCBpZCk7XG4gICAgICAgICAgZW1pdENvdW50ZXIoJ2V2ZW50X2J1c19kZWFkX2xldHRlcmVkX3RvdGFsJywgeyB0b3BpYywgZ3JvdXAgfSk7XG4gICAgICAgICAgbG9nZ2VyLmVycm9yKCdFdmVudCBleGNlZWRlZCBtYXggZGVsaXZlcmllczsgbW92ZWQgdG8gZGVhZC1sZXR0ZXIgc3RyZWFtJywge1xuICAgICAgICAgICAgdG9waWMsIGdyb3VwLCBpZCwgZGVsaXZlcmllcywgZGVhZExldHRlclN0cmVhbTogZGxxS2V5LFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICAvLyBUaGUgY29uc3VtZXIgZ3JvdXAgbXVzdCBleGlzdCBiZWZvcmUgWFJFQURHUk9VUCBjYW4gc3VjY2VlZC4gQ3JlYXRpbmcgaXRcbiAgICAgIC8vIGlzIHJldHJpZWQgaW5zaWRlIHRoZSBsb29wOiBhdCBzdGFydHVwIHRoZSBjbGllbnQgbWF5IG5vdCBoYXZlIGNvbm5lY3RlZFxuICAgICAgLy8geWV0LCBhbmQgYSBzaW5nbGUgZmFpbGVkIGF0dGVtcHQgdXNlZCB0byBsZWF2ZSB0aGUgY29uc3VtZXIgcmVhZGluZyBhXG4gICAgICAvLyBncm91cCB0aGF0IHdhcyBuZXZlciBjcmVhdGVkIOKAlCBmb3JldmVyLlxuICAgICAgbGV0IGdyb3VwUmVhZHkgPSBmYWxzZTtcbiAgICAgIGNvbnN0IGVuc3VyZUdyb3VwID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIE1LU1RSRUFNIHNvIHRoZSBzdHJlYW0gbmVlZCBub3QgZXhpc3QgeWV0OyAnJCcgPSBvbmx5IG5ldyBtZXNzYWdlcy5cbiAgICAgICAgICBhd2FpdCByZWFkZXIueGdyb3VwKCdDUkVBVEUnLCBrZXksIGdyb3VwLCAnJCcsICdNS1NUUkVBTScpO1xuICAgICAgICAgIGdyb3VwUmVhZHkgPSB0cnVlO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBpZiAoIWlzQnVzeUdyb3VwKGVycikpIHRocm93IGVycjtcbiAgICAgICAgICBncm91cFJlYWR5ID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgY29uc3QgbG9vcCA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgICAgd2hpbGUgKCFzdG9wcGVkKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGlmICghZ3JvdXBSZWFkeSkgYXdhaXQgZW5zdXJlR3JvdXAoKTtcbiAgICAgICAgICAgIC8vIDEpIERlYWQtbGV0dGVyIG1lc3NhZ2VzIHRoYXQgaGF2ZSBleGhhdXN0ZWQgdGhlaXIgZGVsaXZlcmllcywgdGhlblxuICAgICAgICAgICAgLy8gICAgcmVjbGFpbSB0aGUgcmVzdCBzdHJhbmRlZCBieSBhIGZhaWxlZC9jcmFzaGVkIGNvbnN1bWVyLlxuICAgICAgICAgICAgYXdhaXQgZGVhZExldHRlckV4aGF1c3RlZCgpO1xuICAgICAgICAgICAgY29uc3QgY2xhaW1lZCA9IGF3YWl0IHJlYWRlci54YXV0b2NsYWltKGtleSwgZ3JvdXAsIGNvbnN1bWVyLCBtaW5JZGxlLCAnMCcsICdDT1VOVCcsIGJhdGNoKTtcbiAgICAgICAgICAgIC8vIFhBVVRPQ0xBSU0gcmVwbHk6IFtuZXh0Q3Vyc29yLCBlbnRyaWVzLCBkZWxldGVkSWRzXVxuICAgICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkoY2xhaW1lZCkgJiYgY2xhaW1lZC5sZW5ndGggPj0gMikge1xuICAgICAgICAgICAgICBhd2FpdCBkZWxpdmVyKHBhcnNlRW50cmllczxUPih0b3BpYywgY2xhaW1lZFsxXSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHN0b3BwZWQpIGJyZWFrO1xuXG4gICAgICAgICAgICAvLyAyKSBSZWFkIG5ldywgbmV2ZXItZGVsaXZlcmVkIG1lc3NhZ2VzIGZvciB0aGlzIGdyb3VwICgnPicpLlxuICAgICAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgcmVhZGVyLnhyZWFkZ3JvdXAoXG4gICAgICAgICAgICAgICdHUk9VUCcsIGdyb3VwLCBjb25zdW1lciwgJ0NPVU5UJywgYmF0Y2gsICdCTE9DSycsIGJsb2NrTXMsICdTVFJFQU1TJywga2V5LCAnPicsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgLy8gWFJFQURHUk9VUCByZXBseTogW1tzdHJlYW1LZXksIGVudHJpZXNdXSB8IG51bGwgKG9uIGJsb2NrIHRpbWVvdXQpXG4gICAgICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShyZXMpICYmIHJlcy5sZW5ndGggPiAwICYmIEFycmF5LmlzQXJyYXkocmVzWzBdKSAmJiByZXNbMF0ubGVuZ3RoID49IDIpIHtcbiAgICAgICAgICAgICAgYXdhaXQgZGVsaXZlcihwYXJzZUVudHJpZXM8VD4odG9waWMsIHJlc1swXVsxXSkpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgaWYgKHN0b3BwZWQpIGJyZWFrO1xuICAgICAgICAgICAgLy8gVGhlIHN0cmVhbSBvciBncm91cCB3YXMgZGVsZXRlZCBvdXQgZnJvbSB1bmRlciB1cyDigJQgcmVjcmVhdGUgaXQuXG4gICAgICAgICAgICBpZiAoL05PR1JPVVAvaS50ZXN0KGVyck1zZyhlcnIpKSkgZ3JvdXBSZWFkeSA9IGZhbHNlO1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ0V2ZW50IGJ1cyBjb25zdW1lciBsb29wIGVycm9yOyBiYWNraW5nIG9mZicsIHsgdG9waWMsIGdyb3VwLCBlcnJvcjogZXJyTXNnKGVycikgfSk7XG4gICAgICAgICAgICAvLyBCYWNrIG9mZiBzbyBhIHBlcnNpc3RlbnQgUmVkaXMgZXJyb3IgZG9lc24ndCBob3Qtc3Bpbi5cbiAgICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIDEwMDApKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IGxvb3BQcm9taXNlID0gbG9vcCgpO1xuICAgICAgbG9nZ2VyLmluZm8oJ0V2ZW50IGJ1cyBjb25zdW1lciBzdGFydGVkJywgeyB0b3BpYywgZ3JvdXAsIGNvbnN1bWVyLCBtYXhEZWxpdmVyaWVzIH0pO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBhc3luYyBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAgIHN0b3BwZWQgPSB0cnVlO1xuICAgICAgICAgIC8vIEF3YWl0IHRoZSBpbi1mbGlnaHQgcmVhZC9oYW5kbGVyIHNvIGEgY2FsbGVyIChlLmcuIGEgc2VydmljZSBzaHV0dGluZ1xuICAgICAgICAgIC8vIGRvd24gaXRzIERCKSBkb2Vzbid0IHJhY2UgYSBtZXNzYWdlIHN0aWxsIGJlaW5nIHByb2Nlc3NlZC4gQm91bmRlZCBieVxuICAgICAgICAgIC8vIHRoZSBYUkVBREdST1VQIEJMT0NLIHdpbmRvdy4gTmV2ZXIgcmVqZWN0cyDigJQgdGhlIGxvb3Agc3dhbGxvd3MgZXJyb3JzLlxuICAgICAgICAgIGF3YWl0IGxvb3BQcm9taXNlLmNhdGNoKCgpID0+IHVuZGVmaW5lZCk7XG4gICAgICAgICAgYXdhaXQgUHJvbWlzZS5yZXNvbHZlKHJlYWRlci5xdWl0Py4oKSkuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIGR1cmFibGUgZXZlbnQgYnVzIGZyb20gdGhlIGFtYmllbnQgUmVkaXMgZW52IHZpYSB0aGUgc2hhcmVkXG4gKiBgY3JlYXRlRW52UmVkaXNDbGllbnRgLiBSZXR1cm5zIGBudWxsYCB3aGVuIFJlZGlzIGlzIG5vdCBjb25maWd1cmVkIOKAlCB0aGVcbiAqIGNhbGxlciB0aGVuIHJ1bnMgd2l0aG91dCBhIGJ1cyAodG9kYXkncyBIVFRQL3JlY29uY2lsZXIgYmVoYXZpb3IpLCBuZXZlclxuICogY3Jhc2hpbmcuIE9wdCBpbiB3aXRoIGEgc2luZ2xlIGJvb3QgbGluZSB3aGVyZSBhIHNlcnZpY2Ugd2FudHMgZHVyYWJsZSBldmVudHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFbnZSZWRpc0R1cmFibGVFdmVudEJ1cyhvcHRzOiB7IG1heExlbj86IG51bWJlciB9ID0ge30pOiBEdXJhYmxlRXZlbnRCdXMgfCBudWxsIHtcbiAgY29uc3QgaW5zdCA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PFJlZGlzU3RyZWFtQ2xpZW50PignZXZlbnQtYnVzJyk7XG4gIGlmICghaW5zdCkgcmV0dXJuIG51bGw7XG4gIGxvZ2dlci5pbmZvKCdSZWRpcyBkdXJhYmxlIGV2ZW50IGJ1cyBpbml0aWFsaXplZCcpO1xuICByZXR1cm4gY3JlYXRlUmVkaXNEdXJhYmxlRXZlbnRCdXMoaW5zdCwgb3B0cyk7XG59XG4iXX0=
@@ -1,44 +1,123 @@
1
1
  /**
2
- * Parse `REDIS_SENTINELS` (comma-separated `host:port` list) into the ioredis
3
- * `{ host, port }[]` sentinel array. Blank/garbage entries are skipped; a
4
- * missing port defaults to the Sentinel default 26379. Returns an empty array
5
- * when the env is unset/empty (→ non-sentinel mode).
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
- * Construct an ioredis client from the standard `REDIS_URL` or
13
- * `REDIS_HOST`/`REDIS_PORT` env — OR, for HA, from Redis Sentinel via
14
- * `REDIS_SENTINELS` + `REDIS_SENTINEL_MASTER`. The shared construction was
15
- * previously duplicated across the leader-lock, audit-spool, and
16
- * token-revocation env factories.
17
- *
18
- * Connection modes (first that matches wins):
19
- * 1. SENTINEL — `REDIS_SENTINELS` set (comma-separated `host:port`): ioredis
20
- * resolves the current master from the sentinels (`name` = `REDIS_SENTINEL_MASTER`,
21
- * default `mymaster`) and transparently reconnects to the new master after a
22
- * failover. This is how the app gets HA against a single-node Redis loss —
23
- * also the shape a managed ElastiCache (cluster-mode-disabled) speaks.
24
- * 2. URL — `REDIS_URL`.
25
- * 3. HOST/PORT — `REDIS_HOST`(+`REDIS_PORT`).
26
- *
27
- * Semantics every caller relies on (kept identical across the consolidation):
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.
38
- *
39
- * Optional auth: `REDIS_PASSWORD` (data-node AUTH) and `REDIS_SENTINEL_PASSWORD`
40
- * (sentinel AUTH, sentinel mode only) are threaded through when set.
41
- *
42
- * @param label short human name for this client, used in the two warn logs.
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.
72
+ *
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}.
78
+ *
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>;
97
+ /** Default bound for {@link createRedisReadyGate}: short enough for a request path. */
98
+ export declare const REQUEST_PATH_REDIS_READY_TIMEOUT_MS = 2000;
99
+ /**
100
+ * A request-path readiness gate for a lazily built env Redis client.
101
+ *
102
+ * The env client has no offline queue, so a command issued before the first
103
+ * connection completes is rejected outright — the first request after a lazy
104
+ * construction would otherwise hit that rejection (e.g. the first step-up per
105
+ * pod failing, or the first impersonation check reading "unavailable").
106
+ *
107
+ * The returned function resolves once the client is ready, or after
108
+ * `timeoutMs` — it NEVER rejects: the caller then runs its command and keeps its
109
+ * own fail-open / fail-closed semantics. Concurrent callers share one wait. After
110
+ * a wait times out (Redis genuinely down) later calls stop waiting until the
111
+ * client reports `ready` again, so an outage costs one bounded wait instead of
112
+ * adding `timeoutMs` to every request.
113
+ */
114
+ export declare function createRedisReadyGate(client: ReadyAwareRedis, timeoutMs?: number): () => Promise<void>;
115
+ /** The subset of ioredis needed by {@link incrWindow}. */
116
+ export interface RedisEvalClient {
117
+ eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
118
+ }
119
+ /**
120
+ * Increment `key` within a window of `windowMs` and return the new count.
121
+ * Throws when Redis errors — callers choose their own failure policy.
122
+ */
123
+ export declare function incrWindow(redis: RedisEvalClient, key: string, windowMs: number): Promise<number>;