@pipeline-builder/api-server 4.1.3 → 4.1.5

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.
@@ -3,32 +3,46 @@
3
3
  import { createLogger, createEnvRedisClient } from '@pipeline-builder/api-core';
4
4
  const logger = createLogger('sse-relay');
5
5
  /**
6
- * Single channel carrying every relayed frame. `requestId` rides in the message
7
- * body rather than the channel name so each pod SUBSCRIBES exactly once on
8
- * startup — no per-subject subscribe/unsubscribe churn as clients come and go.
6
+ * The relay channel for one service. Every replica of a service shares it, and
7
+ * no other service does: an SSE subject (an org id, a build requestId) only has
8
+ * meaning inside the service whose clients subscribed to it, so a shared channel
9
+ * made every service's pods parse — and re-emit to same-named subjects — every
10
+ * other service's frames. `requestId` rides in the message body rather than the
11
+ * channel name so each pod SUBSCRIBES exactly once on startup — no per-subject
12
+ * subscribe/unsubscribe churn as clients come and go.
9
13
  */
10
- const RELAY_CHANNEL = 'sse:relay';
14
+ export function sseRelayChannel(serviceName) {
15
+ return `sse:relay:${serviceName}`;
16
+ }
17
+ /** Backoff bounds for the startup SUBSCRIBE retry. */
18
+ const SUBSCRIBE_RETRY_MIN_MS = 500;
19
+ const SUBSCRIBE_RETRY_MAX_MS = 30_000;
11
20
  /**
12
21
  * Redis-backed relay. Uses the given client for PUBLISH and a `duplicate()` for
13
22
  * SUBSCRIBE (ioredis forbids mixing subscribe with normal commands on one
14
23
  * connection). Publish is fire-and-forget with a swallowed rejection; a subscribe
15
24
  * handler that throws is isolated so one bad frame can't kill the subscriber.
16
25
  */
17
- export function createRedisSSERelay(publisher) {
26
+ export function createRedisSSERelay(publisher, channel) {
18
27
  const subscriber = publisher.duplicate();
28
+ // A duplicated ioredis connection doesn't inherit the publisher's listeners;
29
+ // without its own, a dropped connection is an unhandled 'error' that crashes Node.
30
+ subscriber.on('error', (err) => {
31
+ logger.warn('SSE relay subscriber connection error', { channel, error: err instanceof Error ? err.message : String(err) });
32
+ });
19
33
  let closed = false;
20
34
  return {
21
35
  publish(msg) {
22
36
  if (closed)
23
37
  return;
24
38
  // Fire-and-forget: never await, never surface a rejection to the caller.
25
- void Promise.resolve(publisher.publish(RELAY_CHANNEL, JSON.stringify(msg))).catch((err) => {
39
+ void Promise.resolve(publisher.publish(channel, JSON.stringify(msg))).catch((err) => {
26
40
  logger.warn('SSE relay publish failed', { error: err instanceof Error ? err.message : String(err) });
27
41
  });
28
42
  },
29
43
  subscribe(handler) {
30
- subscriber.on('message', (channel, message) => {
31
- if (channel !== RELAY_CHANNEL)
44
+ subscriber.on('message', (received, message) => {
45
+ if (received !== channel)
32
46
  return;
33
47
  let parsed;
34
48
  try {
@@ -45,9 +59,22 @@ export function createRedisSSERelay(publisher) {
45
59
  logger.warn('SSE relay handler threw', { error: err instanceof Error ? err.message : String(err) });
46
60
  }
47
61
  });
48
- void Promise.resolve(subscriber.subscribe(RELAY_CHANNEL)).catch((err) => {
49
- logger.warn('SSE relay subscribe failed — falling back to local-only delivery', { error: err instanceof Error ? err.message : String(err) });
50
- });
62
+ // Retry until subscribed: at startup the connection usually isn't up yet,
63
+ // and giving up after one attempt left the pod on local-only delivery for
64
+ // its whole life. Once subscribed, ioredis re-subscribes after reconnects.
65
+ let delayMs = SUBSCRIBE_RETRY_MIN_MS;
66
+ const attempt = () => {
67
+ if (closed)
68
+ return;
69
+ void Promise.resolve(subscriber.subscribe(channel)).then(() => logger.info('SSE relay subscribed', { channel }), (err) => {
70
+ logger.warn('SSE relay subscribe failed; retrying (local-only delivery meanwhile)', {
71
+ retryInMs: delayMs, error: err instanceof Error ? err.message : String(err),
72
+ });
73
+ setTimeout(attempt, delayMs).unref?.();
74
+ delayMs = Math.min(delayMs * 2, SUBSCRIBE_RETRY_MAX_MS);
75
+ });
76
+ };
77
+ attempt();
51
78
  },
52
79
  async close() {
53
80
  closed = true;
@@ -56,15 +83,16 @@ export function createRedisSSERelay(publisher) {
56
83
  };
57
84
  }
58
85
  /**
59
- * Build a Redis-backed SSE relay from the shared env Redis (same wiring as the
60
- * SSE ticket store / rate-limiter / audit-spool). Returns null when Redis isn't
61
- * configured so the caller keeps local-only delivery. Never throws.
86
+ * Build a Redis-backed SSE relay from the shared env Redis, on this service's
87
+ * own channel (`SERVICE_NAME`). Returns null when Redis isn't configured so the
88
+ * caller keeps local-only delivery.
62
89
  */
63
- export function createEnvRedisSSERelay() {
90
+ export function createEnvRedisSSERelay(serviceName = process.env.SERVICE_NAME || 'api') {
64
91
  const client = createEnvRedisClient('sse-relay');
65
92
  if (!client)
66
93
  return null;
67
- logger.info('Redis SSE relay initialized (cross-pod fan-out enabled)');
68
- return createRedisSSERelay(client);
94
+ const channel = sseRelayChannel(serviceName);
95
+ logger.info('Redis SSE relay initialized (cross-pod fan-out enabled)', { channel });
96
+ return createRedisSSERelay(client, channel);
69
97
  }
70
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXJlbGF5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvc3NlLXJlbGF5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBR2hGLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztBQXNEekM7Ozs7R0FJRztBQUNILE1BQU0sYUFBYSxHQUFHLFdBQVcsQ0FBQztBQUVsQzs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxTQUE0QjtJQUM5RCxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsU0FBUyxFQUFFLENBQUM7SUFDekMsSUFBSSxNQUFNLEdBQUcsS0FBSyxDQUFDO0lBRW5CLE9BQU87UUFDTCxPQUFPLENBQUMsR0FBRztZQUNULElBQUksTUFBTTtnQkFBRSxPQUFPO1lBQ25CLHlFQUF5RTtZQUN6RSxLQUFLLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7Z0JBQ3hGLE1BQU0sQ0FBQyxJQUFJLENBQUMsMEJBQTBCLEVBQUUsRUFBRSxLQUFLLEVBQUUsR0FBRyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN2RyxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFDRCxTQUFTLENBQUMsT0FBTztZQUNmLFVBQVUsQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxFQUFFO2dCQUM1QyxJQUFJLE9BQU8sS0FBSyxhQUFhO29CQUFFLE9BQU87Z0JBQ3RDLElBQUksTUFBdUIsQ0FBQztnQkFDNUIsSUFBSSxDQUFDO29CQUNILE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBb0IsQ0FBQztnQkFDbEQsQ0FBQztnQkFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO29CQUNiLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0NBQXNDLEVBQUUsRUFBRSxLQUFLLEVBQUUsR0FBRyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztvQkFDakgsT0FBTztnQkFDVCxDQUFDO2dCQUNELElBQUksQ0FBQztvQkFDSCxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ2xCLENBQUM7Z0JBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztvQkFDYixNQUFNLENBQUMsSUFBSSxDQUFDLHlCQUF5QixFQUFFLEVBQUUsS0FBSyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7Z0JBQ3RHLENBQUM7WUFDSCxDQUFDLENBQUMsQ0FBQztZQUNILEtBQUssT0FBTyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7Z0JBQ3RFLE1BQU0sQ0FBQyxJQUFJLENBQUMsa0VBQWtFLEVBQUUsRUFBRSxLQUFLLEVBQUUsR0FBRyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUMvSSxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFDRCxLQUFLLENBQUMsS0FBSztZQUNULE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDZCxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLEVBQUUsVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNsRSxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQjtJQUNwQyxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBb0IsV0FBVyxDQUFDLENBQUM7SUFDcEUsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLElBQUksQ0FBQztJQUN6QixNQUFNLENBQUMsSUFBSSxDQUFDLHlEQUF5RCxDQUFDLENBQUM7SUFDdkUsT0FBTyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUNyQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFNTRVBheWxvYWQgfSBmcm9tICcuL3NzZS1jb25uZWN0aW9uLW1hbmFnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3NzZS1yZWxheScpO1xuXG4vKipcbiAqIEEgcmVsYXllZCBTU0UgZnJhbWUuIGBvcmlnaW5gIGlzIHRoZSBpZCBvZiB0aGUgU1NFTWFuYWdlciBpbnN0YW5jZSB0aGF0XG4gKiBwdWJsaXNoZWQgaXQsIHNvIHRoZSBwdWJsaXNoaW5nIHBvZCBjYW4gaWdub3JlIGl0cyBvd24gZWNobyAoaXQgYWxyZWFkeSB3cm90ZVxuICogdGhlIGZyYW1lIHRvIGl0cyBsb2NhbCBjbGllbnRzIGJlZm9yZSBwdWJsaXNoaW5nKS4gYGtpbmRgIGRpc3Rpbmd1aXNoZXMgYVxuICogcGVyLXN1YmplY3QgYHNlbmRgIChjYXJyaWVzIGByZXF1ZXN0SWRgKSBmcm9tIGEgYGJyb2FkY2FzdGAgKG5vIHN1YmplY3Qg4oCUXG4gKiBldmVyeSBwb2QgcmUtZW1pdHMgdG8gQUxMIG9mIGl0cyBsb2NhbCBjbGllbnRzKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VSZWxheU1lc3NhZ2Uge1xuICBvcmlnaW46IHN0cmluZztcbiAga2luZDogJ3NlbmQnIHwgJ2Jyb2FkY2FzdCc7XG4gIC8qKiBQcmVzZW50IGZvciBgc2VuZGA7IHRoZSBzdHJlYW0gc3ViamVjdCB0aGUgcGF5bG9hZCB0YXJnZXRzLiAqL1xuICByZXF1ZXN0SWQ/OiBzdHJpbmc7XG4gIHBheWxvYWQ6IFNTRVBheWxvYWQ7XG59XG5cbi8qKlxuICogQ3Jvc3MtcG9kIGZhbi1vdXQgYnVzIGZvciBTU0UgZnJhbWVzLlxuICpcbiAqIFVuZGVyIG11bHRpcGxlIHJlcGxpY2FzIHRoZSBwcm9kdWNlciBvZiBhIGxvZyBmcmFtZSAoZS5nLiBhIHBsdWdpbiBidWlsZFxuICogd29ya2VyIG9uIHBvZCBBKSBhbmQgdGhlIGNvbnN1bWVyIChhIGJyb3dzZXIgRXZlbnRTb3VyY2Ugb24gcG9kIEIpIGFyZSBvblxuICogRElGRkVSRU5UIHBvZHMuIFRoZSBpbi1wcm9jZXNzIGNsaWVudCBtYXAgb25seSByZWFjaGVzIHRoZSBwcm9kdWNlcidzIG93blxuICogcG9kLCBzbyBCJ3Mgc3RyZWFtIHdvdWxkIHNpbGVudGx5IG1pc3MgZnJhbWVzLiBUaGUgcmVsYXkgY2xvc2VzIHRoYXQ6IGV2ZXJ5XG4gKiBwb2QgU1VCU0NSSUJFUyBvbmNlIG9uIHN0YXJ0dXAgYW5kIHJlLWVtaXRzIHJlY2VpdmVkIGZyYW1lcyB0byBpdHMgTE9DQUxcbiAqIGNsaWVudHMsIHdoaWxlIGBzZW5kKClgL2Bicm9hZGNhc3QoKWAgUFVCTElTSCB0byB0aGUgYnVzIChpbiBhZGRpdGlvbiB0b1xuICogd3JpdGluZyBsb2NhbGx5KS4gVGhlIG9yaWdpbiB0YWcgcHJldmVudHMgYSBwb2QgZnJvbSBkb3VibGUtZGVsaXZlcmluZyBpdHMgb3duXG4gKiBmcmFtZSB3aGVuIGl0IHJlY2VpdmVzIGl0cyBvd24gcHVibGlzaCBiYWNrLlxuICpcbiAqIEFsbCBtZXRob2RzIGFyZSBiZXN0LWVmZm9ydCAvIGZhaWwtc2FmZTogYSBidXMgb3V0YWdlIG11c3QgZGVncmFkZSB0b1xuICogbG9jYWwtb25seSBkZWxpdmVyeSAodG9kYXkncyBzaW5nbGUtcmVwbGljYSBiZWhhdmlvciksIG5ldmVyIGNyYXNoIHRoZSBzdHJlYW0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU1NFUmVsYXkge1xuICAvKiogRmlyZS1hbmQtZm9yZ2V0IHB1Ymxpc2ggb2YgYSBmcmFtZSB0byBldmVyeSBzdWJzY3JpYmVkIHBvZC4gTmV2ZXIgdGhyb3dzLiAqL1xuICBwdWJsaXNoKG1zZzogU1NFUmVsYXlNZXNzYWdlKTogdm9pZDtcbiAgLyoqIFN1YnNjcmliZSB0aGlzIHBvZCdzIHJlLWVtaXQgaGFuZGxlci4gQ2FsbGVkIG9uY2Ugb24gbWFuYWdlciBjb25zdHJ1Y3Rpb24uICovXG4gIHN1YnNjcmliZShoYW5kbGVyOiAobXNnOiBTU0VSZWxheU1lc3NhZ2UpID0+IHZvaWQpOiB2b2lkO1xuICAvKiogVGVhciBkb3duIHRoZSB1bmRlcmx5aW5nIGNvbm5lY3Rpb24ocykuICovXG4gIGNsb3NlKCk6IFByb21pc2U8dm9pZD47XG59XG5cbi8qKlxuICogTWluaW1hbCBpb3JlZGlzIHB1Yi9zdWIgc3VyZmFjZSB0aGUgUmVkaXMgcmVsYXkgbmVlZHMuIGBzdWJzY3JpYmVgIHJlcXVpcmVzIGFcbiAqIERFRElDQVRFRCBjb25uZWN0aW9uIChhIHN1YnNjcmliZWQgaW9yZWRpcyBjbGllbnQgY2FuJ3QgcnVuIG90aGVyIGNvbW1hbmRzKSxcbiAqIHNvIHRoZSByZWxheSBgZHVwbGljYXRlKClgcyB0aGUgcHVibGlzaGVyIGZvciB0aGUgc3Vic2NyaWJlciBzaWRlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlZGlzUHViU3ViQ2xpZW50IHtcbiAgcHVibGlzaChjaGFubmVsOiBzdHJpbmcsIG1lc3NhZ2U6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcbiAgc3Vic2NyaWJlKC4uLmNoYW5uZWxzOiBzdHJpbmdbXSk6IFByb21pc2U8dW5rbm93bj47XG4gIG9uKGV2ZW50OiAnbWVzc2FnZScsIGNiOiAoY2hhbm5lbDogc3RyaW5nLCBtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWQpOiB2b2lkO1xuICBkdXBsaWNhdGUoKTogUmVkaXNQdWJTdWJDbGllbnQ7XG4gIHF1aXQoKTogUHJvbWlzZTx1bmtub3duPjtcbn1cblxuLyoqXG4gKiBTaW5nbGUgY2hhbm5lbCBjYXJyeWluZyBldmVyeSByZWxheWVkIGZyYW1lLiBgcmVxdWVzdElkYCByaWRlcyBpbiB0aGUgbWVzc2FnZVxuICogYm9keSByYXRoZXIgdGhhbiB0aGUgY2hhbm5lbCBuYW1lIHNvIGVhY2ggcG9kIFNVQlNDUklCRVMgZXhhY3RseSBvbmNlIG9uXG4gKiBzdGFydHVwIOKAlCBubyBwZXItc3ViamVjdCBzdWJzY3JpYmUvdW5zdWJzY3JpYmUgY2h1cm4gYXMgY2xpZW50cyBjb21lIGFuZCBnby5cbiAqL1xuY29uc3QgUkVMQVlfQ0hBTk5FTCA9ICdzc2U6cmVsYXknO1xuXG4vKipcbiAqIFJlZGlzLWJhY2tlZCByZWxheS4gVXNlcyB0aGUgZ2l2ZW4gY2xpZW50IGZvciBQVUJMSVNIIGFuZCBhIGBkdXBsaWNhdGUoKWAgZm9yXG4gKiBTVUJTQ1JJQkUgKGlvcmVkaXMgZm9yYmlkcyBtaXhpbmcgc3Vic2NyaWJlIHdpdGggbm9ybWFsIGNvbW1hbmRzIG9uIG9uZVxuICogY29ubmVjdGlvbikuIFB1Ymxpc2ggaXMgZmlyZS1hbmQtZm9yZ2V0IHdpdGggYSBzd2FsbG93ZWQgcmVqZWN0aW9uOyBhIHN1YnNjcmliZVxuICogaGFuZGxlciB0aGF0IHRocm93cyBpcyBpc29sYXRlZCBzbyBvbmUgYmFkIGZyYW1lIGNhbid0IGtpbGwgdGhlIHN1YnNjcmliZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWRpc1NTRVJlbGF5KHB1Ymxpc2hlcjogUmVkaXNQdWJTdWJDbGllbnQpOiBTU0VSZWxheSB7XG4gIGNvbnN0IHN1YnNjcmliZXIgPSBwdWJsaXNoZXIuZHVwbGljYXRlKCk7XG4gIGxldCBjbG9zZWQgPSBmYWxzZTtcblxuICByZXR1cm4ge1xuICAgIHB1Ymxpc2gobXNnKSB7XG4gICAgICBpZiAoY2xvc2VkKSByZXR1cm47XG4gICAgICAvLyBGaXJlLWFuZC1mb3JnZXQ6IG5ldmVyIGF3YWl0LCBuZXZlciBzdXJmYWNlIGEgcmVqZWN0aW9uIHRvIHRoZSBjYWxsZXIuXG4gICAgICB2b2lkIFByb21pc2UucmVzb2x2ZShwdWJsaXNoZXIucHVibGlzaChSRUxBWV9DSEFOTkVMLCBKU09OLnN0cmluZ2lmeShtc2cpKSkuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICBsb2dnZXIud2FybignU1NFIHJlbGF5IHB1Ymxpc2ggZmFpbGVkJywgeyBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0pO1xuICAgICAgfSk7XG4gICAgfSxcbiAgICBzdWJzY3JpYmUoaGFuZGxlcikge1xuICAgICAgc3Vic2NyaWJlci5vbignbWVzc2FnZScsIChjaGFubmVsLCBtZXNzYWdlKSA9PiB7XG4gICAgICAgIGlmIChjaGFubmVsICE9PSBSRUxBWV9DSEFOTkVMKSByZXR1cm47XG4gICAgICAgIGxldCBwYXJzZWQ6IFNTRVJlbGF5TWVzc2FnZTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBwYXJzZWQgPSBKU09OLnBhcnNlKG1lc3NhZ2UpIGFzIFNTRVJlbGF5TWVzc2FnZTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oJ1NTRSByZWxheSByZWNlaXZlZCB1bnBhcnNlYWJsZSBmcmFtZScsIHsgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBoYW5kbGVyKHBhcnNlZCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdTU0UgcmVsYXkgaGFuZGxlciB0aHJldycsIHsgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICB2b2lkIFByb21pc2UucmVzb2x2ZShzdWJzY3JpYmVyLnN1YnNjcmliZShSRUxBWV9DSEFOTkVMKSkuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICBsb2dnZXIud2FybignU1NFIHJlbGF5IHN1YnNjcmliZSBmYWlsZWQg4oCUIGZhbGxpbmcgYmFjayB0byBsb2NhbC1vbmx5IGRlbGl2ZXJ5JywgeyBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0pO1xuICAgICAgfSk7XG4gICAgfSxcbiAgICBhc3luYyBjbG9zZSgpIHtcbiAgICAgIGNsb3NlZCA9IHRydWU7XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW3B1Ymxpc2hlci5xdWl0KCksIHN1YnNjcmliZXIucXVpdCgpXSk7XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIFJlZGlzLWJhY2tlZCBTU0UgcmVsYXkgZnJvbSB0aGUgc2hhcmVkIGVudiBSZWRpcyAoc2FtZSB3aXJpbmcgYXMgdGhlXG4gKiBTU0UgdGlja2V0IHN0b3JlIC8gcmF0ZS1saW1pdGVyIC8gYXVkaXQtc3Bvb2wpLiBSZXR1cm5zIG51bGwgd2hlbiBSZWRpcyBpc24ndFxuICogY29uZmlndXJlZCBzbyB0aGUgY2FsbGVyIGtlZXBzIGxvY2FsLW9ubHkgZGVsaXZlcnkuIE5ldmVyIHRocm93cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUVudlJlZGlzU1NFUmVsYXkoKTogU1NFUmVsYXkgfCBudWxsIHtcbiAgY29uc3QgY2xpZW50ID0gY3JlYXRlRW52UmVkaXNDbGllbnQ8UmVkaXNQdWJTdWJDbGllbnQ+KCdzc2UtcmVsYXknKTtcbiAgaWYgKCFjbGllbnQpIHJldHVybiBudWxsO1xuICBsb2dnZXIuaW5mbygnUmVkaXMgU1NFIHJlbGF5IGluaXRpYWxpemVkIChjcm9zcy1wb2QgZmFuLW91dCBlbmFibGVkKScpO1xuICByZXR1cm4gY3JlYXRlUmVkaXNTU0VSZWxheShjbGllbnQpO1xufVxuIl19
98
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXJlbGF5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvc3NlLXJlbGF5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBR2hGLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztBQXVEekM7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsZUFBZSxDQUFDLFdBQW1CO0lBQ2pELE9BQU8sYUFBYSxXQUFXLEVBQUUsQ0FBQztBQUNwQyxDQUFDO0FBRUQsc0RBQXNEO0FBQ3RELE1BQU0sc0JBQXNCLEdBQUcsR0FBRyxDQUFDO0FBQ25DLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDO0FBRXRDOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUFDLFNBQTRCLEVBQUUsT0FBZTtJQUMvRSxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsU0FBUyxFQUFFLENBQUM7SUFDekMsNkVBQTZFO0lBQzdFLG1GQUFtRjtJQUNuRixVQUFVLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFO1FBQzdCLE1BQU0sQ0FBQyxJQUFJLENBQUMsdUNBQXVDLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDN0gsQ0FBQyxDQUFDLENBQUM7SUFDSCxJQUFJLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFFbkIsT0FBTztRQUNMLE9BQU8sQ0FBQyxHQUFHO1lBQ1QsSUFBSSxNQUFNO2dCQUFFLE9BQU87WUFDbkIseUVBQXlFO1lBQ3pFLEtBQUssT0FBTyxDQUFDLE9B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QztRQUNsRSxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQixDQUFDLFdBQVcsR0FBVyxPQUFPLENBQUMsR0FBRyxDQUFDLFlBQVksSUFBSSxLQUFLO0lBQzVGLE1BQU0sTUFBTSxHQUFHLG9CQUFvQixDQUFvQixXQUFXLENBQUMsQ0FBQztJQUNwRSxJQUFJLENBQUMsTUFBTTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ3pCLE1BQU0sT0FBTyxHQUFHLGVBQWUsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUM3QyxNQUFNLENBQUMsSUFBSSxDQUFDLHlEQUF5RCxFQUFFLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNwRixPQUFPLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztBQUM5QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFNTRVBheWxvYWQgfSBmcm9tICcuL3NzZS1jb25uZWN0aW9uLW1hbmFnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3NzZS1yZWxheScpO1xuXG4vKipcbiAqIEEgcmVsYXllZCBTU0UgZnJhbWUuIGBvcmlnaW5gIGlzIHRoZSBpZCBvZiB0aGUgU1NFTWFuYWdlciBpbnN0YW5jZSB0aGF0XG4gKiBwdWJsaXNoZWQgaXQsIHNvIHRoZSBwdWJsaXNoaW5nIHBvZCBjYW4gaWdub3JlIGl0cyBvd24gZWNobyAoaXQgYWxyZWFkeSB3cm90ZVxuICogdGhlIGZyYW1lIHRvIGl0cyBsb2NhbCBjbGllbnRzIGJlZm9yZSBwdWJsaXNoaW5nKS4gYGtpbmRgIGRpc3Rpbmd1aXNoZXMgYVxuICogcGVyLXN1YmplY3QgYHNlbmRgIChjYXJyaWVzIGByZXF1ZXN0SWRgKSBmcm9tIGEgYGJyb2FkY2FzdGAgKG5vIHN1YmplY3Qg4oCUXG4gKiBldmVyeSBwb2QgcmUtZW1pdHMgdG8gQUxMIG9mIGl0cyBsb2NhbCBjbGllbnRzKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VSZWxheU1lc3NhZ2Uge1xuICBvcmlnaW46IHN0cmluZztcbiAga2luZDogJ3NlbmQnIHwgJ2Jyb2FkY2FzdCc7XG4gIC8qKiBQcmVzZW50IGZvciBgc2VuZGA7IHRoZSBzdHJlYW0gc3ViamVjdCB0aGUgcGF5bG9hZCB0YXJnZXRzLiAqL1xuICByZXF1ZXN0SWQ/OiBzdHJpbmc7XG4gIHBheWxvYWQ6IFNTRVBheWxvYWQ7XG59XG5cbi8qKlxuICogQ3Jvc3MtcG9kIGZhbi1vdXQgYnVzIGZvciBTU0UgZnJhbWVzLlxuICpcbiAqIFVuZGVyIG11bHRpcGxlIHJlcGxpY2FzIHRoZSBwcm9kdWNlciBvZiBhIGxvZyBmcmFtZSAoZS5nLiBhIHBsdWdpbiBidWlsZFxuICogd29ya2VyIG9uIHBvZCBBKSBhbmQgdGhlIGNvbnN1bWVyIChhIGJyb3dzZXIgRXZlbnRTb3VyY2Ugb24gcG9kIEIpIGFyZSBvblxuICogRElGRkVSRU5UIHBvZHMuIFRoZSBpbi1wcm9jZXNzIGNsaWVudCBtYXAgb25seSByZWFjaGVzIHRoZSBwcm9kdWNlcidzIG93blxuICogcG9kLCBzbyBCJ3Mgc3RyZWFtIHdvdWxkIHNpbGVudGx5IG1pc3MgZnJhbWVzLiBUaGUgcmVsYXkgY2xvc2VzIHRoYXQ6IGV2ZXJ5XG4gKiBwb2QgU1VCU0NSSUJFUyBvbmNlIG9uIHN0YXJ0dXAgYW5kIHJlLWVtaXRzIHJlY2VpdmVkIGZyYW1lcyB0byBpdHMgTE9DQUxcbiAqIGNsaWVudHMsIHdoaWxlIGBzZW5kKClgL2Bicm9hZGNhc3QoKWAgUFVCTElTSCB0byB0aGUgYnVzIChpbiBhZGRpdGlvbiB0b1xuICogd3JpdGluZyBsb2NhbGx5KS4gVGhlIG9yaWdpbiB0YWcgcHJldmVudHMgYSBwb2QgZnJvbSBkb3VibGUtZGVsaXZlcmluZyBpdHMgb3duXG4gKiBmcmFtZSB3aGVuIGl0IHJlY2VpdmVzIGl0cyBvd24gcHVibGlzaCBiYWNrLlxuICpcbiAqIEFsbCBtZXRob2RzIGFyZSBiZXN0LWVmZm9ydCAvIGZhaWwtc2FmZTogYSBidXMgb3V0YWdlIG11c3QgZGVncmFkZSB0b1xuICogbG9jYWwtb25seSBkZWxpdmVyeSAodG9kYXkncyBzaW5nbGUtcmVwbGljYSBiZWhhdmlvciksIG5ldmVyIGNyYXNoIHRoZSBzdHJlYW0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU1NFUmVsYXkge1xuICAvKiogRmlyZS1hbmQtZm9yZ2V0IHB1Ymxpc2ggb2YgYSBmcmFtZSB0byBldmVyeSBzdWJzY3JpYmVkIHBvZC4gTmV2ZXIgdGhyb3dzLiAqL1xuICBwdWJsaXNoKG1zZzogU1NFUmVsYXlNZXNzYWdlKTogdm9pZDtcbiAgLyoqIFN1YnNjcmliZSB0aGlzIHBvZCdzIHJlLWVtaXQgaGFuZGxlci4gQ2FsbGVkIG9uY2Ugb24gbWFuYWdlciBjb25zdHJ1Y3Rpb24uICovXG4gIHN1YnNjcmliZShoYW5kbGVyOiAobXNnOiBTU0VSZWxheU1lc3NhZ2UpID0+IHZvaWQpOiB2b2lkO1xuICAvKiogVGVhciBkb3duIHRoZSB1bmRlcmx5aW5nIGNvbm5lY3Rpb24ocykuICovXG4gIGNsb3NlKCk6IFByb21pc2U8dm9pZD47XG59XG5cbi8qKlxuICogTWluaW1hbCBpb3JlZGlzIHB1Yi9zdWIgc3VyZmFjZSB0aGUgUmVkaXMgcmVsYXkgbmVlZHMuIGBzdWJzY3JpYmVgIHJlcXVpcmVzIGFcbiAqIERFRElDQVRFRCBjb25uZWN0aW9uIChhIHN1YnNjcmliZWQgaW9yZWRpcyBjbGllbnQgY2FuJ3QgcnVuIG90aGVyIGNvbW1hbmRzKSxcbiAqIHNvIHRoZSByZWxheSBgZHVwbGljYXRlKClgcyB0aGUgcHVibGlzaGVyIGZvciB0aGUgc3Vic2NyaWJlciBzaWRlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlZGlzUHViU3ViQ2xpZW50IHtcbiAgcHVibGlzaChjaGFubmVsOiBzdHJpbmcsIG1lc3NhZ2U6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcbiAgc3Vic2NyaWJlKC4uLmNoYW5uZWxzOiBzdHJpbmdbXSk6IFByb21pc2U8dW5rbm93bj47XG4gIG9uKGV2ZW50OiAnbWVzc2FnZScsIGNiOiAoY2hhbm5lbDogc3RyaW5nLCBtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWQpOiB2b2lkO1xuICBvbihldmVudDogJ2Vycm9yJywgY2I6IChlcnI6IHVua25vd24pID0+IHZvaWQpOiB2b2lkO1xuICBkdXBsaWNhdGUoKTogUmVkaXNQdWJTdWJDbGllbnQ7XG4gIHF1aXQoKTogUHJvbWlzZTx1bmtub3duPjtcbn1cblxuLyoqXG4gKiBUaGUgcmVsYXkgY2hhbm5lbCBmb3Igb25lIHNlcnZpY2UuIEV2ZXJ5IHJlcGxpY2Egb2YgYSBzZXJ2aWNlIHNoYXJlcyBpdCwgYW5kXG4gKiBubyBvdGhlciBzZXJ2aWNlIGRvZXM6IGFuIFNTRSBzdWJqZWN0IChhbiBvcmcgaWQsIGEgYnVpbGQgcmVxdWVzdElkKSBvbmx5IGhhc1xuICogbWVhbmluZyBpbnNpZGUgdGhlIHNlcnZpY2Ugd2hvc2UgY2xpZW50cyBzdWJzY3JpYmVkIHRvIGl0LCBzbyBhIHNoYXJlZCBjaGFubmVsXG4gKiBtYWRlIGV2ZXJ5IHNlcnZpY2UncyBwb2RzIHBhcnNlIOKAlCBhbmQgcmUtZW1pdCB0byBzYW1lLW5hbWVkIHN1YmplY3RzIOKAlCBldmVyeVxuICogb3RoZXIgc2VydmljZSdzIGZyYW1lcy4gYHJlcXVlc3RJZGAgcmlkZXMgaW4gdGhlIG1lc3NhZ2UgYm9keSByYXRoZXIgdGhhbiB0aGVcbiAqIGNoYW5uZWwgbmFtZSBzbyBlYWNoIHBvZCBTVUJTQ1JJQkVTIGV4YWN0bHkgb25jZSBvbiBzdGFydHVwIOKAlCBubyBwZXItc3ViamVjdFxuICogc3Vic2NyaWJlL3Vuc3Vic2NyaWJlIGNodXJuIGFzIGNsaWVudHMgY29tZSBhbmQgZ28uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzc2VSZWxheUNoYW5uZWwoc2VydmljZU5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgc3NlOnJlbGF5OiR7c2VydmljZU5hbWV9YDtcbn1cblxuLyoqIEJhY2tvZmYgYm91bmRzIGZvciB0aGUgc3RhcnR1cCBTVUJTQ1JJQkUgcmV0cnkuICovXG5jb25zdCBTVUJTQ1JJQkVfUkVUUllfTUlOX01TID0gNTAwO1xuY29uc3QgU1VCU0NSSUJFX1JFVFJZX01BWF9NUyA9IDMwXzAwMDtcblxuLyoqXG4gKiBSZWRpcy1iYWNrZWQgcmVsYXkuIFVzZXMgdGhlIGdpdmVuIGNsaWVudCBmb3IgUFVCTElTSCBhbmQgYSBgZHVwbGljYXRlKClgIGZvclxuICogU1VCU0NSSUJFIChpb3JlZGlzIGZvcmJpZHMgbWl4aW5nIHN1YnNjcmliZSB3aXRoIG5vcm1hbCBjb21tYW5kcyBvbiBvbmVcbiAqIGNvbm5lY3Rpb24pLiBQdWJsaXNoIGlzIGZpcmUtYW5kLWZvcmdldCB3aXRoIGEgc3dhbGxvd2VkIHJlamVjdGlvbjsgYSBzdWJzY3JpYmVcbiAqIGhhbmRsZXIgdGhhdCB0aHJvd3MgaXMgaXNvbGF0ZWQgc28gb25lIGJhZCBmcmFtZSBjYW4ndCBraWxsIHRoZSBzdWJzY3JpYmVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlUmVkaXNTU0VSZWxheShwdWJsaXNoZXI6IFJlZGlzUHViU3ViQ2xpZW50LCBjaGFubmVsOiBzdHJpbmcpOiBTU0VSZWxheSB7XG4gIGNvbnN0IHN1YnNjcmliZXIgPSBwdWJsaXNoZXIuZHVwbGljYXRlKCk7XG4gIC8vIEEgZHVwbGljYXRlZCBpb3JlZGlzIGNvbm5lY3Rpb24gZG9lc24ndCBpbmhlcml0IHRoZSBwdWJsaXNoZXIncyBsaXN0ZW5lcnM7XG4gIC8vIHdpdGhvdXQgaXRzIG93biwgYSBkcm9wcGVkIGNvbm5lY3Rpb24gaXMgYW4gdW5oYW5kbGVkICdlcnJvcicgdGhhdCBjcmFzaGVzIE5vZGUuXG4gIHN1YnNjcmliZXIub24oJ2Vycm9yJywgKGVycikgPT4ge1xuICAgIGxvZ2dlci53YXJuKCdTU0UgcmVsYXkgc3Vic2NyaWJlciBjb25uZWN0aW9uIGVycm9yJywgeyBjaGFubmVsLCBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0pO1xuICB9KTtcbiAgbGV0IGNsb3NlZCA9IGZhbHNlO1xuXG4gIHJldHVybiB7XG4gICAgcHVibGlzaChtc2cpIHtcbiAgICAgIGlmIChjbG9zZWQpIHJldHVybjtcbiAgICAgIC8vIEZpcmUtYW5kLWZvcmdldDogbmV2ZXIgYXdhaXQsIG5ldmVyIHN1cmZhY2UgYSByZWplY3Rpb24gdG8gdGhlIGNhbGxlci5cbiAgICAgIHZvaWQgUHJvbWlzZS5yZXNvbHZlKHB1Ymxpc2hlci5wdWJsaXNoKGNoYW5uZWwsIEpTT04uc3RyaW5naWZ5KG1zZykpKS5jYXRjaCgoZXJyKSA9PiB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdTU0UgcmVsYXkgcHVibGlzaCBmYWlsZWQnLCB7IGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfSk7XG4gICAgICB9KTtcbiAgICB9LFxuICAgIHN1YnNjcmliZShoYW5kbGVyKSB7XG4gICAgICBzdWJzY3JpYmVyLm9uKCdtZXNzYWdlJywgKHJlY2VpdmVkLCBtZXNzYWdlKSA9PiB7XG4gICAgICAgIGlmIChyZWNlaXZlZCAhPT0gY2hhbm5lbCkgcmV0dXJuO1xuICAgICAgICBsZXQgcGFyc2VkOiBTU0VSZWxheU1lc3NhZ2U7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcGFyc2VkID0gSlNPTi5wYXJzZShtZXNzYWdlKSBhcyBTU0VSZWxheU1lc3NhZ2U7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdTU0UgcmVsYXkgcmVjZWl2ZWQgdW5wYXJzZWFibGUgZnJhbWUnLCB7IGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfSk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgaGFuZGxlcihwYXJzZWQpO1xuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBsb2dnZXIud2FybignU1NFIHJlbGF5IGhhbmRsZXIgdGhyZXcnLCB7IGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgLy8gUmV0cnkgdW50aWwgc3Vic2NyaWJlZDogYXQgc3RhcnR1cCB0aGUgY29ubmVjdGlvbiB1c3VhbGx5IGlzbid0IHVwIHlldCxcbiAgICAgIC8vIGFuZCBnaXZpbmcgdXAgYWZ0ZXIgb25lIGF0dGVtcHQgbGVmdCB0aGUgcG9kIG9uIGxvY2FsLW9ubHkgZGVsaXZlcnkgZm9yXG4gICAgICAvLyBpdHMgd2hvbGUgbGlmZS4gT25jZSBzdWJzY3JpYmVkLCBpb3JlZGlzIHJlLXN1YnNjcmliZXMgYWZ0ZXIgcmVjb25uZWN0cy5cbiAgICAgIGxldCBkZWxheU1zID0gU1VCU0NSSUJFX1JFVFJZX01JTl9NUztcbiAgICAgIGNvbnN0IGF0dGVtcHQgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGlmIChjbG9zZWQpIHJldHVybjtcbiAgICAgICAgdm9pZCBQcm9taXNlLnJlc29sdmUoc3Vic2NyaWJlci5zdWJzY3JpYmUoY2hhbm5lbCkpLnRoZW4oXG4gICAgICAgICAgKCkgPT4gbG9nZ2VyLmluZm8oJ1NTRSByZWxheSBzdWJzY3JpYmVkJywgeyBjaGFubmVsIH0pLFxuICAgICAgICAgIChlcnIpID0+IHtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKCdTU0UgcmVsYXkgc3Vic2NyaWJlIGZhaWxlZDsgcmV0cnlpbmcgKGxvY2FsLW9ubHkgZGVsaXZlcnkgbWVhbndoaWxlKScsIHtcbiAgICAgICAgICAgICAgcmV0cnlJbk1zOiBkZWxheU1zLCBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KGF0dGVtcHQsIGRlbGF5TXMpLnVucmVmPy4oKTtcbiAgICAgICAgICAgIGRlbGF5TXMgPSBNYXRoLm1pbihkZWxheU1zICogMiwgU1VCU0NSSUJFX1JFVFJZX01BWF9NUyk7XG4gICAgICAgICAgfSxcbiAgICAgICAgKTtcbiAgICAgIH07XG4gICAgICBhdHRlbXB0KCk7XG4gICAgfSxcbiAgICBhc3luYyBjbG9zZSgpIHtcbiAgICAgIGNsb3NlZCA9IHRydWU7XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW3B1Ymxpc2hlci5xdWl0KCksIHN1YnNjcmliZXIucXVpdCgpXSk7XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIFJlZGlzLWJhY2tlZCBTU0UgcmVsYXkgZnJvbSB0aGUgc2hhcmVkIGVudiBSZWRpcywgb24gdGhpcyBzZXJ2aWNlJ3NcbiAqIG93biBjaGFubmVsIChgU0VSVklDRV9OQU1FYCkuIFJldHVybnMgbnVsbCB3aGVuIFJlZGlzIGlzbid0IGNvbmZpZ3VyZWQgc28gdGhlXG4gKiBjYWxsZXIga2VlcHMgbG9jYWwtb25seSBkZWxpdmVyeS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUVudlJlZGlzU1NFUmVsYXkoc2VydmljZU5hbWU6IHN0cmluZyA9IHByb2Nlc3MuZW52LlNFUlZJQ0VfTkFNRSB8fCAnYXBpJyk6IFNTRVJlbGF5IHwgbnVsbCB7XG4gIGNvbnN0IGNsaWVudCA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PFJlZGlzUHViU3ViQ2xpZW50Pignc3NlLXJlbGF5Jyk7XG4gIGlmICghY2xpZW50KSByZXR1cm4gbnVsbDtcbiAgY29uc3QgY2hhbm5lbCA9IHNzZVJlbGF5Q2hhbm5lbChzZXJ2aWNlTmFtZSk7XG4gIGxvZ2dlci5pbmZvKCdSZWRpcyBTU0UgcmVsYXkgaW5pdGlhbGl6ZWQgKGNyb3NzLXBvZCBmYW4tb3V0IGVuYWJsZWQpJywgeyBjaGFubmVsIH0pO1xuICByZXR1cm4gY3JlYXRlUmVkaXNTU0VSZWxheShjbGllbnQsIGNoYW5uZWwpO1xufVxuIl19
@@ -19,8 +19,10 @@ export interface SseTicketChannelOptions {
19
19
  * Register a per-ORG SSE channel: a POST that exchanges the caller's VERIFIED JWT
20
20
  * org for a single-use, org-bound ticket, and a GET that redeems the ticket and
21
21
  * attaches an EventSource keyed by that org. The org IS the stream subject, so a
22
- * producer reaches subscribers with `sseManager.send(orgId, …)` (cross-pod via the
23
- * relay). Shared by the message-notification and reporting execution-status
22
+ * producer reaches subscribers with `sseManager.send(orgId, …)`. Registering a
23
+ * channel turns on the manager's cross-pod relay (this service's own relay
24
+ * channel), so a frame produced on one replica reaches a subscriber on another.
25
+ * Shared by the message-notification and reporting execution-status
24
26
  * channels so the subtle bits — the ticket→addClient→flushHeaders ordering (a 429
25
27
  * MUST precede flushHeaders, which commits the 200) and the capacity semantics —
26
28
  * live in exactly one place.
@@ -1,13 +1,15 @@
1
1
  // Copyright 2026 Pipeline Builder Contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { requireAuth, sendSuccess, sendError, ErrorCode, createLogger } from '@pipeline-builder/api-core';
3
+ import { requireAuth, sendSuccess, sendError, ErrorCode, createLogger, writeSseHeaders } from '@pipeline-builder/api-core';
4
4
  const logger = createLogger('sse-ticket-channel');
5
5
  /**
6
6
  * Register a per-ORG SSE channel: a POST that exchanges the caller's VERIFIED JWT
7
7
  * org for a single-use, org-bound ticket, and a GET that redeems the ticket and
8
8
  * attaches an EventSource keyed by that org. The org IS the stream subject, so a
9
- * producer reaches subscribers with `sseManager.send(orgId, …)` (cross-pod via the
10
- * relay). Shared by the message-notification and reporting execution-status
9
+ * producer reaches subscribers with `sseManager.send(orgId, …)`. Registering a
10
+ * channel turns on the manager's cross-pod relay (this service's own relay
11
+ * channel), so a frame produced on one replica reaches a subscriber on another.
12
+ * Shared by the message-notification and reporting execution-status
11
13
  * channels so the subtle bits — the ticket→addClient→flushHeaders ordering (a 429
12
14
  * MUST precede flushHeaders, which commits the 200) and the capacity semantics —
13
15
  * live in exactly one place.
@@ -17,6 +19,7 @@ const logger = createLogger('sse-ticket-channel');
17
19
  */
18
20
  export function registerSseTicketChannel(app, opts) {
19
21
  const { ticketPath, streamPath, ticketStore, sseManager, label, ticketGuards = [] } = opts;
22
+ sseManager.enableRelay();
20
23
  app.post(ticketPath, requireAuth, ...ticketGuards, async (req, res) => {
21
24
  const orgId = req.user?.organizationId?.toLowerCase();
22
25
  if (!orgId)
@@ -34,19 +37,19 @@ export function registerSseTicketChannel(app, opts) {
34
37
  if (!ticketId)
35
38
  return void sendError(res, 401, 'Missing ticket parameter', ErrorCode.UNAUTHORIZED);
36
39
  const ticket = await ticketStore.consume(ticketId); // atomic single-use
37
- if (!ticket)
40
+ // An org channel's ticket is org-bound only; a subject-bound ticket (e.g. a
41
+ // build-log ticket from a store sharing this namespace) doesn't open it.
42
+ if (!ticket || ticket.subject !== undefined)
38
43
  return void sendError(res, 401, 'Invalid or expired ticket', ErrorCode.UNAUTHORIZED);
39
44
  // Reserve the connection slot BEFORE flushing SSE headers: once flushHeaders
40
45
  // runs the response is committed at 200 and a later 429 is silently dropped.
41
- const added = sseManager.addClient(ticket.orgId, res);
46
+ // The org is the subject, so the per-ORG cap applies (not the per-request one).
47
+ const added = sseManager.addOrgClient(ticket.orgId, res);
42
48
  if (!added)
43
49
  return void sendError(res, 429, `Too many ${label} connections`, ErrorCode.QUOTA_EXCEEDED);
44
- res.setHeader('Content-Type', 'text/event-stream');
45
- res.setHeader('Cache-Control', 'no-cache');
46
- res.setHeader('Connection', 'keep-alive');
47
- res.setHeader('X-Accel-Buffering', 'no');
50
+ writeSseHeaders(res);
48
51
  res.flushHeaders();
49
52
  logger.info('SSE client connected', { channel: label, orgId: ticket.orgId });
50
53
  });
51
54
  }
52
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXRpY2tldC1jaGFubmVsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvc3NlLXRpY2tldC1jaGFubmVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQXVCLE1BQU0sNEJBQTRCLENBQUM7QUFJL0gsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFpQmxEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxHQUFZLEVBQUUsSUFBNkI7SUFDbEYsTUFBTSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsWUFBWSxHQUFHLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQztJQUUzRixHQUFHLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUsR0FBRyxZQUFZLEVBQUUsS0FBSyxFQUFFLEdBQVksRUFBRSxHQUFhLEVBQUUsRUFBRTtRQUN2RixNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLGNBQWMsRUFBRSxXQUFXLEVBQUUsQ0FBQztRQUN0RCxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU8sU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsNEJBQTRCLEVBQUUsU0FBUyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDakcsTUFBTSxNQUFNLEdBQUcsTUFBTSxXQUFXLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzlDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDZixPQUFPLE1BQU0sQ0FBQyxNQUFNLEtBQUssT0FBTztnQkFDOUIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLE9BQU8sS0FBSywyQkFBMkIsRUFBRSxTQUFTLENBQUMsY0FBYyxDQUFDO2dCQUN4RixDQUFDLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsWUFBWSxLQUFLLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUN4RixDQUFDO1FBQ0QsT0FBTyxXQUFXLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUMxRCxDQUFDLENBQUMsQ0FBQztJQUVILEdBQUcsQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLEtBQUssRUFBRSxHQUFZLEVBQUUsR0FBYSxFQUFFLEVBQUU7UUFDeEQsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxNQUE0QixDQUFDO1FBQ3hELElBQUksQ0FBQyxRQUFRO1lBQUUsT0FBTyxLQUFLLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLDBCQUEwQixFQUFFLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUNuRyxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxvQkFBb0I7UUFDeEUsSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLEtBQUssU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsMkJBQTJCLEVBQUUsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2xHLDZFQUE2RTtRQUM3RSw2RUFBNkU7UUFDN0UsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ3RELElBQUksQ0FBQyxLQUFLO1lBQUUsT0FBTyxLQUFLLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLFlBQVksS0FBSyxjQUFjLEVBQUUsU0FBUyxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ3ZHLEdBQUcsQ0FBQyxTQUFTLENBQUMsY0FBYyxFQUFFLG1CQUFtQixDQUFDLENBQUM7UUFDbkQsR0FBRyxDQUFDLFNBQVMsQ0FBQyxlQUFlLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDM0MsR0FBRyxDQUFDLFNBQVMsQ0FBQyxZQUFZLEVBQUUsWUFBWSxDQUFDLENBQUM7UUFDMUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxtQkFBbUIsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN6QyxHQUFHLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDbkIsTUFBTSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQy9FLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyByZXF1aXJlQXV0aCwgc2VuZFN1Y2Nlc3MsIHNlbmRFcnJvciwgRXJyb3JDb2RlLCBjcmVhdGVMb2dnZXIsIHR5cGUgU3NlVGlja2V0U3RvcmUgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IEV4cHJlc3MsIFJlcXVlc3QsIFJlcXVlc3RIYW5kbGVyLCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHR5cGUgeyBTU0VNYW5hZ2VyIH0gZnJvbSAnLi9zc2UtY29ubmVjdGlvbi1tYW5hZ2VyLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdzc2UtdGlja2V0LWNoYW5uZWwnKTtcblxuZXhwb3J0IGludGVyZmFjZSBTc2VUaWNrZXRDaGFubmVsT3B0aW9ucyB7XG4gIC8qKiBQT1NUIHJvdXRlIHRoYXQgZXhjaGFuZ2VzIHRoZSBKV1QgZm9yIGEgc2luZ2xlLXVzZSB0aWNrZXQgKGUuZy4gJy9tZXNzYWdlcy9ub3RpZmljYXRpb25zL3RpY2tldCcpLiAqL1xuICB0aWNrZXRQYXRoOiBzdHJpbmc7XG4gIC8qKiBHRVQgcm91dGUgdGhhdCByZWRlZW1zIHRoZSB0aWNrZXQgYW5kIG9wZW5zIHRoZSBFdmVudFNvdXJjZSAoZS5nLiAnL21lc3NhZ2VzL25vdGlmaWNhdGlvbnMnKS4gKi9cbiAgc3RyZWFtUGF0aDogc3RyaW5nO1xuICAvKiogT3JnLXNjb3BlZCwgc2luZ2xlLXVzZSB0aWNrZXQgc3RvcmUgKFJlZGlzLWJhY2tlZCBmb3IgbXVsdGktcmVwbGljYSkuICovXG4gIHRpY2tldFN0b3JlOiBTc2VUaWNrZXRTdG9yZTtcbiAgLyoqIFNTRSBtYW5hZ2VyIOKAlCB0aGUgb3JnIGlkIGlzIHRoZSBzdHJlYW0gc3ViamVjdCAoYHNzZU1hbmFnZXIuc2VuZChvcmdJZCwg4oCmKWApLiAqL1xuICBzc2VNYW5hZ2VyOiBTU0VNYW5hZ2VyO1xuICAvKiogTm91biB1c2VkIGluIGNhcGFjaXR5L2Nvbm5lY3Rpb24gZXJyb3IgbWVzc2FnZXMgKGUuZy4gJ25vdGlmaWNhdGlvbicsICdleGVjdXRpb24tc3RyZWFtJykuICovXG4gIGxhYmVsOiBzdHJpbmc7XG4gIC8qKiBFeHRyYSBtaWRkbGV3YXJlIGFwcGxpZWQgQUZURVIgcmVxdWlyZUF1dGggb24gdGhlIG1pbnQgcm91dGUgKGUuZy4gcmVxdWlyZVBlcm1pc3Npb24oJ3JlcG9ydHM6cmVhZCcpKS4gKi9cbiAgdGlja2V0R3VhcmRzPzogUmVxdWVzdEhhbmRsZXJbXTtcbn1cblxuLyoqXG4gKiBSZWdpc3RlciBhIHBlci1PUkcgU1NFIGNoYW5uZWw6IGEgUE9TVCB0aGF0IGV4Y2hhbmdlcyB0aGUgY2FsbGVyJ3MgVkVSSUZJRUQgSldUXG4gKiBvcmcgZm9yIGEgc2luZ2xlLXVzZSwgb3JnLWJvdW5kIHRpY2tldCwgYW5kIGEgR0VUIHRoYXQgcmVkZWVtcyB0aGUgdGlja2V0IGFuZFxuICogYXR0YWNoZXMgYW4gRXZlbnRTb3VyY2Uga2V5ZWQgYnkgdGhhdCBvcmcuIFRoZSBvcmcgSVMgdGhlIHN0cmVhbSBzdWJqZWN0LCBzbyBhXG4gKiBwcm9kdWNlciByZWFjaGVzIHN1YnNjcmliZXJzIHdpdGggYHNzZU1hbmFnZXIuc2VuZChvcmdJZCwg4oCmKWAgKGNyb3NzLXBvZCB2aWEgdGhlXG4gKiByZWxheSkuIFNoYXJlZCBieSB0aGUgbWVzc2FnZS1ub3RpZmljYXRpb24gYW5kIHJlcG9ydGluZyBleGVjdXRpb24tc3RhdHVzXG4gKiBjaGFubmVscyBzbyB0aGUgc3VidGxlIGJpdHMg4oCUIHRoZSB0aWNrZXTihpJhZGRDbGllbnTihpJmbHVzaEhlYWRlcnMgb3JkZXJpbmcgKGEgNDI5XG4gKiBNVVNUIHByZWNlZGUgZmx1c2hIZWFkZXJzLCB3aGljaCBjb21taXRzIHRoZSAyMDApIGFuZCB0aGUgY2FwYWNpdHkgc2VtYW50aWNzIOKAlFxuICogbGl2ZSBpbiBleGFjdGx5IG9uZSBwbGFjZS5cbiAqXG4gKiBEaXN0aW5jdCBmcm9tIGFwcC1mYWN0b3J5J3MgYC9sb2dzYCBjaGFubmVsLCB3aGljaCBpcyBTVUJKRUNULWJvdW5kIChwZXIgYnVpbGQsXG4gKiB2aWEgU1NFTWFuYWdlci5jcmVhdGVUaWNrZXQpIHJhdGhlciB0aGFuIG9yZy1ib3VuZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZ2lzdGVyU3NlVGlja2V0Q2hhbm5lbChhcHA6IEV4cHJlc3MsIG9wdHM6IFNzZVRpY2tldENoYW5uZWxPcHRpb25zKTogdm9pZCB7XG4gIGNvbnN0IHsgdGlja2V0UGF0aCwgc3RyZWFtUGF0aCwgdGlja2V0U3RvcmUsIHNzZU1hbmFnZXIsIGxhYmVsLCB0aWNrZXRHdWFyZHMgPSBbXSB9ID0gb3B0cztcblxuICBhcHAucG9zdCh0aWNrZXRQYXRoLCByZXF1aXJlQXV0aCwgLi4udGlja2V0R3VhcmRzLCBhc3luYyAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlKSA9PiB7XG4gICAgY29uc3Qgb3JnSWQgPSByZXEudXNlcj8ub3JnYW5pemF0aW9uSWQ/LnRvTG93ZXJDYXNlKCk7XG4gICAgaWYgKCFvcmdJZCkgcmV0dXJuIHNlbmRFcnJvcihyZXMsIDQwMCwgJ1Rva2VuIG1pc3Npbmcgb3JnYW5pemF0aW9uJywgRXJyb3JDb2RlLlZBTElEQVRJT05fRVJST1IpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRpY2tldFN0b3JlLmlzc3VlKG9yZ0lkKTtcbiAgICBpZiAoIXJlc3VsdC5vaykge1xuICAgICAgcmV0dXJuIHJlc3VsdC5yZWFzb24gPT09ICd0b3RhbCdcbiAgICAgICAgPyBzZW5kRXJyb3IocmVzLCA1MDMsIGBUaGUgJHtsYWJlbH0gc3Vic3lzdGVtIGlzIGF0IGNhcGFjaXR5YCwgRXJyb3JDb2RlLlFVT1RBX0VYQ0VFREVEKVxuICAgICAgICA6IHNlbmRFcnJvcihyZXMsIDQyOSwgYFRvbyBtYW55ICR7bGFiZWx9IHRpY2tldHMgaXNzdWVkYCwgRXJyb3JDb2RlLlFVT1RBX0VYQ0VFREVEKTtcbiAgICB9XG4gICAgcmV0dXJuIHNlbmRTdWNjZXNzKHJlcywgMjAwLCB7IHRpY2tldDogcmVzdWx0LnRpY2tldCB9KTtcbiAgfSk7XG5cbiAgYXBwLmdldChzdHJlYW1QYXRoLCBhc3luYyAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlKSA9PiB7XG4gICAgY29uc3QgdGlja2V0SWQgPSByZXEucXVlcnkudGlja2V0IGFzIHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICBpZiAoIXRpY2tldElkKSByZXR1cm4gdm9pZCBzZW5kRXJyb3IocmVzLCA0MDEsICdNaXNzaW5nIHRpY2tldCBwYXJhbWV0ZXInLCBFcnJvckNvZGUuVU5BVVRIT1JJWkVEKTtcbiAgICBjb25zdCB0aWNrZXQgPSBhd2FpdCB0aWNrZXRTdG9yZS5jb25zdW1lKHRpY2tldElkKTsgLy8gYXRvbWljIHNpbmdsZS11c2VcbiAgICBpZiAoIXRpY2tldCkgcmV0dXJuIHZvaWQgc2VuZEVycm9yKHJlcywgNDAxLCAnSW52YWxpZCBvciBleHBpcmVkIHRpY2tldCcsIEVycm9yQ29kZS5VTkFVVEhPUklaRUQpO1xuICAgIC8vIFJlc2VydmUgdGhlIGNvbm5lY3Rpb24gc2xvdCBCRUZPUkUgZmx1c2hpbmcgU1NFIGhlYWRlcnM6IG9uY2UgZmx1c2hIZWFkZXJzXG4gICAgLy8gcnVucyB0aGUgcmVzcG9uc2UgaXMgY29tbWl0dGVkIGF0IDIwMCBhbmQgYSBsYXRlciA0MjkgaXMgc2lsZW50bHkgZHJvcHBlZC5cbiAgICBjb25zdCBhZGRlZCA9IHNzZU1hbmFnZXIuYWRkQ2xpZW50KHRpY2tldC5vcmdJZCwgcmVzKTtcbiAgICBpZiAoIWFkZGVkKSByZXR1cm4gdm9pZCBzZW5kRXJyb3IocmVzLCA0MjksIGBUb28gbWFueSAke2xhYmVsfSBjb25uZWN0aW9uc2AsIEVycm9yQ29kZS5RVU9UQV9FWENFRURFRCk7XG4gICAgcmVzLnNldEhlYWRlcignQ29udGVudC1UeXBlJywgJ3RleHQvZXZlbnQtc3RyZWFtJyk7XG4gICAgcmVzLnNldEhlYWRlcignQ2FjaGUtQ29udHJvbCcsICduby1jYWNoZScpO1xuICAgIHJlcy5zZXRIZWFkZXIoJ0Nvbm5lY3Rpb24nLCAna2VlcC1hbGl2ZScpO1xuICAgIHJlcy5zZXRIZWFkZXIoJ1gtQWNjZWwtQnVmZmVyaW5nJywgJ25vJyk7XG4gICAgcmVzLmZsdXNoSGVhZGVycygpO1xuICAgIGxvZ2dlci5pbmZvKCdTU0UgY2xpZW50IGNvbm5lY3RlZCcsIHsgY2hhbm5lbDogbGFiZWwsIG9yZ0lkOiB0aWNrZXQub3JnSWQgfSk7XG4gIH0pO1xufVxuIl19
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXRpY2tldC1jaGFubmVsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvc3NlLXRpY2tldC1jaGFubmVsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsZUFBZSxFQUF1QixNQUFNLDRCQUE0QixDQUFDO0FBSWhKLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBaUJsRDs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxHQUFZLEVBQUUsSUFBNkI7SUFDbEYsTUFBTSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsWUFBWSxHQUFHLEVBQUUsRUFBRSxHQUFHLElBQUksQ0FBQztJQUMzRixVQUFVLENBQUMsV0FBVyxFQUFFLENBQUM7SUFFekIsR0FBRyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsV0FBVyxFQUFFLEdBQUcsWUFBWSxFQUFFLEtBQUssRUFBRSxHQUFZLEVBQUUsR0FBYSxFQUFFLEVBQUU7UUFDdkYsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLENBQUM7UUFDdEQsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLDRCQUE0QixFQUFFLFNBQVMsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ2pHLE1BQU0sTUFBTSxHQUFHLE1BQU0sV0FBVyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUM5QyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ2YsT0FBTyxNQUFNLENBQUMsTUFBTSxLQUFLLE9BQU87Z0JBQzlCLENBQUMsQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxPQUFPLEtBQUssMkJBQTJCLEVBQUUsU0FBUyxDQUFDLGNBQWMsQ0FBQztnQkFDeEYsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLFlBQVksS0FBSyxpQkFBaUIsRUFBRSxTQUFTLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDeEYsQ0FBQztRQUNELE9BQU8sV0FBVyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDMUQsQ0FBQyxDQUFDLENBQUM7SUFFSCxHQUFHLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxLQUFLLEVBQUUsR0FBWSxFQUFFLEdBQWEsRUFBRSxFQUFFO1FBQ3hELE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsTUFBNEIsQ0FBQztRQUN4RCxJQUFJLENBQUMsUUFBUTtZQUFFLE9BQU8sS0FBSyxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSwwQkFBMEIsRUFBRSxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDbkcsTUFBTSxNQUFNLEdBQUcsTUFBTSxXQUFXLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsb0JBQW9CO1FBQ3hFLDRFQUE0RTtRQUM1RSx5RUFBeUU7UUFDekUsSUFBSSxDQUFDLE1BQU0sSUFBSSxNQUFNLENBQUMsT0FBTyxLQUFLLFNBQVM7WUFBRSxPQUFPLEtBQUssU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsMkJBQTJCLEVBQUUsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2xJLDZFQUE2RTtRQUM3RSw2RUFBNkU7UUFDN0UsZ0ZBQWdGO1FBQ2hGLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztRQUN6RCxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU8sS0FBSyxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxZQUFZLEtBQUssY0FBYyxFQUFFLFNBQVMsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUN2RyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDckIsR0FBRyxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ25CLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0JBQXNCLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUMvRSxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgcmVxdWlyZUF1dGgsIHNlbmRTdWNjZXNzLCBzZW5kRXJyb3IsIEVycm9yQ29kZSwgY3JlYXRlTG9nZ2VyLCB3cml0ZVNzZUhlYWRlcnMsIHR5cGUgU3NlVGlja2V0U3RvcmUgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IEV4cHJlc3MsIFJlcXVlc3QsIFJlcXVlc3RIYW5kbGVyLCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHR5cGUgeyBTU0VNYW5hZ2VyIH0gZnJvbSAnLi9zc2UtY29ubmVjdGlvbi1tYW5hZ2VyLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdzc2UtdGlja2V0LWNoYW5uZWwnKTtcblxuZXhwb3J0IGludGVyZmFjZSBTc2VUaWNrZXRDaGFubmVsT3B0aW9ucyB7XG4gIC8qKiBQT1NUIHJvdXRlIHRoYXQgZXhjaGFuZ2VzIHRoZSBKV1QgZm9yIGEgc2luZ2xlLXVzZSB0aWNrZXQgKGUuZy4gJy9tZXNzYWdlcy9ub3RpZmljYXRpb25zL3RpY2tldCcpLiAqL1xuICB0aWNrZXRQYXRoOiBzdHJpbmc7XG4gIC8qKiBHRVQgcm91dGUgdGhhdCByZWRlZW1zIHRoZSB0aWNrZXQgYW5kIG9wZW5zIHRoZSBFdmVudFNvdXJjZSAoZS5nLiAnL21lc3NhZ2VzL25vdGlmaWNhdGlvbnMnKS4gKi9cbiAgc3RyZWFtUGF0aDogc3RyaW5nO1xuICAvKiogT3JnLXNjb3BlZCwgc2luZ2xlLXVzZSB0aWNrZXQgc3RvcmUgKFJlZGlzLWJhY2tlZCBmb3IgbXVsdGktcmVwbGljYSkuICovXG4gIHRpY2tldFN0b3JlOiBTc2VUaWNrZXRTdG9yZTtcbiAgLyoqIFNTRSBtYW5hZ2VyIOKAlCB0aGUgb3JnIGlkIGlzIHRoZSBzdHJlYW0gc3ViamVjdCAoYHNzZU1hbmFnZXIuc2VuZChvcmdJZCwg4oCmKWApLiAqL1xuICBzc2VNYW5hZ2VyOiBTU0VNYW5hZ2VyO1xuICAvKiogTm91biB1c2VkIGluIGNhcGFjaXR5L2Nvbm5lY3Rpb24gZXJyb3IgbWVzc2FnZXMgKGUuZy4gJ25vdGlmaWNhdGlvbicsICdleGVjdXRpb24tc3RyZWFtJykuICovXG4gIGxhYmVsOiBzdHJpbmc7XG4gIC8qKiBFeHRyYSBtaWRkbGV3YXJlIGFwcGxpZWQgQUZURVIgcmVxdWlyZUF1dGggb24gdGhlIG1pbnQgcm91dGUgKGUuZy4gcmVxdWlyZVBlcm1pc3Npb24oJ3JlcG9ydHM6cmVhZCcpKS4gKi9cbiAgdGlja2V0R3VhcmRzPzogUmVxdWVzdEhhbmRsZXJbXTtcbn1cblxuLyoqXG4gKiBSZWdpc3RlciBhIHBlci1PUkcgU1NFIGNoYW5uZWw6IGEgUE9TVCB0aGF0IGV4Y2hhbmdlcyB0aGUgY2FsbGVyJ3MgVkVSSUZJRUQgSldUXG4gKiBvcmcgZm9yIGEgc2luZ2xlLXVzZSwgb3JnLWJvdW5kIHRpY2tldCwgYW5kIGEgR0VUIHRoYXQgcmVkZWVtcyB0aGUgdGlja2V0IGFuZFxuICogYXR0YWNoZXMgYW4gRXZlbnRTb3VyY2Uga2V5ZWQgYnkgdGhhdCBvcmcuIFRoZSBvcmcgSVMgdGhlIHN0cmVhbSBzdWJqZWN0LCBzbyBhXG4gKiBwcm9kdWNlciByZWFjaGVzIHN1YnNjcmliZXJzIHdpdGggYHNzZU1hbmFnZXIuc2VuZChvcmdJZCwg4oCmKWAuIFJlZ2lzdGVyaW5nIGFcbiAqIGNoYW5uZWwgdHVybnMgb24gdGhlIG1hbmFnZXIncyBjcm9zcy1wb2QgcmVsYXkgKHRoaXMgc2VydmljZSdzIG93biByZWxheVxuICogY2hhbm5lbCksIHNvIGEgZnJhbWUgcHJvZHVjZWQgb24gb25lIHJlcGxpY2EgcmVhY2hlcyBhIHN1YnNjcmliZXIgb24gYW5vdGhlci5cbiAqIFNoYXJlZCBieSB0aGUgbWVzc2FnZS1ub3RpZmljYXRpb24gYW5kIHJlcG9ydGluZyBleGVjdXRpb24tc3RhdHVzXG4gKiBjaGFubmVscyBzbyB0aGUgc3VidGxlIGJpdHMg4oCUIHRoZSB0aWNrZXTihpJhZGRDbGllbnTihpJmbHVzaEhlYWRlcnMgb3JkZXJpbmcgKGEgNDI5XG4gKiBNVVNUIHByZWNlZGUgZmx1c2hIZWFkZXJzLCB3aGljaCBjb21taXRzIHRoZSAyMDApIGFuZCB0aGUgY2FwYWNpdHkgc2VtYW50aWNzIOKAlFxuICogbGl2ZSBpbiBleGFjdGx5IG9uZSBwbGFjZS5cbiAqXG4gKiBEaXN0aW5jdCBmcm9tIGFwcC1mYWN0b3J5J3MgYC9sb2dzYCBjaGFubmVsLCB3aGljaCBpcyBTVUJKRUNULWJvdW5kIChwZXIgYnVpbGQsXG4gKiB2aWEgU1NFTWFuYWdlci5jcmVhdGVUaWNrZXQpIHJhdGhlciB0aGFuIG9yZy1ib3VuZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZ2lzdGVyU3NlVGlja2V0Q2hhbm5lbChhcHA6IEV4cHJlc3MsIG9wdHM6IFNzZVRpY2tldENoYW5uZWxPcHRpb25zKTogdm9pZCB7XG4gIGNvbnN0IHsgdGlja2V0UGF0aCwgc3RyZWFtUGF0aCwgdGlja2V0U3RvcmUsIHNzZU1hbmFnZXIsIGxhYmVsLCB0aWNrZXRHdWFyZHMgPSBbXSB9ID0gb3B0cztcbiAgc3NlTWFuYWdlci5lbmFibGVSZWxheSgpO1xuXG4gIGFwcC5wb3N0KHRpY2tldFBhdGgsIHJlcXVpcmVBdXRoLCAuLi50aWNrZXRHdWFyZHMsIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgICBjb25zdCBvcmdJZCA9IHJlcS51c2VyPy5vcmdhbml6YXRpb25JZD8udG9Mb3dlckNhc2UoKTtcbiAgICBpZiAoIW9yZ0lkKSByZXR1cm4gc2VuZEVycm9yKHJlcywgNDAwLCAnVG9rZW4gbWlzc2luZyBvcmdhbml6YXRpb24nLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGlja2V0U3RvcmUuaXNzdWUob3JnSWQpO1xuICAgIGlmICghcmVzdWx0Lm9rKSB7XG4gICAgICByZXR1cm4gcmVzdWx0LnJlYXNvbiA9PT0gJ3RvdGFsJ1xuICAgICAgICA/IHNlbmRFcnJvcihyZXMsIDUwMywgYFRoZSAke2xhYmVsfSBzdWJzeXN0ZW0gaXMgYXQgY2FwYWNpdHlgLCBFcnJvckNvZGUuUVVPVEFfRVhDRUVERUQpXG4gICAgICAgIDogc2VuZEVycm9yKHJlcywgNDI5LCBgVG9vIG1hbnkgJHtsYWJlbH0gdGlja2V0cyBpc3N1ZWRgLCBFcnJvckNvZGUuUVVPVEFfRVhDRUVERUQpO1xuICAgIH1cbiAgICByZXR1cm4gc2VuZFN1Y2Nlc3MocmVzLCAyMDAsIHsgdGlja2V0OiByZXN1bHQudGlja2V0IH0pO1xuICB9KTtcblxuICBhcHAuZ2V0KHN0cmVhbVBhdGgsIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgICBjb25zdCB0aWNrZXRJZCA9IHJlcS5xdWVyeS50aWNrZXQgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIGlmICghdGlja2V0SWQpIHJldHVybiB2b2lkIHNlbmRFcnJvcihyZXMsIDQwMSwgJ01pc3NpbmcgdGlja2V0IHBhcmFtZXRlcicsIEVycm9yQ29kZS5VTkFVVEhPUklaRUQpO1xuICAgIGNvbnN0IHRpY2tldCA9IGF3YWl0IHRpY2tldFN0b3JlLmNvbnN1bWUodGlja2V0SWQpOyAvLyBhdG9taWMgc2luZ2xlLXVzZVxuICAgIC8vIEFuIG9yZyBjaGFubmVsJ3MgdGlja2V0IGlzIG9yZy1ib3VuZCBvbmx5OyBhIHN1YmplY3QtYm91bmQgdGlja2V0IChlLmcuIGFcbiAgICAvLyBidWlsZC1sb2cgdGlja2V0IGZyb20gYSBzdG9yZSBzaGFyaW5nIHRoaXMgbmFtZXNwYWNlKSBkb2Vzbid0IG9wZW4gaXQuXG4gICAgaWYgKCF0aWNrZXQgfHwgdGlja2V0LnN1YmplY3QgIT09IHVuZGVmaW5lZCkgcmV0dXJuIHZvaWQgc2VuZEVycm9yKHJlcywgNDAxLCAnSW52YWxpZCBvciBleHBpcmVkIHRpY2tldCcsIEVycm9yQ29kZS5VTkFVVEhPUklaRUQpO1xuICAgIC8vIFJlc2VydmUgdGhlIGNvbm5lY3Rpb24gc2xvdCBCRUZPUkUgZmx1c2hpbmcgU1NFIGhlYWRlcnM6IG9uY2UgZmx1c2hIZWFkZXJzXG4gICAgLy8gcnVucyB0aGUgcmVzcG9uc2UgaXMgY29tbWl0dGVkIGF0IDIwMCBhbmQgYSBsYXRlciA0MjkgaXMgc2lsZW50bHkgZHJvcHBlZC5cbiAgICAvLyBUaGUgb3JnIGlzIHRoZSBzdWJqZWN0LCBzbyB0aGUgcGVyLU9SRyBjYXAgYXBwbGllcyAobm90IHRoZSBwZXItcmVxdWVzdCBvbmUpLlxuICAgIGNvbnN0IGFkZGVkID0gc3NlTWFuYWdlci5hZGRPcmdDbGllbnQodGlja2V0Lm9yZ0lkLCByZXMpO1xuICAgIGlmICghYWRkZWQpIHJldHVybiB2b2lkIHNlbmRFcnJvcihyZXMsIDQyOSwgYFRvbyBtYW55ICR7bGFiZWx9IGNvbm5lY3Rpb25zYCwgRXJyb3JDb2RlLlFVT1RBX0VYQ0VFREVEKTtcbiAgICB3cml0ZVNzZUhlYWRlcnMocmVzKTtcbiAgICByZXMuZmx1c2hIZWFkZXJzKCk7XG4gICAgbG9nZ2VyLmluZm8oJ1NTRSBjbGllbnQgY29ubmVjdGVkJywgeyBjaGFubmVsOiBsYWJlbCwgb3JnSWQ6IHRpY2tldC5vcmdJZCB9KTtcbiAgfSk7XG59XG4iXX0=
package/package.json CHANGED
@@ -23,6 +23,7 @@
23
23
  "eslint-plugin-import": "^2.32.0",
24
24
  "jest": "30.4.2",
25
25
  "jest-junit": "^17",
26
+ "jsonwebtoken": "9.0.3",
26
27
  "ts-jest": "29.4.12",
27
28
  "typescript": "npm:@typescript/typescript6@^6.0.2"
28
29
  },
@@ -38,15 +39,13 @@
38
39
  "express": "5.2.1",
39
40
  "express-rate-limit": "8.6.1",
40
41
  "helmet": "8.3.0",
41
- "ioredis": "6.0.0",
42
- "jsonwebtoken": "9.0.3",
43
42
  "prom-client": "15.1.3",
44
43
  "rate-limit-redis": "6.0.0",
45
44
  "swagger-ui-express": "5.0.1",
46
45
  "uuid": "14.0.1",
47
- "@pipeline-builder/api-core": "4.1.2",
48
- "@pipeline-builder/pipeline-core": "4.1.3",
49
- "@pipeline-builder/pipeline-data": "4.1.3"
46
+ "@pipeline-builder/api-core": "4.1.4",
47
+ "@pipeline-builder/pipeline-data": "4.1.5",
48
+ "@pipeline-builder/pipeline-core": "4.1.5"
50
49
  },
51
50
  "keywords": [
52
51
  "ci-cd",
@@ -102,7 +101,7 @@
102
101
  "access": "public",
103
102
  "registry": "https://registry.npmjs.org/"
104
103
  },
105
- "version": "4.1.3",
104
+ "version": "4.1.5",
106
105
  "bugs": {
107
106
  "url": "https://github.com/mwashburn160/pipeline-builder/issues"
108
107
  },
@@ -1 +0,0 @@
1
- export { requireAuth } from '@pipeline-builder/api-core';
@@ -1,8 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- // Convenience re-export: api-server consumers usually compose middleware
4
- // already imported from this package, so `requireAuth` is exposed here too.
5
- // Other auth middleware (requireFeature, requireSystemAdmin, isSystemAdmin) is
6
- // consumed directly from api-core where it's defined.
7
- export { requireAuth } from '@pipeline-builder/api-core';
8
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvbWlkZGxld2FyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLHlFQUF5RTtBQUN6RSw0RUFBNEU7QUFDNUUsK0VBQStFO0FBQy9FLHNEQUFzRDtBQUN0RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sNEJBQTRCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLy8gQ29udmVuaWVuY2UgcmUtZXhwb3J0OiBhcGktc2VydmVyIGNvbnN1bWVycyB1c3VhbGx5IGNvbXBvc2UgbWlkZGxld2FyZVxuLy8gYWxyZWFkeSBpbXBvcnRlZCBmcm9tIHRoaXMgcGFja2FnZSwgc28gYHJlcXVpcmVBdXRoYCBpcyBleHBvc2VkIGhlcmUgdG9vLlxuLy8gT3RoZXIgYXV0aCBtaWRkbGV3YXJlIChyZXF1aXJlRmVhdHVyZSwgcmVxdWlyZVN5c3RlbUFkbWluLCBpc1N5c3RlbUFkbWluKSBpc1xuLy8gY29uc3VtZWQgZGlyZWN0bHkgZnJvbSBhcGktY29yZSB3aGVyZSBpdCdzIGRlZmluZWQuXG5leHBvcnQgeyByZXF1aXJlQXV0aCB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbiJdfQ==
@@ -1,75 +0,0 @@
1
- /** A minted ticket's bound identity: owning org + normalized stream subject. */
2
- export interface SSEStreamTicketRecord {
3
- orgId: string;
4
- /** Normalized (dashes stripped, lowercased) stream subject id. */
5
- requestId: string;
6
- }
7
- /**
8
- * Pluggable backend for SSE log-stream tickets AND stream-ownership.
9
- *
10
- * NOTE: distinct from api-core's `SseTicketStore` (services/sse-ticket-store.ts),
11
- * which is the message-service ticket store (issue + abuse counters). This one
12
- * (api-server) additionally tracks stream ownership. The names deliberately differ
13
- * (`SSEStreamTicketStore` here vs `SseTicketStore` there) so consumers don't pick
14
- * the wrong one.
15
- *
16
- * Two concerns share one store because both must be consistent across pods:
17
- * - **Tickets** — short-lived, single-use, bound to (org, subject). The default
18
- * in-memory store is correct only single-replica; the Redis store makes the
19
- * mint (one pod) / redeem (another pod) exchange work behind a load balancer.
20
- * - **Stream ownership** — which org OWNS a given stream subject. The stream
21
- * producer (platform, a different service) records this so ticket minting can
22
- * assert the caller's org owns the subject, closing the cross-tenant attach
23
- * gap where any org could mint a ticket for a guessed requestId.
24
- *
25
- * All methods are async (Redis is async) and best-effort on the failure side per
26
- * their doc — a store outage must degrade gracefully, never crash the stream.
27
- */
28
- export interface SSEStreamTicketStore {
29
- /** Persist a single-use ticket bound to `record`, expiring after `ttlMs`. */
30
- put(ticketId: string, record: SSEStreamTicketRecord, ttlMs: number): Promise<void>;
31
- /** Atomically fetch-and-delete a ticket (single-use). Null if absent/expired. */
32
- consume(ticketId: string): Promise<SSEStreamTicketRecord | null>;
33
- /** Count of live (unexpired) tickets held for an org (per-org cap). */
34
- countForOrg(orgId: string): Promise<number>;
35
- /** Count of all live tickets across the store (capacity cap). */
36
- total(): Promise<number>;
37
- /** Record the org that OWNS a stream subject (producer-side), TTL `ttlMs`. */
38
- bindStreamOwner(requestId: string, orgId: string, ttlMs: number): Promise<void>;
39
- /** The owning org of a stream subject, or null when none is bound. */
40
- getStreamOwner(requestId: string): Promise<string | null>;
41
- /** Optional hygiene sweep of expired entries (Redis TTL makes this a no-op). */
42
- sweep?(): Promise<void>;
43
- }
44
- /**
45
- * In-memory ticket store — the default. Correct for a single replica only;
46
- * mirrors the behavior the SSEManager previously implemented inline.
47
- */
48
- export declare function createMemoryTicketStore(): SSEStreamTicketStore;
49
- /**
50
- * Minimal ioredis surface the Redis ticket store needs. `set` takes the variadic
51
- * option tail (`EX <s>`); `zadd`/`zremrangebyscore`/`zcard` back the cap counters.
52
- */
53
- export interface RedisTicketClient {
54
- get(key: string): Promise<string | null>;
55
- set(key: string, value: string, ...args: (string | number)[]): Promise<string | null>;
56
- del(...keys: string[]): Promise<number>;
57
- zadd(key: string, score: number, member: string): Promise<number>;
58
- zrem(key: string, ...members: string[]): Promise<number>;
59
- zcard(key: string): Promise<number>;
60
- zremrangebyscore(key: string, min: number | string, max: number | string): Promise<number>;
61
- expire(key: string, seconds: number): Promise<number>;
62
- }
63
- /**
64
- * Redis-backed ticket store — required for multi-pod correctness. Tickets are
65
- * per-key with native TTL; cap counters are ZSETs scored by expiry (pruned on
66
- * read). Stream ownership is a per-subject key with its own TTL. All reads are
67
- * fail-safe (null / 0) so a Redis blip never wedges the stream.
68
- */
69
- export declare function createRedisTicketStore(redis: RedisTicketClient): SSEStreamTicketStore;
70
- /**
71
- * Build a Redis-backed ticket store from the shared env Redis (same wiring as
72
- * the rate-limiter / audit-spool). Returns null when Redis isn't configured so
73
- * the caller keeps the in-memory default. Never throws.
74
- */
75
- export declare function createEnvRedisTicketStore(): SSEStreamTicketStore | null;
@@ -1,160 +0,0 @@
1
- // Copyright 2026 Pipeline Builder Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createLogger, createEnvRedisClient } from '@pipeline-builder/api-core';
4
- const logger = createLogger('sse-ticket-store');
5
- /**
6
- * In-memory ticket store — the default. Correct for a single replica only;
7
- * mirrors the behavior the SSEManager previously implemented inline.
8
- */
9
- export function createMemoryTicketStore() {
10
- const tickets = new Map();
11
- const owners = new Map();
12
- return {
13
- async put(ticketId, record, ttlMs) {
14
- tickets.set(ticketId, { ...record, expiresAt: Date.now() + ttlMs });
15
- },
16
- async consume(ticketId) {
17
- const t = tickets.get(ticketId);
18
- tickets.delete(ticketId); // single-use — remove whether or not valid
19
- if (!t || Date.now() > t.expiresAt)
20
- return null;
21
- return { orgId: t.orgId, requestId: t.requestId };
22
- },
23
- async countForOrg(orgId) {
24
- const now = Date.now();
25
- let n = 0;
26
- for (const t of tickets.values())
27
- if (t.orgId === orgId && t.expiresAt > now)
28
- n++;
29
- return n;
30
- },
31
- async total() {
32
- const now = Date.now();
33
- let n = 0;
34
- for (const t of tickets.values())
35
- if (t.expiresAt > now)
36
- n++;
37
- return n;
38
- },
39
- async bindStreamOwner(requestId, orgId, ttlMs) {
40
- owners.set(requestId, { orgId, expiresAt: Date.now() + ttlMs });
41
- },
42
- async getStreamOwner(requestId) {
43
- const o = owners.get(requestId);
44
- if (!o)
45
- return null;
46
- if (Date.now() > o.expiresAt) {
47
- owners.delete(requestId);
48
- return null;
49
- }
50
- return o.orgId;
51
- },
52
- async sweep() {
53
- const now = Date.now();
54
- for (const [id, t] of tickets)
55
- if (now > t.expiresAt)
56
- tickets.delete(id);
57
- for (const [id, o] of owners)
58
- if (now > o.expiresAt)
59
- owners.delete(id);
60
- },
61
- };
62
- }
63
- const TICKET_PREFIX = 'sse:tkt:';
64
- const ORG_ZSET_PREFIX = 'sse:tkt:org:';
65
- const ALL_ZSET = 'sse:tkt:all';
66
- const OWNER_PREFIX = 'sse:owner:';
67
- /**
68
- * Redis-backed ticket store — required for multi-pod correctness. Tickets are
69
- * per-key with native TTL; cap counters are ZSETs scored by expiry (pruned on
70
- * read). Stream ownership is a per-subject key with its own TTL. All reads are
71
- * fail-safe (null / 0) so a Redis blip never wedges the stream.
72
- */
73
- export function createRedisTicketStore(redis) {
74
- const tkey = (id) => `${TICKET_PREFIX}${id}`;
75
- const orgKey = (orgId) => `${ORG_ZSET_PREFIX}${orgId}`;
76
- const ownerKey = (requestId) => `${OWNER_PREFIX}${requestId}`;
77
- const ttlSec = (ttlMs) => Math.max(1, Math.ceil(ttlMs / 1000));
78
- return {
79
- async put(ticketId, record, ttlMs) {
80
- const expiresAt = Date.now() + ttlMs;
81
- const sec = ttlSec(ttlMs);
82
- try {
83
- await redis.set(tkey(ticketId), JSON.stringify(record), 'EX', sec);
84
- // Track for the caps. Score = expiry so pruning is a range delete.
85
- await redis.zadd(orgKey(record.orgId), expiresAt, ticketId);
86
- await redis.zadd(ALL_ZSET, expiresAt, ticketId);
87
- // Bound the per-org ZSET's own lifetime so an idle org's key can't leak.
88
- await redis.expire(orgKey(record.orgId), sec);
89
- }
90
- catch (err) {
91
- logger.warn('SSE ticket put failed', { error: err instanceof Error ? err.message : String(err) });
92
- }
93
- },
94
- async consume(ticketId) {
95
- try {
96
- const raw = await redis.get(tkey(ticketId));
97
- // Single-use: delete regardless. A concurrent double-redeem: at most one
98
- // saw a non-null value before the delete; the ZREM cleans the counters.
99
- await redis.del(tkey(ticketId));
100
- if (!raw)
101
- return null;
102
- const rec = JSON.parse(raw);
103
- await redis.zrem(orgKey(rec.orgId), ticketId);
104
- await redis.zrem(ALL_ZSET, ticketId);
105
- return rec;
106
- }
107
- catch (err) {
108
- logger.warn('SSE ticket consume failed', { error: err instanceof Error ? err.message : String(err) });
109
- return null;
110
- }
111
- },
112
- async countForOrg(orgId) {
113
- try {
114
- await redis.zremrangebyscore(orgKey(orgId), 0, Date.now());
115
- return await redis.zcard(orgKey(orgId));
116
- }
117
- catch {
118
- return 0;
119
- }
120
- },
121
- async total() {
122
- try {
123
- await redis.zremrangebyscore(ALL_ZSET, 0, Date.now());
124
- return await redis.zcard(ALL_ZSET);
125
- }
126
- catch {
127
- return 0;
128
- }
129
- },
130
- async bindStreamOwner(requestId, orgId, ttlMs) {
131
- try {
132
- await redis.set(ownerKey(requestId), orgId, 'EX', ttlSec(ttlMs));
133
- }
134
- catch (err) {
135
- logger.warn('SSE stream-owner bind failed', { error: err instanceof Error ? err.message : String(err) });
136
- }
137
- },
138
- async getStreamOwner(requestId) {
139
- try {
140
- return await redis.get(ownerKey(requestId));
141
- }
142
- catch {
143
- return null;
144
- }
145
- },
146
- };
147
- }
148
- /**
149
- * Build a Redis-backed ticket store from the shared env Redis (same wiring as
150
- * the rate-limiter / audit-spool). Returns null when Redis isn't configured so
151
- * the caller keeps the in-memory default. Never throws.
152
- */
153
- export function createEnvRedisTicketStore() {
154
- const client = createEnvRedisClient('sse-ticket');
155
- if (!client)
156
- return null;
157
- logger.info('Redis SSE ticket store initialized');
158
- return createRedisTicketStore(client);
159
- }
160
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXRpY2tldC1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9odHRwL3NzZS10aWNrZXQtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFaEYsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUF3RGhEOzs7R0FHRztBQUNILE1BQU0sVUFBVSx1QkFBdUI7SUFDckMsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQXdCLENBQUM7SUFDaEQsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFFOUMsT0FBTztRQUNMLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxLQUFLO1lBQy9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLEVBQUUsR0FBRyxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7UUFDRCxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVE7WUFDcEIsTUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNoQyxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsMkNBQTJDO1lBQ3JFLElBQUksQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQyxTQUFTO2dCQUFFLE9BQU8sSUFBSSxDQUFDO1lBQ2hELE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ3BELENBQUM7UUFDRCxLQUFLLENBQUMsV0FBVyxDQUFDLEtBQUs7WUFDckIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNWLEtBQUssTUFBTSxDQUFDLElBQUksT0FBTyxDQUFDLE1BQU0sRUFBRTtnQkFBRSxJQUFJLENBQUMsQ0FBQyxLQUFLLEtBQUssS0FBSyxJQUFJLENBQUMsQ0FBQyxTQUFTLEdBQUcsR0FBRztvQkFBRSxDQUFDLEVBQUUsQ0FBQztZQUNsRixPQUFPLENBQUMsQ0FBQztRQUNYLENBQUM7UUFDRCxLQUFLLENBQUMsS0FBSztZQUNULE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDVixLQUFLLE1BQU0sQ0FBQyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEVBQUU7Z0JBQUUsSUFBSSxDQUFDLENBQUMsU0FBUyxHQUFHLEdBQUc7b0JBQUUsQ0FBQyxFQUFFLENBQUM7WUFDN0QsT0FBTyxDQUFDLENBQUM7UUFDWCxDQUFDO1FBQ0QsS0FBSyxDQUFDLGVBQWUsQ0FBQyxTQUFTLEVBQUUsS0FBSyxFQUFFLEtBQUs7WUFDM0MsTUFBTSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFDRCxLQUFLLENBQUMsY0FBYyxDQUFDLFNBQVM7WUFDNUIsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUNoQyxJQUFJLENBQUMsQ0FBQztnQkFBRSxPQUFPLElBQUksQ0FBQztZQUNwQixJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFBQyxPQUFPLElBQUksQ0FBQztZQUFDLENBQUM7WUFDeEUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDO1FBQ2pCLENBQUM7UUFDRCxLQUFLLENBQUMsS0FBSztZQUNULE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUN2QixLQUFLLE1BQU0sQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLElBQUksT0FBTztnQkFBRSxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUMsU0FBUztvQkFBRSxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3pFLEtBQUssTUFBTSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxNQUFNO2dCQUFFLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQyxTQUFTO29CQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDekUsQ0FBQztLQUNGLENBQUM7QUFDSixDQUFDO0FBaUJELE1BQU0sYUFBYSxHQUFHLFVBQVUsQ0FBQztBQUNqQyxNQUFNLGVBQWUsR0FBRyxjQUFjLENBQUM7QUFDdkMsTUFBTSxRQUFRLEdBQUcsYUFBYSxDQUFDO0FBQy9CLE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBQztBQUVsQzs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxLQUF3QjtJQUM3RCxNQUFNLElBQUksR0FBRyxDQUFDLEVBQVUsRUFBVSxFQUFFLENBQUMsR0FBRyxhQUFhLEdBQUcsRUFBRSxFQUFFLENBQUM7SUFDN0QsTUFBTSxNQUFNLEdBQUcsQ0FBQyxLQUFhLEVBQVUsRUFBRSxDQUFDLEdBQUcsZUFBZSxHQUFHLEtBQUssRUFBRSxDQUFDO0lBQ3ZFLE1BQU0sUUFBUSxHQUFHLENBQUMsU0FBaUIsRUFBVSxFQUFFLENBQUMsR0FBRyxZQUFZLEdBQUcsU0FBUyxFQUFFLENBQUM7SUFDOUUsTUFBTSxNQUFNLEdBQUcsQ0FBQyxLQUFhLEVBQVUsRUFBRSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUM7SUFFL0UsT0FBTztRQUNMLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxLQUFLO1lBQy9CLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLENBQUM7WUFDckMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQzFCLElBQUksQ0FBQztnQkFDSCxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO2dCQUNuRSxtRUFBbUU7Z0JBQ25FLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLFNBQVMsRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDNUQsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQ2hELHlFQUF5RTtnQkFDekUsTUFBTSxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDaEQsQ0FBQztZQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7Z0JBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3BHLENBQUM7UUFDSCxDQUFDO1FBQ0QsS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRO1lBQ3BCLElBQUksQ0FBQztnQkFDSCxNQUFNLEdBQUcsR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7Z0JBQzVDLHlFQUF5RTtnQkFDekUsd0VBQXdFO2dCQUN4RSxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7Z0JBQ2hDLElBQUksQ0FBQyxHQUFHO29CQUFFLE9BQU8sSUFBSSxDQUFDO2dCQUN0QixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBMEIsQ0FBQztnQkFDckQsTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQzlDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQ3JDLE9BQU8sR0FBRyxDQUFDO1lBQ2IsQ0FBQztZQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7Z0JBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQywyQkFBMkIsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN0RyxPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7UUFDSCxDQUFDO1FBQ0QsS0FBSyxDQUFDLFdBQVcsQ0FBQyxLQUFLO1lBQ3JCLElBQUksQ0FBQztnQkFDSCxNQUFNLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO2dCQUMzRCxPQUFPLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztZQUMxQyxDQUFDO1lBQUMsTUFBTSxDQUFDO2dCQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQUMsQ0FBQztRQUN2QixDQUFDO1FBQ0QsS0FBSyxDQUFDLEtBQUs7WUFDVCxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztnQkFDdEQsT0FBTyxNQUFNLEtBQUssQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDckMsQ0FBQztZQUFDLE1BQU0sQ0FBQztnQkFBQyxPQUFPLENBQUMsQ0FBQztZQUFDLENBQUM7UUFDdkIsQ0FBQztRQUNELEtBQUssQ0FBQyxlQUFlLENBQUMsU0FBUyxFQUFFLEtBQUssRUFBRSxLQUFLO1lBQzNDLElBQUksQ0FBQztnQkFDSCxNQUFNLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDbkUsQ0FBQztZQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7Z0JBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQyw4QkFBOEIsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzNHLENBQUM7UUFDSCxDQUFDO1FBQ0QsS0FBSyxDQUFDLGNBQWMsQ0FBQyxTQUFTO1lBQzVCLElBQUksQ0FBQztnQkFDSCxPQUFPLE1BQU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztZQUM5QyxDQUFDO1lBQUMsTUFBTSxDQUFDO2dCQUFDLE9BQU8sSUFBSSxDQUFDO1lBQUMsQ0FBQztRQUMxQixDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHlCQUF5QjtJQUN2QyxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBb0IsWUFBWSxDQUFDLENBQUM7SUFDckUsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLElBQUksQ0FBQztJQUN6QixNQUFNLENBQUMsSUFBSSxDQUFDLG9DQUFvQyxDQUFDLENBQUM7SUFDbEQsT0FBTyxzQkFBc0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUN4QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignc3NlLXRpY2tldC1zdG9yZScpO1xuXG4vKiogQSBtaW50ZWQgdGlja2V0J3MgYm91bmQgaWRlbnRpdHk6IG93bmluZyBvcmcgKyBub3JtYWxpemVkIHN0cmVhbSBzdWJqZWN0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VTdHJlYW1UaWNrZXRSZWNvcmQge1xuICBvcmdJZDogc3RyaW5nO1xuICAvKiogTm9ybWFsaXplZCAoZGFzaGVzIHN0cmlwcGVkLCBsb3dlcmNhc2VkKSBzdHJlYW0gc3ViamVjdCBpZC4gKi9cbiAgcmVxdWVzdElkOiBzdHJpbmc7XG59XG5cbi8qKlxuICogUGx1Z2dhYmxlIGJhY2tlbmQgZm9yIFNTRSBsb2ctc3RyZWFtIHRpY2tldHMgQU5EIHN0cmVhbS1vd25lcnNoaXAuXG4gKlxuICogTk9URTogZGlzdGluY3QgZnJvbSBhcGktY29yZSdzIGBTc2VUaWNrZXRTdG9yZWAgKHNlcnZpY2VzL3NzZS10aWNrZXQtc3RvcmUudHMpLFxuICogd2hpY2ggaXMgdGhlIG1lc3NhZ2Utc2VydmljZSB0aWNrZXQgc3RvcmUgKGlzc3VlICsgYWJ1c2UgY291bnRlcnMpLiBUaGlzIG9uZVxuICogKGFwaS1zZXJ2ZXIpIGFkZGl0aW9uYWxseSB0cmFja3Mgc3RyZWFtIG93bmVyc2hpcC4gVGhlIG5hbWVzIGRlbGliZXJhdGVseSBkaWZmZXJcbiAqIChgU1NFU3RyZWFtVGlja2V0U3RvcmVgIGhlcmUgdnMgYFNzZVRpY2tldFN0b3JlYCB0aGVyZSkgc28gY29uc3VtZXJzIGRvbid0IHBpY2tcbiAqIHRoZSB3cm9uZyBvbmUuXG4gKlxuICogVHdvIGNvbmNlcm5zIHNoYXJlIG9uZSBzdG9yZSBiZWNhdXNlIGJvdGggbXVzdCBiZSBjb25zaXN0ZW50IGFjcm9zcyBwb2RzOlxuICogLSAqKlRpY2tldHMqKiDigJQgc2hvcnQtbGl2ZWQsIHNpbmdsZS11c2UsIGJvdW5kIHRvIChvcmcsIHN1YmplY3QpLiBUaGUgZGVmYXVsdFxuICogICBpbi1tZW1vcnkgc3RvcmUgaXMgY29ycmVjdCBvbmx5IHNpbmdsZS1yZXBsaWNhOyB0aGUgUmVkaXMgc3RvcmUgbWFrZXMgdGhlXG4gKiAgIG1pbnQgKG9uZSBwb2QpIC8gcmVkZWVtIChhbm90aGVyIHBvZCkgZXhjaGFuZ2Ugd29yayBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyLlxuICogLSAqKlN0cmVhbSBvd25lcnNoaXAqKiDigJQgd2hpY2ggb3JnIE9XTlMgYSBnaXZlbiBzdHJlYW0gc3ViamVjdC4gVGhlIHN0cmVhbVxuICogICBwcm9kdWNlciAocGxhdGZvcm0sIGEgZGlmZmVyZW50IHNlcnZpY2UpIHJlY29yZHMgdGhpcyBzbyB0aWNrZXQgbWludGluZyBjYW5cbiAqICAgYXNzZXJ0IHRoZSBjYWxsZXIncyBvcmcgb3ducyB0aGUgc3ViamVjdCwgY2xvc2luZyB0aGUgY3Jvc3MtdGVuYW50IGF0dGFjaFxuICogICBnYXAgd2hlcmUgYW55IG9yZyBjb3VsZCBtaW50IGEgdGlja2V0IGZvciBhIGd1ZXNzZWQgcmVxdWVzdElkLlxuICpcbiAqIEFsbCBtZXRob2RzIGFyZSBhc3luYyAoUmVkaXMgaXMgYXN5bmMpIGFuZCBiZXN0LWVmZm9ydCBvbiB0aGUgZmFpbHVyZSBzaWRlIHBlclxuICogdGhlaXIgZG9jIOKAlCBhIHN0b3JlIG91dGFnZSBtdXN0IGRlZ3JhZGUgZ3JhY2VmdWxseSwgbmV2ZXIgY3Jhc2ggdGhlIHN0cmVhbS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VTdHJlYW1UaWNrZXRTdG9yZSB7XG4gIC8qKiBQZXJzaXN0IGEgc2luZ2xlLXVzZSB0aWNrZXQgYm91bmQgdG8gYHJlY29yZGAsIGV4cGlyaW5nIGFmdGVyIGB0dGxNc2AuICovXG4gIHB1dCh0aWNrZXRJZDogc3RyaW5nLCByZWNvcmQ6IFNTRVN0cmVhbVRpY2tldFJlY29yZCwgdHRsTXM6IG51bWJlcik6IFByb21pc2U8dm9pZD47XG4gIC8qKiBBdG9taWNhbGx5IGZldGNoLWFuZC1kZWxldGUgYSB0aWNrZXQgKHNpbmdsZS11c2UpLiBOdWxsIGlmIGFic2VudC9leHBpcmVkLiAqL1xuICBjb25zdW1lKHRpY2tldElkOiBzdHJpbmcpOiBQcm9taXNlPFNTRVN0cmVhbVRpY2tldFJlY29yZCB8IG51bGw+O1xuICAvKiogQ291bnQgb2YgbGl2ZSAodW5leHBpcmVkKSB0aWNrZXRzIGhlbGQgZm9yIGFuIG9yZyAocGVyLW9yZyBjYXApLiAqL1xuICBjb3VudEZvck9yZyhvcmdJZDogc3RyaW5nKTogUHJvbWlzZTxudW1iZXI+O1xuICAvKiogQ291bnQgb2YgYWxsIGxpdmUgdGlja2V0cyBhY3Jvc3MgdGhlIHN0b3JlIChjYXBhY2l0eSBjYXApLiAqL1xuICB0b3RhbCgpOiBQcm9taXNlPG51bWJlcj47XG4gIC8qKiBSZWNvcmQgdGhlIG9yZyB0aGF0IE9XTlMgYSBzdHJlYW0gc3ViamVjdCAocHJvZHVjZXItc2lkZSksIFRUTCBgdHRsTXNgLiAqL1xuICBiaW5kU3RyZWFtT3duZXIocmVxdWVzdElkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcsIHR0bE1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+O1xuICAvKiogVGhlIG93bmluZyBvcmcgb2YgYSBzdHJlYW0gc3ViamVjdCwgb3IgbnVsbCB3aGVuIG5vbmUgaXMgYm91bmQuICovXG4gIGdldFN0cmVhbU93bmVyKHJlcXVlc3RJZDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbiAgLyoqIE9wdGlvbmFsIGh5Z2llbmUgc3dlZXAgb2YgZXhwaXJlZCBlbnRyaWVzIChSZWRpcyBUVEwgbWFrZXMgdGhpcyBhIG5vLW9wKS4gKi9cbiAgc3dlZXA/KCk6IFByb21pc2U8dm9pZD47XG59XG5cbmludGVyZmFjZSBTdG9yZWRUaWNrZXQgZXh0ZW5kcyBTU0VTdHJlYW1UaWNrZXRSZWNvcmQge1xuICBleHBpcmVzQXQ6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFN0b3JlZE93bmVyIHtcbiAgb3JnSWQ6IHN0cmluZztcbiAgZXhwaXJlc0F0OiBudW1iZXI7XG59XG5cbi8qKlxuICogSW4tbWVtb3J5IHRpY2tldCBzdG9yZSDigJQgdGhlIGRlZmF1bHQuIENvcnJlY3QgZm9yIGEgc2luZ2xlIHJlcGxpY2Egb25seTtcbiAqIG1pcnJvcnMgdGhlIGJlaGF2aW9yIHRoZSBTU0VNYW5hZ2VyIHByZXZpb3VzbHkgaW1wbGVtZW50ZWQgaW5saW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTWVtb3J5VGlja2V0U3RvcmUoKTogU1NFU3RyZWFtVGlja2V0U3RvcmUge1xuICBjb25zdCB0aWNrZXRzID0gbmV3IE1hcDxzdHJpbmcsIFN0b3JlZFRpY2tldD4oKTtcbiAgY29uc3Qgb3duZXJzID0gbmV3IE1hcDxzdHJpbmcsIFN0b3JlZE93bmVyPigpO1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgcHV0KHRpY2tldElkLCByZWNvcmQsIHR0bE1zKSB7XG4gICAgICB0aWNrZXRzLnNldCh0aWNrZXRJZCwgeyAuLi5yZWNvcmQsIGV4cGlyZXNBdDogRGF0ZS5ub3coKSArIHR0bE1zIH0pO1xuICAgIH0sXG4gICAgYXN5bmMgY29uc3VtZSh0aWNrZXRJZCkge1xuICAgICAgY29uc3QgdCA9IHRpY2tldHMuZ2V0KHRpY2tldElkKTtcbiAgICAgIHRpY2tldHMuZGVsZXRlKHRpY2tldElkKTsgLy8gc2luZ2xlLXVzZSDigJQgcmVtb3ZlIHdoZXRoZXIgb3Igbm90IHZhbGlkXG4gICAgICBpZiAoIXQgfHwgRGF0ZS5ub3coKSA+IHQuZXhwaXJlc0F0KSByZXR1cm4gbnVsbDtcbiAgICAgIHJldHVybiB7IG9yZ0lkOiB0Lm9yZ0lkLCByZXF1ZXN0SWQ6IHQucmVxdWVzdElkIH07XG4gICAgfSxcbiAgICBhc3luYyBjb3VudEZvck9yZyhvcmdJZCkge1xuICAgICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICAgIGxldCBuID0gMDtcbiAgICAgIGZvciAoY29uc3QgdCBvZiB0aWNrZXRzLnZhbHVlcygpKSBpZiAodC5vcmdJZCA9PT0gb3JnSWQgJiYgdC5leHBpcmVzQXQgPiBub3cpIG4rKztcbiAgICAgIHJldHVybiBuO1xuICAgIH0sXG4gICAgYXN5bmMgdG90YWwoKSB7XG4gICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgICAgbGV0IG4gPSAwO1xuICAgICAgZm9yIChjb25zdCB0IG9mIHRpY2tldHMudmFsdWVzKCkpIGlmICh0LmV4cGlyZXNBdCA+IG5vdykgbisrO1xuICAgICAgcmV0dXJuIG47XG4gICAgfSxcbiAgICBhc3luYyBiaW5kU3RyZWFtT3duZXIocmVxdWVzdElkLCBvcmdJZCwgdHRsTXMpIHtcbiAgICAgIG93bmVycy5zZXQocmVxdWVzdElkLCB7IG9yZ0lkLCBleHBpcmVzQXQ6IERhdGUubm93KCkgKyB0dGxNcyB9KTtcbiAgICB9LFxuICAgIGFzeW5jIGdldFN0cmVhbU93bmVyKHJlcXVlc3RJZCkge1xuICAgICAgY29uc3QgbyA9IG93bmVycy5nZXQocmVxdWVzdElkKTtcbiAgICAgIGlmICghbykgcmV0dXJuIG51bGw7XG4gICAgICBpZiAoRGF0ZS5ub3coKSA+IG8uZXhwaXJlc0F0KSB7IG93bmVycy5kZWxldGUocmVxdWVzdElkKTsgcmV0dXJuIG51bGw7IH1cbiAgICAgIHJldHVybiBvLm9yZ0lkO1xuICAgIH0sXG4gICAgYXN5bmMgc3dlZXAoKSB7XG4gICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgICAgZm9yIChjb25zdCBbaWQsIHRdIG9mIHRpY2tldHMpIGlmIChub3cgPiB0LmV4cGlyZXNBdCkgdGlja2V0cy5kZWxldGUoaWQpO1xuICAgICAgZm9yIChjb25zdCBbaWQsIG9dIG9mIG93bmVycykgaWYgKG5vdyA+IG8uZXhwaXJlc0F0KSBvd25lcnMuZGVsZXRlKGlkKTtcbiAgICB9LFxuICB9O1xufVxuXG4vKipcbiAqIE1pbmltYWwgaW9yZWRpcyBzdXJmYWNlIHRoZSBSZWRpcyB0aWNrZXQgc3RvcmUgbmVlZHMuIGBzZXRgIHRha2VzIHRoZSB2YXJpYWRpY1xuICogb3B0aW9uIHRhaWwgKGBFWCA8cz5gKTsgYHphZGRgL2B6cmVtcmFuZ2VieXNjb3JlYC9gemNhcmRgIGJhY2sgdGhlIGNhcCBjb3VudGVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZWRpc1RpY2tldENsaWVudCB7XG4gIGdldChrZXk6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIHNldChrZXk6IHN0cmluZywgdmFsdWU6IHN0cmluZywgLi4uYXJnczogKHN0cmluZyB8IG51bWJlcilbXSk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIGRlbCguLi5rZXlzOiBzdHJpbmdbXSk6IFByb21pc2U8bnVtYmVyPjtcbiAgemFkZChrZXk6IHN0cmluZywgc2NvcmU6IG51bWJlciwgbWVtYmVyOiBzdHJpbmcpOiBQcm9taXNlPG51bWJlcj47XG4gIHpyZW0oa2V5OiBzdHJpbmcsIC4uLm1lbWJlcnM6IHN0cmluZ1tdKTogUHJvbWlzZTxudW1iZXI+O1xuICB6Y2FyZChrZXk6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcbiAgenJlbXJhbmdlYnlzY29yZShrZXk6IHN0cmluZywgbWluOiBudW1iZXIgfCBzdHJpbmcsIG1heDogbnVtYmVyIHwgc3RyaW5nKTogUHJvbWlzZTxudW1iZXI+O1xuICBleHBpcmUoa2V5OiBzdHJpbmcsIHNlY29uZHM6IG51bWJlcik6IFByb21pc2U8bnVtYmVyPjtcbn1cblxuY29uc3QgVElDS0VUX1BSRUZJWCA9ICdzc2U6dGt0Oic7XG5jb25zdCBPUkdfWlNFVF9QUkVGSVggPSAnc3NlOnRrdDpvcmc6JztcbmNvbnN0IEFMTF9aU0VUID0gJ3NzZTp0a3Q6YWxsJztcbmNvbnN0IE9XTkVSX1BSRUZJWCA9ICdzc2U6b3duZXI6JztcblxuLyoqXG4gKiBSZWRpcy1iYWNrZWQgdGlja2V0IHN0b3JlIOKAlCByZXF1aXJlZCBmb3IgbXVsdGktcG9kIGNvcnJlY3RuZXNzLiBUaWNrZXRzIGFyZVxuICogcGVyLWtleSB3aXRoIG5hdGl2ZSBUVEw7IGNhcCBjb3VudGVycyBhcmUgWlNFVHMgc2NvcmVkIGJ5IGV4cGlyeSAocHJ1bmVkIG9uXG4gKiByZWFkKS4gU3RyZWFtIG93bmVyc2hpcCBpcyBhIHBlci1zdWJqZWN0IGtleSB3aXRoIGl0cyBvd24gVFRMLiBBbGwgcmVhZHMgYXJlXG4gKiBmYWlsLXNhZmUgKG51bGwgLyAwKSBzbyBhIFJlZGlzIGJsaXAgbmV2ZXIgd2VkZ2VzIHRoZSBzdHJlYW0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWRpc1RpY2tldFN0b3JlKHJlZGlzOiBSZWRpc1RpY2tldENsaWVudCk6IFNTRVN0cmVhbVRpY2tldFN0b3JlIHtcbiAgY29uc3QgdGtleSA9IChpZDogc3RyaW5nKTogc3RyaW5nID0+IGAke1RJQ0tFVF9QUkVGSVh9JHtpZH1gO1xuICBjb25zdCBvcmdLZXkgPSAob3JnSWQ6IHN0cmluZyk6IHN0cmluZyA9PiBgJHtPUkdfWlNFVF9QUkVGSVh9JHtvcmdJZH1gO1xuICBjb25zdCBvd25lcktleSA9IChyZXF1ZXN0SWQ6IHN0cmluZyk6IHN0cmluZyA9PiBgJHtPV05FUl9QUkVGSVh9JHtyZXF1ZXN0SWR9YDtcbiAgY29uc3QgdHRsU2VjID0gKHR0bE1zOiBudW1iZXIpOiBudW1iZXIgPT4gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKHR0bE1zIC8gMTAwMCkpO1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgcHV0KHRpY2tldElkLCByZWNvcmQsIHR0bE1zKSB7XG4gICAgICBjb25zdCBleHBpcmVzQXQgPSBEYXRlLm5vdygpICsgdHRsTXM7XG4gICAgICBjb25zdCBzZWMgPSB0dGxTZWModHRsTXMpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgcmVkaXMuc2V0KHRrZXkodGlja2V0SWQpLCBKU09OLnN0cmluZ2lmeShyZWNvcmQpLCAnRVgnLCBzZWMpO1xuICAgICAgICAvLyBUcmFjayBmb3IgdGhlIGNhcHMuIFNjb3JlID0gZXhwaXJ5IHNvIHBydW5pbmcgaXMgYSByYW5nZSBkZWxldGUuXG4gICAgICAgIGF3YWl0IHJlZGlzLnphZGQob3JnS2V5KHJlY29yZC5vcmdJZCksIGV4cGlyZXNBdCwgdGlja2V0SWQpO1xuICAgICAgICBhd2FpdCByZWRpcy56YWRkKEFMTF9aU0VULCBleHBpcmVzQXQsIHRpY2tldElkKTtcbiAgICAgICAgLy8gQm91bmQgdGhlIHBlci1vcmcgWlNFVCdzIG93biBsaWZldGltZSBzbyBhbiBpZGxlIG9yZydzIGtleSBjYW4ndCBsZWFrLlxuICAgICAgICBhd2FpdCByZWRpcy5leHBpcmUob3JnS2V5KHJlY29yZC5vcmdJZCksIHNlYyk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1NTRSB0aWNrZXQgcHV0IGZhaWxlZCcsIHsgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9KTtcbiAgICAgIH1cbiAgICB9LFxuICAgIGFzeW5jIGNvbnN1bWUodGlja2V0SWQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJhdyA9IGF3YWl0IHJlZGlzLmdldCh0a2V5KHRpY2tldElkKSk7XG4gICAgICAgIC8vIFNpbmdsZS11c2U6IGRlbGV0ZSByZWdhcmRsZXNzLiBBIGNvbmN1cnJlbnQgZG91YmxlLXJlZGVlbTogYXQgbW9zdCBvbmVcbiAgICAgICAgLy8gc2F3IGEgbm9uLW51bGwgdmFsdWUgYmVmb3JlIHRoZSBkZWxldGU7IHRoZSBaUkVNIGNsZWFucyB0aGUgY291bnRlcnMuXG4gICAgICAgIGF3YWl0IHJlZGlzLmRlbCh0a2V5KHRpY2tldElkKSk7XG4gICAgICAgIGlmICghcmF3KSByZXR1cm4gbnVsbDtcbiAgICAgICAgY29uc3QgcmVjID0gSlNPTi5wYXJzZShyYXcpIGFzIFNTRVN0cmVhbVRpY2tldFJlY29yZDtcbiAgICAgICAgYXdhaXQgcmVkaXMuenJlbShvcmdLZXkocmVjLm9yZ0lkKSwgdGlja2V0SWQpO1xuICAgICAgICBhd2FpdCByZWRpcy56cmVtKEFMTF9aU0VULCB0aWNrZXRJZCk7XG4gICAgICAgIHJldHVybiByZWM7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1NTRSB0aWNrZXQgY29uc3VtZSBmYWlsZWQnLCB7IGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfSk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgIH0sXG4gICAgYXN5bmMgY291bnRGb3JPcmcob3JnSWQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHJlZGlzLnpyZW1yYW5nZWJ5c2NvcmUob3JnS2V5KG9yZ0lkKSwgMCwgRGF0ZS5ub3coKSk7XG4gICAgICAgIHJldHVybiBhd2FpdCByZWRpcy56Y2FyZChvcmdLZXkob3JnSWQpKTtcbiAgICAgIH0gY2F0Y2ggeyByZXR1cm4gMDsgfVxuICAgIH0sXG4gICAgYXN5bmMgdG90YWwoKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCByZWRpcy56cmVtcmFuZ2VieXNjb3JlKEFMTF9aU0VULCAwLCBEYXRlLm5vdygpKTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHJlZGlzLnpjYXJkKEFMTF9aU0VUKTtcbiAgICAgIH0gY2F0Y2ggeyByZXR1cm4gMDsgfVxuICAgIH0sXG4gICAgYXN5bmMgYmluZFN0cmVhbU93bmVyKHJlcXVlc3RJZCwgb3JnSWQsIHR0bE1zKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCByZWRpcy5zZXQob3duZXJLZXkocmVxdWVzdElkKSwgb3JnSWQsICdFWCcsIHR0bFNlYyh0dGxNcykpO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdTU0Ugc3RyZWFtLW93bmVyIGJpbmQgZmFpbGVkJywgeyBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgYXN5bmMgZ2V0U3RyZWFtT3duZXIocmVxdWVzdElkKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gYXdhaXQgcmVkaXMuZ2V0KG93bmVyS2V5KHJlcXVlc3RJZCkpO1xuICAgICAgfSBjYXRjaCB7IHJldHVybiBudWxsOyB9XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIFJlZGlzLWJhY2tlZCB0aWNrZXQgc3RvcmUgZnJvbSB0aGUgc2hhcmVkIGVudiBSZWRpcyAoc2FtZSB3aXJpbmcgYXNcbiAqIHRoZSByYXRlLWxpbWl0ZXIgLyBhdWRpdC1zcG9vbCkuIFJldHVybnMgbnVsbCB3aGVuIFJlZGlzIGlzbid0IGNvbmZpZ3VyZWQgc29cbiAqIHRoZSBjYWxsZXIga2VlcHMgdGhlIGluLW1lbW9yeSBkZWZhdWx0LiBOZXZlciB0aHJvd3MuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFbnZSZWRpc1RpY2tldFN0b3JlKCk6IFNTRVN0cmVhbVRpY2tldFN0b3JlIHwgbnVsbCB7XG4gIGNvbnN0IGNsaWVudCA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PFJlZGlzVGlja2V0Q2xpZW50Pignc3NlLXRpY2tldCcpO1xuICBpZiAoIWNsaWVudCkgcmV0dXJuIG51bGw7XG4gIGxvZ2dlci5pbmZvKCdSZWRpcyBTU0UgdGlja2V0IHN0b3JlIGluaXRpYWxpemVkJyk7XG4gIHJldHVybiBjcmVhdGVSZWRpc1RpY2tldFN0b3JlKGNsaWVudCk7XG59XG4iXX0=