@pipeline-builder/api-server 4.1.4 → 4.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/api/app-factory.d.ts +8 -1
- package/lib/api/app-factory.js +68 -59
- package/lib/api/idempotency-middleware.js +14 -1
- package/lib/api/index.d.ts +0 -1
- package/lib/api/index.js +1 -2
- package/lib/api/metrics.d.ts +9 -0
- package/lib/api/metrics.js +23 -1
- package/lib/api/middleware-factory.js +2 -2
- package/lib/api/quota-helpers.d.ts +2 -4
- package/lib/api/quota-helpers.js +3 -3
- package/lib/api/rate-limit-by-org.js +10 -5
- package/lib/api/request-types.d.ts +1 -1
- package/lib/api/request-types.js +7 -2
- package/lib/api/server.js +19 -2
- package/lib/api/tenant-context.js +28 -3
- package/lib/http/index.d.ts +0 -1
- package/lib/http/index.js +1 -2
- package/lib/http/sse-connection-manager.d.ts +59 -29
- package/lib/http/sse-connection-manager.js +89 -42
- package/lib/http/sse-relay.d.ts +16 -5
- package/lib/http/sse-relay.js +28 -16
- package/lib/http/sse-ticket-channel.d.ts +4 -2
- package/lib/http/sse-ticket-channel.js +13 -10
- package/package.json +5 -6
- package/lib/api/middleware.d.ts +0 -1
- package/lib/api/middleware.js +0 -8
- package/lib/http/sse-ticket-store.d.ts +0 -75
- package/lib/http/sse-ticket-store.js +0 -160
package/lib/http/sse-relay.js
CHANGED
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
import { createLogger, createEnvRedisClient } from '@pipeline-builder/api-core';
|
|
4
4
|
const logger = createLogger('sse-relay');
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
-
|
|
14
|
+
export function sseRelayChannel(serviceName) {
|
|
15
|
+
return `sse:relay:${serviceName}`;
|
|
16
|
+
}
|
|
11
17
|
/** Backoff bounds for the startup SUBSCRIBE retry. */
|
|
12
18
|
const SUBSCRIBE_RETRY_MIN_MS = 500;
|
|
13
19
|
const SUBSCRIBE_RETRY_MAX_MS = 30_000;
|
|
@@ -17,21 +23,26 @@ const SUBSCRIBE_RETRY_MAX_MS = 30_000;
|
|
|
17
23
|
* connection). Publish is fire-and-forget with a swallowed rejection; a subscribe
|
|
18
24
|
* handler that throws is isolated so one bad frame can't kill the subscriber.
|
|
19
25
|
*/
|
|
20
|
-
export function createRedisSSERelay(publisher) {
|
|
26
|
+
export function createRedisSSERelay(publisher, channel) {
|
|
21
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
|
+
});
|
|
22
33
|
let closed = false;
|
|
23
34
|
return {
|
|
24
35
|
publish(msg) {
|
|
25
36
|
if (closed)
|
|
26
37
|
return;
|
|
27
38
|
// Fire-and-forget: never await, never surface a rejection to the caller.
|
|
28
|
-
void Promise.resolve(publisher.publish(
|
|
39
|
+
void Promise.resolve(publisher.publish(channel, JSON.stringify(msg))).catch((err) => {
|
|
29
40
|
logger.warn('SSE relay publish failed', { error: err instanceof Error ? err.message : String(err) });
|
|
30
41
|
});
|
|
31
42
|
},
|
|
32
43
|
subscribe(handler) {
|
|
33
|
-
subscriber.on('message', (
|
|
34
|
-
if (
|
|
44
|
+
subscriber.on('message', (received, message) => {
|
|
45
|
+
if (received !== channel)
|
|
35
46
|
return;
|
|
36
47
|
let parsed;
|
|
37
48
|
try {
|
|
@@ -55,7 +66,7 @@ export function createRedisSSERelay(publisher) {
|
|
|
55
66
|
const attempt = () => {
|
|
56
67
|
if (closed)
|
|
57
68
|
return;
|
|
58
|
-
void Promise.resolve(subscriber.subscribe(
|
|
69
|
+
void Promise.resolve(subscriber.subscribe(channel)).then(() => logger.info('SSE relay subscribed', { channel }), (err) => {
|
|
59
70
|
logger.warn('SSE relay subscribe failed; retrying (local-only delivery meanwhile)', {
|
|
60
71
|
retryInMs: delayMs, error: err instanceof Error ? err.message : String(err),
|
|
61
72
|
});
|
|
@@ -72,15 +83,16 @@ export function createRedisSSERelay(publisher) {
|
|
|
72
83
|
};
|
|
73
84
|
}
|
|
74
85
|
/**
|
|
75
|
-
* Build a Redis-backed SSE relay from the shared env Redis
|
|
76
|
-
*
|
|
77
|
-
*
|
|
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.
|
|
78
89
|
*/
|
|
79
|
-
export function createEnvRedisSSERelay() {
|
|
90
|
+
export function createEnvRedisSSERelay(serviceName = process.env.SERVICE_NAME || 'api') {
|
|
80
91
|
const client = createEnvRedisClient('sse-relay');
|
|
81
92
|
if (!client)
|
|
82
93
|
return null;
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
const channel = sseRelayChannel(serviceName);
|
|
95
|
+
logger.info('Redis SSE relay initialized (cross-pod fan-out enabled)', { channel });
|
|
96
|
+
return createRedisSSERelay(client, channel);
|
|
85
97
|
}
|
|
86
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXJlbGF5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvc3NlLXJlbGF5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBR2hGLE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztBQXNEekM7Ozs7R0FJRztBQUNILE1BQU0sYUFBYSxHQUFHLFdBQVcsQ0FBQztBQUVsQyxzREFBc0Q7QUFDdEQsTUFBTSxzQkFBc0IsR0FBRyxHQUFHLENBQUM7QUFDbkMsTUFBTSxzQkFBc0IsR0FBRyxNQUFNLENBQUM7QUFFdEM7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBQUMsU0FBNEI7SUFDOUQsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQ3pDLElBQUksTUFBTSxHQUFHLEtBQUssQ0FBQztJQUVuQixPQUFPO1FBQ0wsT0FBTyxDQUFDLEdBQUc7WUFDVCxJQUFJLE1BQU07Z0JBQUUsT0FBTztZQUNuQix5RUFBeUU7WUFDekUsS0FBSyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFO2dCQUN4RixNQUFNLENBQUMsSUFBSSxDQUFDLDBCQUEwQixFQUFFLEVBQUUsS0FBSyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDdkcsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO1FBQ0QsU0FBUyxDQUFDLE9BQU87WUFDZixVQUFVLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsRUFBRTtnQkFDNUMsSUFBSSxPQUFPLEtBQUssYUFBYTtvQkFBRSxPQUFPO2dCQUN0QyxJQUFJLE1BQXVCLENBQUM7Z0JBQzVCLElBQUksQ0FBQztvQkFDSCxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQW9CLENBQUM7Z0JBQ2xELENBQUM7Z0JBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztvQkFDYixNQUFNLENBQUMsSUFBSSxDQUFDLHNDQUFzQyxFQUFFLEVBQUUsS0FBSyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQ2pILE9BQU87Z0JBQ1QsQ0FBQztnQkFDRCxJQUFJLENBQUM7b0JBQ0gsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNsQixDQUFDO2dCQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7b0JBQ2IsTUFBTSxDQUFDLElBQUksQ0FBQyx5QkFBeUIsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN0RyxDQUFDO1lBQ0gsQ0FBQyxDQUFDLENBQUM7WUFDSCwwRUFBMEU7WUFDMUUsMEVBQTBFO1lBQzFFLDJFQUEyRTtZQUMzRSxJQUFJLE9BQU8sR0FBRyxzQkFBc0IsQ0FBQztZQUNyQyxNQUFNLE9BQU8sR0FBRyxHQUFTLEVBQUU7Z0JBQ3pCLElBQUksTUFBTTtvQkFBRSxPQUFPO2dCQUNuQixLQUFLLE9BQU8sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FDNUQsR0FBRyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxFQUN6QyxDQUFDLEdBQUcsRUFBRSxFQUFFO29CQUNOLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0VBQXNFLEVBQUU7d0JBQ2xGLFNBQVMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7cUJBQzVFLENBQUMsQ0FBQztvQkFDSCxVQUFVLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUM7b0JBQ3ZDLE9BQU8sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sR0FBRyxDQUFDLEVBQUUsc0JBQXNCLENBQUMsQ0FBQztnQkFDMUQsQ0FBQyxDQUNGLENBQUM7WUFDSixDQUFDLENBQUM7WUFDRixPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUM7UUFDRCxLQUFLLENBQUMsS0FBSztZQUNULE1BQU0sR0FBRyxJQUFJLENBQUM7WUFDZCxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLEVBQUUsVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNsRSxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQjtJQUNwQyxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBb0IsV0FBVyxDQUFDLENBQUM7SUFDcEUsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLElBQUksQ0FBQztJQUN6QixNQUFNLENBQUMsSUFBSSxDQUFDLHlEQUF5RCxDQUFDLENBQUM7SUFDdkUsT0FBTyxtQkFBbUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUNyQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFNTRVBheWxvYWQgfSBmcm9tICcuL3NzZS1jb25uZWN0aW9uLW1hbmFnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3NzZS1yZWxheScpO1xuXG4vKipcbiAqIEEgcmVsYXllZCBTU0UgZnJhbWUuIGBvcmlnaW5gIGlzIHRoZSBpZCBvZiB0aGUgU1NFTWFuYWdlciBpbnN0YW5jZSB0aGF0XG4gKiBwdWJsaXNoZWQgaXQsIHNvIHRoZSBwdWJsaXNoaW5nIHBvZCBjYW4gaWdub3JlIGl0cyBvd24gZWNobyAoaXQgYWxyZWFkeSB3cm90ZVxuICogdGhlIGZyYW1lIHRvIGl0cyBsb2NhbCBjbGllbnRzIGJlZm9yZSBwdWJsaXNoaW5nKS4gYGtpbmRgIGRpc3Rpbmd1aXNoZXMgYVxuICogcGVyLXN1YmplY3QgYHNlbmRgIChjYXJyaWVzIGByZXF1ZXN0SWRgKSBmcm9tIGEgYGJyb2FkY2FzdGAgKG5vIHN1YmplY3Qg4oCUXG4gKiBldmVyeSBwb2QgcmUtZW1pdHMgdG8gQUxMIG9mIGl0cyBsb2NhbCBjbGllbnRzKS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VSZWxheU1lc3NhZ2Uge1xuICBvcmlnaW46IHN0cmluZztcbiAga2luZDogJ3NlbmQnIHwgJ2Jyb2FkY2FzdCc7XG4gIC8qKiBQcmVzZW50IGZvciBgc2VuZGA7IHRoZSBzdHJlYW0gc3ViamVjdCB0aGUgcGF5bG9hZCB0YXJnZXRzLiAqL1xuICByZXF1ZXN0SWQ/OiBzdHJpbmc7XG4gIHBheWxvYWQ6IFNTRVBheWxvYWQ7XG59XG5cbi8qKlxuICogQ3Jvc3MtcG9kIGZhbi1vdXQgYnVzIGZvciBTU0UgZnJhbWVzLlxuICpcbiAqIFVuZGVyIG11bHRpcGxlIHJlcGxpY2FzIHRoZSBwcm9kdWNlciBvZiBhIGxvZyBmcmFtZSAoZS5nLiBhIHBsdWdpbiBidWlsZFxuICogd29ya2VyIG9uIHBvZCBBKSBhbmQgdGhlIGNvbnN1bWVyIChhIGJyb3dzZXIgRXZlbnRTb3VyY2Ugb24gcG9kIEIpIGFyZSBvblxuICogRElGRkVSRU5UIHBvZHMuIFRoZSBpbi1wcm9jZXNzIGNsaWVudCBtYXAgb25seSByZWFjaGVzIHRoZSBwcm9kdWNlcidzIG93blxuICogcG9kLCBzbyBCJ3Mgc3RyZWFtIHdvdWxkIHNpbGVudGx5IG1pc3MgZnJhbWVzLiBUaGUgcmVsYXkgY2xvc2VzIHRoYXQ6IGV2ZXJ5XG4gKiBwb2QgU1VCU0NSSUJFUyBvbmNlIG9uIHN0YXJ0dXAgYW5kIHJlLWVtaXRzIHJlY2VpdmVkIGZyYW1lcyB0byBpdHMgTE9DQUxcbiAqIGNsaWVudHMsIHdoaWxlIGBzZW5kKClgL2Bicm9hZGNhc3QoKWAgUFVCTElTSCB0byB0aGUgYnVzIChpbiBhZGRpdGlvbiB0b1xuICogd3JpdGluZyBsb2NhbGx5KS4gVGhlIG9yaWdpbiB0YWcgcHJldmVudHMgYSBwb2QgZnJvbSBkb3VibGUtZGVsaXZlcmluZyBpdHMgb3duXG4gKiBmcmFtZSB3aGVuIGl0IHJlY2VpdmVzIGl0cyBvd24gcHVibGlzaCBiYWNrLlxuICpcbiAqIEFsbCBtZXRob2RzIGFyZSBiZXN0LWVmZm9ydCAvIGZhaWwtc2FmZTogYSBidXMgb3V0YWdlIG11c3QgZGVncmFkZSB0b1xuICogbG9jYWwtb25seSBkZWxpdmVyeSAodG9kYXkncyBzaW5nbGUtcmVwbGljYSBiZWhhdmlvciksIG5ldmVyIGNyYXNoIHRoZSBzdHJlYW0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU1NFUmVsYXkge1xuICAvKiogRmlyZS1hbmQtZm9yZ2V0IHB1Ymxpc2ggb2YgYSBmcmFtZSB0byBldmVyeSBzdWJzY3JpYmVkIHBvZC4gTmV2ZXIgdGhyb3dzLiAqL1xuICBwdWJsaXNoKG1zZzogU1NFUmVsYXlNZXNzYWdlKTogdm9pZDtcbiAgLyoqIFN1YnNjcmliZSB0aGlzIHBvZCdzIHJlLWVtaXQgaGFuZGxlci4gQ2FsbGVkIG9uY2Ugb24gbWFuYWdlciBjb25zdHJ1Y3Rpb24uICovXG4gIHN1YnNjcmliZShoYW5kbGVyOiAobXNnOiBTU0VSZWxheU1lc3NhZ2UpID0+IHZvaWQpOiB2b2lkO1xuICAvKiogVGVhciBkb3duIHRoZSB1bmRlcmx5aW5nIGNvbm5lY3Rpb24ocykuICovXG4gIGNsb3NlKCk6IFByb21pc2U8dm9pZD47XG59XG5cbi8qKlxuICogTWluaW1hbCBpb3JlZGlzIHB1Yi9zdWIgc3VyZmFjZSB0aGUgUmVkaXMgcmVsYXkgbmVlZHMuIGBzdWJzY3JpYmVgIHJlcXVpcmVzIGFcbiAqIERFRElDQVRFRCBjb25uZWN0aW9uIChhIHN1YnNjcmliZWQgaW9yZWRpcyBjbGllbnQgY2FuJ3QgcnVuIG90aGVyIGNvbW1hbmRzKSxcbiAqIHNvIHRoZSByZWxheSBgZHVwbGljYXRlKClgcyB0aGUgcHVibGlzaGVyIGZvciB0aGUgc3Vic2NyaWJlciBzaWRlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlZGlzUHViU3ViQ2xpZW50IHtcbiAgcHVibGlzaChjaGFubmVsOiBzdHJpbmcsIG1lc3NhZ2U6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcbiAgc3Vic2NyaWJlKC4uLmNoYW5uZWxzOiBzdHJpbmdbXSk6IFByb21pc2U8dW5rbm93bj47XG4gIG9uKGV2ZW50OiAnbWVzc2FnZScsIGNiOiAoY2hhbm5lbDogc3RyaW5nLCBtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWQpOiB2b2lkO1xuICBkdXBsaWNhdGUoKTogUmVkaXNQdWJTdWJDbGllbnQ7XG4gIHF1aXQoKTogUHJvbWlzZTx1bmtub3duPjtcbn1cblxuLyoqXG4gKiBTaW5nbGUgY2hhbm5lbCBjYXJyeWluZyBldmVyeSByZWxheWVkIGZyYW1lLiBgcmVxdWVzdElkYCByaWRlcyBpbiB0aGUgbWVzc2FnZVxuICogYm9keSByYXRoZXIgdGhhbiB0aGUgY2hhbm5lbCBuYW1lIHNvIGVhY2ggcG9kIFNVQlNDUklCRVMgZXhhY3RseSBvbmNlIG9uXG4gKiBzdGFydHVwIOKAlCBubyBwZXItc3ViamVjdCBzdWJzY3JpYmUvdW5zdWJzY3JpYmUgY2h1cm4gYXMgY2xpZW50cyBjb21lIGFuZCBnby5cbiAqL1xuY29uc3QgUkVMQVlfQ0hBTk5FTCA9ICdzc2U6cmVsYXknO1xuXG4vKiogQmFja29mZiBib3VuZHMgZm9yIHRoZSBzdGFydHVwIFNVQlNDUklCRSByZXRyeS4gKi9cbmNvbnN0IFNVQlNDUklCRV9SRVRSWV9NSU5fTVMgPSA1MDA7XG5jb25zdCBTVUJTQ1JJQkVfUkVUUllfTUFYX01TID0gMzBfMDAwO1xuXG4vKipcbiAqIFJlZGlzLWJhY2tlZCByZWxheS4gVXNlcyB0aGUgZ2l2ZW4gY2xpZW50IGZvciBQVUJMSVNIIGFuZCBhIGBkdXBsaWNhdGUoKWAgZm9yXG4gKiBTVUJTQ1JJQkUgKGlvcmVkaXMgZm9yYmlkcyBtaXhpbmcgc3Vic2NyaWJlIHdpdGggbm9ybWFsIGNvbW1hbmRzIG9uIG9uZVxuICogY29ubmVjdGlvbikuIFB1Ymxpc2ggaXMgZmlyZS1hbmQtZm9yZ2V0IHdpdGggYSBzd2FsbG93ZWQgcmVqZWN0aW9uOyBhIHN1YnNjcmliZVxuICogaGFuZGxlciB0aGF0IHRocm93cyBpcyBpc29sYXRlZCBzbyBvbmUgYmFkIGZyYW1lIGNhbid0IGtpbGwgdGhlIHN1YnNjcmliZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWRpc1NTRVJlbGF5KHB1Ymxpc2hlcjogUmVkaXNQdWJTdWJDbGllbnQpOiBTU0VSZWxheSB7XG4gIGNvbnN0IHN1YnNjcmliZXIgPSBwdWJsaXNoZXIuZHVwbGljYXRlKCk7XG4gIGxldCBjbG9zZWQgPSBmYWxzZTtcblxuICByZXR1cm4ge1xuICAgIHB1Ymxpc2gobXNnKSB7XG4gICAgICBpZiAoY2xvc2VkKSByZXR1cm47XG4gICAgICAvLyBGaXJlLWFuZC1mb3JnZXQ6IG5ldmVyIGF3YWl0LCBuZXZlciBzdXJmYWNlIGEgcmVqZWN0aW9uIHRvIHRoZSBjYWxsZXIuXG4gICAgICB2b2lkIFByb21pc2UucmVzb2x2ZShwdWJsaXNoZXIucHVibGlzaChSRUxBWV9DSEFOTkVMLCBKU09OLnN0cmluZ2lmeShtc2cpKSkuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICBsb2dnZXIud2FybignU1NFIHJlbGF5IHB1Ymxpc2ggZmFpbGVkJywgeyBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0pO1xuICAgICAgfSk7XG4gICAgfSxcbiAgICBzdWJzY3JpYmUoaGFuZGxlcikge1xuICAgICAgc3Vic2NyaWJlci5vbignbWVzc2FnZScsIChjaGFubmVsLCBtZXNzYWdlKSA9PiB7XG4gICAgICAgIGlmIChjaGFubmVsICE9PSBSRUxBWV9DSEFOTkVMKSByZXR1cm47XG4gICAgICAgIGxldCBwYXJzZWQ6IFNTRVJlbGF5TWVzc2FnZTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBwYXJzZWQgPSBKU09OLnBhcnNlKG1lc3NhZ2UpIGFzIFNTRVJlbGF5TWVzc2FnZTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oJ1NTRSByZWxheSByZWNlaXZlZCB1bnBhcnNlYWJsZSBmcmFtZScsIHsgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBoYW5kbGVyKHBhcnNlZCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdTU0UgcmVsYXkgaGFuZGxlciB0aHJldycsIHsgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICAvLyBSZXRyeSB1bnRpbCBzdWJzY3JpYmVkOiBhdCBzdGFydHVwIHRoZSBjb25uZWN0aW9uIHVzdWFsbHkgaXNuJ3QgdXAgeWV0LFxuICAgICAgLy8gYW5kIGdpdmluZyB1cCBhZnRlciBvbmUgYXR0ZW1wdCBsZWZ0IHRoZSBwb2Qgb24gbG9jYWwtb25seSBkZWxpdmVyeSBmb3JcbiAgICAgIC8vIGl0cyB3aG9sZSBsaWZlLiBPbmNlIHN1YnNjcmliZWQsIGlvcmVkaXMgcmUtc3Vic2NyaWJlcyBhZnRlciByZWNvbm5lY3RzLlxuICAgICAgbGV0IGRlbGF5TXMgPSBTVUJTQ1JJQkVfUkVUUllfTUlOX01TO1xuICAgICAgY29uc3QgYXR0ZW1wdCA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKGNsb3NlZCkgcmV0dXJuO1xuICAgICAgICB2b2lkIFByb21pc2UucmVzb2x2ZShzdWJzY3JpYmVyLnN1YnNjcmliZShSRUxBWV9DSEFOTkVMKSkudGhlbihcbiAgICAgICAgICAoKSA9PiBsb2dnZXIuaW5mbygnU1NFIHJlbGF5IHN1YnNjcmliZWQnKSxcbiAgICAgICAgICAoZXJyKSA9PiB7XG4gICAgICAgICAgICBsb2dnZXIud2FybignU1NFIHJlbGF5IHN1YnNjcmliZSBmYWlsZWQ7IHJldHJ5aW5nIChsb2NhbC1vbmx5IGRlbGl2ZXJ5IG1lYW53aGlsZSknLCB7XG4gICAgICAgICAgICAgIHJldHJ5SW5NczogZGVsYXlNcywgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgc2V0VGltZW91dChhdHRlbXB0LCBkZWxheU1zKS51bnJlZj8uKCk7XG4gICAgICAgICAgICBkZWxheU1zID0gTWF0aC5taW4oZGVsYXlNcyAqIDIsIFNVQlNDUklCRV9SRVRSWV9NQVhfTVMpO1xuICAgICAgICAgIH0sXG4gICAgICAgICk7XG4gICAgICB9O1xuICAgICAgYXR0ZW1wdCgpO1xuICAgIH0sXG4gICAgYXN5bmMgY2xvc2UoKSB7XG4gICAgICBjbG9zZWQgPSB0cnVlO1xuICAgICAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtwdWJsaXNoZXIucXVpdCgpLCBzdWJzY3JpYmVyLnF1aXQoKV0pO1xuICAgIH0sXG4gIH07XG59XG5cbi8qKlxuICogQnVpbGQgYSBSZWRpcy1iYWNrZWQgU1NFIHJlbGF5IGZyb20gdGhlIHNoYXJlZCBlbnYgUmVkaXMgKHNhbWUgd2lyaW5nIGFzIHRoZVxuICogU1NFIHRpY2tldCBzdG9yZSAvIHJhdGUtbGltaXRlciAvIGF1ZGl0LXNwb29sKS4gUmV0dXJucyBudWxsIHdoZW4gUmVkaXMgaXNuJ3RcbiAqIGNvbmZpZ3VyZWQgc28gdGhlIGNhbGxlciBrZWVwcyBsb2NhbC1vbmx5IGRlbGl2ZXJ5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlRW52UmVkaXNTU0VSZWxheSgpOiBTU0VSZWxheSB8IG51bGwge1xuICBjb25zdCBjbGllbnQgPSBjcmVhdGVFbnZSZWRpc0NsaWVudDxSZWRpc1B1YlN1YkNsaWVudD4oJ3NzZS1yZWxheScpO1xuICBpZiAoIWNsaWVudCkgcmV0dXJuIG51bGw7XG4gIGxvZ2dlci5pbmZvKCdSZWRpcyBTU0UgcmVsYXkgaW5pdGlhbGl6ZWQgKGNyb3NzLXBvZCBmYW4tb3V0IGVuYWJsZWQpJyk7XG4gIHJldHVybiBjcmVhdGVSZWRpc1NTRVJlbGF5KGNsaWVudCk7XG59XG4iXX0=
|
|
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, …)
|
|
23
|
-
*
|
|
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, …)
|
|
10
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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.
|
|
48
|
-
"@pipeline-builder/pipeline-
|
|
49
|
-
"@pipeline-builder/pipeline-
|
|
46
|
+
"@pipeline-builder/api-core": "4.1.5",
|
|
47
|
+
"@pipeline-builder/pipeline-core": "4.1.6",
|
|
48
|
+
"@pipeline-builder/pipeline-data": "4.1.6"
|
|
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.
|
|
104
|
+
"version": "4.1.6",
|
|
106
105
|
"bugs": {
|
|
107
106
|
"url": "https://github.com/mwashburn160/pipeline-builder/issues"
|
|
108
107
|
},
|
package/lib/api/middleware.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { requireAuth } from '@pipeline-builder/api-core';
|
package/lib/api/middleware.js
DELETED
|
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLXRpY2tldC1zdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9odHRwL3NzZS10aWNrZXQtc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFaEYsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUF3RGhEOzs7R0FHRztBQUNILE1BQU0sVUFBVSx1QkFBdUI7SUFDckMsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQXdCLENBQUM7SUFDaEQsTUFBTSxNQUFNLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7SUFFOUMsT0FBTztRQUNMLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxLQUFLO1lBQy9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLEVBQUUsR0FBRyxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7UUFDRCxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVE7WUFDcEIsTUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNoQyxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsMkNBQTJDO1lBQ3JFLElBQUksQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQyxTQUFTO2dCQUFFLE9BQU8sSUFBSSxDQUFDO1lBQ2hELE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ3BELENBQUM7UUFDRCxLQUFLLENBQUMsV0FBVyxD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QUFPLElBQUksQ0FBQztJQUN6QixNQUFNLENBQUMsSUFBSSxDQUFDLG9DQUFvQyxDQUFDLENBQUM7SUFDbEQsT0FBTyxzQkFBc0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUN4QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignc3NlLXRpY2tldC1zdG9yZScpO1xuXG4vKiogQSBtaW50ZWQgdGlja2V0J3MgYm91bmQgaWRlbnRpdHk6IG93bmluZyBvcmcgKyBub3JtYWxpemVkIHN0cmVhbSBzdWJqZWN0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VTdHJlYW1UaWNrZXRSZWNvcmQge1xuICBvcmdJZDogc3RyaW5nO1xuICAvKiogTm9ybWFsaXplZCAoZGFzaGVzIHN0cmlwcGVkLCBsb3dlcmNhc2VkKSBzdHJlYW0gc3ViamVjdCBpZC4gKi9cbiAgcmVxdWVzdElkOiBzdHJpbmc7XG59XG5cbi8qKlxuICogUGx1Z2dhYmxlIGJhY2tlbmQgZm9yIFNTRSBsb2ctc3RyZWFtIHRpY2tldHMgQU5EIHN0cmVhbS1vd25lcnNoaXAuXG4gKlxuICogTk9URTogZGlzdGluY3QgZnJvbSBhcGktY29yZSdzIGBTc2VUaWNrZXRTdG9yZWAgKHNlcnZpY2VzL3NzZS10aWNrZXQtc3RvcmUudHMpLFxuICogd2hpY2ggaXMgdGhlIG1lc3NhZ2Utc2VydmljZSB0aWNrZXQgc3RvcmUgKGlzc3VlICsgYWJ1c2UgY291bnRlcnMpLiBUaGlzIG9uZVxuICogKGFwaS1zZXJ2ZXIpIGFkZGl0aW9uYWxseSB0cmFja3Mgc3RyZWFtIG93bmVyc2hpcC4gVGhlIG5hbWVzIGRlbGliZXJhdGVseSBkaWZmZXJcbiAqIChgU1NFU3RyZWFtVGlja2V0U3RvcmVgIGhlcmUgdnMgYFNzZVRpY2tldFN0b3JlYCB0aGVyZSkgc28gY29uc3VtZXJzIGRvbid0IHBpY2tcbiAqIHRoZSB3cm9uZyBvbmUuXG4gKlxuICogVHdvIGNvbmNlcm5zIHNoYXJlIG9uZSBzdG9yZSBiZWNhdXNlIGJvdGggbXVzdCBiZSBjb25zaXN0ZW50IGFjcm9zcyBwb2RzOlxuICogLSAqKlRpY2tldHMqKiDigJQgc2hvcnQtbGl2ZWQsIHNpbmdsZS11c2UsIGJvdW5kIHRvIChvcmcsIHN1YmplY3QpLiBUaGUgZGVmYXVsdFxuICogICBpbi1tZW1vcnkgc3RvcmUgaXMgY29ycmVjdCBvbmx5IHNpbmdsZS1yZXBsaWNhOyB0aGUgUmVkaXMgc3RvcmUgbWFrZXMgdGhlXG4gKiAgIG1pbnQgKG9uZSBwb2QpIC8gcmVkZWVtIChhbm90aGVyIHBvZCkgZXhjaGFuZ2Ugd29yayBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyLlxuICogLSAqKlN0cmVhbSBvd25lcnNoaXAqKiDigJQgd2hpY2ggb3JnIE9XTlMgYSBnaXZlbiBzdHJlYW0gc3ViamVjdC4gVGhlIHN0cmVhbVxuICogICBwcm9kdWNlciAocGxhdGZvcm0sIGEgZGlmZmVyZW50IHNlcnZpY2UpIHJlY29yZHMgdGhpcyBzbyB0aWNrZXQgbWludGluZyBjYW5cbiAqICAgYXNzZXJ0IHRoZSBjYWxsZXIncyBvcmcgb3ducyB0aGUgc3ViamVjdCwgY2xvc2luZyB0aGUgY3Jvc3MtdGVuYW50IGF0dGFjaFxuICogICBnYXAgd2hlcmUgYW55IG9yZyBjb3VsZCBtaW50IGEgdGlja2V0IGZvciBhIGd1ZXNzZWQgcmVxdWVzdElkLlxuICpcbiAqIEFsbCBtZXRob2RzIGFyZSBhc3luYyAoUmVkaXMgaXMgYXN5bmMpIGFuZCBiZXN0LWVmZm9ydCBvbiB0aGUgZmFpbHVyZSBzaWRlIHBlclxuICogdGhlaXIgZG9jIOKAlCBhIHN0b3JlIG91dGFnZSBtdXN0IGRlZ3JhZGUgZ3JhY2VmdWxseSwgbmV2ZXIgY3Jhc2ggdGhlIHN0cmVhbS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTU0VTdHJlYW1UaWNrZXRTdG9yZSB7XG4gIC8qKiBQZXJzaXN0IGEgc2luZ2xlLXVzZSB0aWNrZXQgYm91bmQgdG8gYHJlY29yZGAsIGV4cGlyaW5nIGFmdGVyIGB0dGxNc2AuICovXG4gIHB1dCh0aWNrZXRJZDogc3RyaW5nLCByZWNvcmQ6IFNTRVN0cmVhbVRpY2tldFJlY29yZCwgdHRsTXM6IG51bWJlcik6IFByb21pc2U8dm9pZD47XG4gIC8qKiBBdG9taWNhbGx5IGZldGNoLWFuZC1kZWxldGUgYSB0aWNrZXQgKHNpbmdsZS11c2UpLiBOdWxsIGlmIGFic2VudC9leHBpcmVkLiAqL1xuICBjb25zdW1lKHRpY2tldElkOiBzdHJpbmcpOiBQcm9taXNlPFNTRVN0cmVhbVRpY2tldFJlY29yZCB8IG51bGw+O1xuICAvKiogQ291bnQgb2YgbGl2ZSAodW5leHBpcmVkKSB0aWNrZXRzIGhlbGQgZm9yIGFuIG9yZyAocGVyLW9yZyBjYXApLiAqL1xuICBjb3VudEZvck9yZyhvcmdJZDogc3RyaW5nKTogUHJvbWlzZTxudW1iZXI+O1xuICAvKiogQ291bnQgb2YgYWxsIGxpdmUgdGlja2V0cyBhY3Jvc3MgdGhlIHN0b3JlIChjYXBhY2l0eSBjYXApLiAqL1xuICB0b3RhbCgpOiBQcm9taXNlPG51bWJlcj47XG4gIC8qKiBSZWNvcmQgdGhlIG9yZyB0aGF0IE9XTlMgYSBzdHJlYW0gc3ViamVjdCAocHJvZHVjZXItc2lkZSksIFRUTCBgdHRsTXNgLiAqL1xuICBiaW5kU3RyZWFtT3duZXIocmVxdWVzdElkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcsIHR0bE1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+O1xuICAvKiogVGhlIG93bmluZyBvcmcgb2YgYSBzdHJlYW0gc3ViamVjdCwgb3IgbnVsbCB3aGVuIG5vbmUgaXMgYm91bmQuICovXG4gIGdldFN0cmVhbU93bmVyKHJlcXVlc3RJZDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbiAgLyoqIE9wdGlvbmFsIGh5Z2llbmUgc3dlZXAgb2YgZXhwaXJlZCBlbnRyaWVzIChSZWRpcyBUVEwgbWFrZXMgdGhpcyBhIG5vLW9wKS4gKi9cbiAgc3dlZXA/KCk6IFByb21pc2U8dm9pZD47XG59XG5cbmludGVyZmFjZSBTdG9yZWRUaWNrZXQgZXh0ZW5kcyBTU0VTdHJlYW1UaWNrZXRSZWNvcmQge1xuICBleHBpcmVzQXQ6IG51bWJlcjtcbn1cblxuaW50ZXJmYWNlIFN0b3JlZE93bmVyIHtcbiAgb3JnSWQ6IHN0cmluZztcbiAgZXhwaXJlc0F0OiBudW1iZXI7XG59XG5cbi8qKlxuICogSW4tbWVtb3J5IHRpY2tldCBzdG9yZSDigJQgdGhlIGRlZmF1bHQuIENvcnJlY3QgZm9yIGEgc2luZ2xlIHJlcGxpY2Egb25seTtcbiAqIG1pcnJvcnMgdGhlIGJlaGF2aW9yIHRoZSBTU0VNYW5hZ2VyIHByZXZpb3VzbHkgaW1wbGVtZW50ZWQgaW5saW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlTWVtb3J5VGlja2V0U3RvcmUoKTogU1NFU3RyZWFtVGlja2V0U3RvcmUge1xuICBjb25zdCB0aWNrZXRzID0gbmV3IE1hcDxzdHJpbmcsIFN0b3JlZFRpY2tldD4oKTtcbiAgY29uc3Qgb3duZXJzID0gbmV3IE1hcDxzdHJpbmcsIFN0b3JlZE93bmVyPigpO1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgcHV0KHRpY2tldElkLCByZWNvcmQsIHR0bE1zKSB7XG4gICAgICB0aWNrZXRzLnNldCh0aWNrZXRJZCwgeyAuLi5yZWNvcmQsIGV4cGlyZXNBdDogRGF0ZS5ub3coKSArIHR0bE1zIH0pO1xuICAgIH0sXG4gICAgYXN5bmMgY29uc3VtZSh0aWNrZXRJZCkge1xuICAgICAgY29uc3QgdCA9IHRpY2tldHMuZ2V0KHRpY2tldElkKTtcbiAgICAgIHRpY2tldHMuZGVsZXRlKHRpY2tldElkKTsgLy8gc2luZ2xlLXVzZSDigJQgcmVtb3ZlIHdoZXRoZXIgb3Igbm90IHZhbGlkXG4gICAgICBpZiAoIXQgfHwgRGF0ZS5ub3coKSA+IHQuZXhwaXJlc0F0KSByZXR1cm4gbnVsbDtcbiAgICAgIHJldHVybiB7IG9yZ0lkOiB0Lm9yZ0lkLCByZXF1ZXN0SWQ6IHQucmVxdWVzdElkIH07XG4gICAgfSxcbiAgICBhc3luYyBjb3VudEZvck9yZyhvcmdJZCkge1xuICAgICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICAgIGxldCBuID0gMDtcbiAgICAgIGZvciAoY29uc3QgdCBvZiB0aWNrZXRzLnZhbHVlcygpKSBpZiAodC5vcmdJZCA9PT0gb3JnSWQgJiYgdC5leHBpcmVzQXQgPiBub3cpIG4rKztcbiAgICAgIHJldHVybiBuO1xuICAgIH0sXG4gICAgYXN5bmMgdG90YWwoKSB7XG4gICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgICAgbGV0IG4gPSAwO1xuICAgICAgZm9yIChjb25zdCB0IG9mIHRpY2tldHMudmFsdWVzKCkpIGlmICh0LmV4cGlyZXNBdCA+IG5vdykgbisrO1xuICAgICAgcmV0dXJuIG47XG4gICAgfSxcbiAgICBhc3luYyBiaW5kU3RyZWFtT3duZXIocmVxdWVzdElkLCBvcmdJZCwgdHRsTXMpIHtcbiAgICAgIG93bmVycy5zZXQocmVxdWVzdElkLCB7IG9yZ0lkLCBleHBpcmVzQXQ6IERhdGUubm93KCkgKyB0dGxNcyB9KTtcbiAgICB9LFxuICAgIGFzeW5jIGdldFN0cmVhbU93bmVyKHJlcXVlc3RJZCkge1xuICAgICAgY29uc3QgbyA9IG93bmVycy5nZXQocmVxdWVzdElkKTtcbiAgICAgIGlmICghbykgcmV0dXJuIG51bGw7XG4gICAgICBpZiAoRGF0ZS5ub3coKSA+IG8uZXhwaXJlc0F0KSB7IG93bmVycy5kZWxldGUocmVxdWVzdElkKTsgcmV0dXJuIG51bGw7IH1cbiAgICAgIHJldHVybiBvLm9yZ0lkO1xuICAgIH0sXG4gICAgYXN5bmMgc3dlZXAoKSB7XG4gICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgICAgZm9yIChjb25zdCBbaWQsIHRdIG9mIHRpY2tldHMpIGlmIChub3cgPiB0LmV4cGlyZXNBdCkgdGlja2V0cy5kZWxldGUoaWQpO1xuICAgICAgZm9yIChjb25zdCBbaWQsIG9dIG9mIG93bmVycykgaWYgKG5vdyA+IG8uZXhwaXJlc0F0KSBvd25lcnMuZGVsZXRlKGlkKTtcbiAgICB9LFxuICB9O1xufVxuXG4vKipcbiAqIE1pbmltYWwgaW9yZWRpcyBzdXJmYWNlIHRoZSBSZWRpcyB0aWNrZXQgc3RvcmUgbmVlZHMuIGBzZXRgIHRha2VzIHRoZSB2YXJpYWRpY1xuICogb3B0aW9uIHRhaWwgKGBFWCA8cz5gKTsgYHphZGRgL2B6cmVtcmFuZ2VieXNjb3JlYC9gemNhcmRgIGJhY2sgdGhlIGNhcCBjb3VudGVycy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZWRpc1RpY2tldENsaWVudCB7XG4gIGdldChrZXk6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIHNldChrZXk6IHN0cmluZywgdmFsdWU6IHN0cmluZywgLi4uYXJnczogKHN0cmluZyB8IG51bWJlcilbXSk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG4gIGRlbCguLi5rZXlzOiBzdHJpbmdbXSk6IFByb21pc2U8bnVtYmVyPjtcbiAgemFkZChrZXk6IHN0cmluZywgc2NvcmU6IG51bWJlciwgbWVtYmVyOiBzdHJpbmcpOiBQcm9taXNlPG51bWJlcj47XG4gIHpyZW0oa2V5OiBzdHJpbmcsIC4uLm1lbWJlcnM6IHN0cmluZ1tdKTogUHJvbWlzZTxudW1iZXI+O1xuICB6Y2FyZChrZXk6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcbiAgenJlbXJhbmdlYnlzY29yZShrZXk6IHN0cmluZywgbWluOiBudW1iZXIgfCBzdHJpbmcsIG1heDogbnVtYmVyIHwgc3RyaW5nKTogUHJvbWlzZTxudW1iZXI+O1xuICBleHBpcmUoa2V5OiBzdHJpbmcsIHNlY29uZHM6IG51bWJlcik6IFByb21pc2U8bnVtYmVyPjtcbn1cblxuY29uc3QgVElDS0VUX1BSRUZJWCA9ICdzc2U6dGt0Oic7XG5jb25zdCBPUkdfWlNFVF9QUkVGSVggPSAnc3NlOnRrdDpvcmc6JztcbmNvbnN0IEFMTF9aU0VUID0gJ3NzZTp0a3Q6YWxsJztcbmNvbnN0IE9XTkVSX1BSRUZJWCA9ICdzc2U6b3duZXI6JztcblxuLyoqXG4gKiBSZWRpcy1iYWNrZWQgdGlja2V0IHN0b3JlIOKAlCByZXF1aXJlZCBmb3IgbXVsdGktcG9kIGNvcnJlY3RuZXNzLiBUaWNrZXRzIGFyZVxuICogcGVyLWtleSB3aXRoIG5hdGl2ZSBUVEw7IGNhcCBjb3VudGVycyBhcmUgWlNFVHMgc2NvcmVkIGJ5IGV4cGlyeSAocHJ1bmVkIG9uXG4gKiByZWFkKS4gU3RyZWFtIG93bmVyc2hpcCBpcyBhIHBlci1zdWJqZWN0IGtleSB3aXRoIGl0cyBvd24gVFRMLiBBbGwgcmVhZHMgYXJlXG4gKiBmYWlsLXNhZmUgKG51bGwgLyAwKSBzbyBhIFJlZGlzIGJsaXAgbmV2ZXIgd2VkZ2VzIHRoZSBzdHJlYW0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWRpc1RpY2tldFN0b3JlKHJlZGlzOiBSZWRpc1RpY2tldENsaWVudCk6IFNTRVN0cmVhbVRpY2tldFN0b3JlIHtcbiAgY29uc3QgdGtleSA9IChpZDogc3RyaW5nKTogc3RyaW5nID0+IGAke1RJQ0tFVF9QUkVGSVh9JHtpZH1gO1xuICBjb25zdCBvcmdLZXkgPSAob3JnSWQ6IHN0cmluZyk6IHN0cmluZyA9PiBgJHtPUkdfWlNFVF9QUkVGSVh9JHtvcmdJZH1gO1xuICBjb25zdCBvd25lcktleSA9IChyZXF1ZXN0SWQ6IHN0cmluZyk6IHN0cmluZyA9PiBgJHtPV05FUl9QUkVGSVh9JHtyZXF1ZXN0SWR9YDtcbiAgY29uc3QgdHRsU2VjID0gKHR0bE1zOiBudW1iZXIpOiBudW1iZXIgPT4gTWF0aC5tYXgoMSwgTWF0aC5jZWlsKHR0bE1zIC8gMTAwMCkpO1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgcHV0KHRpY2tldElkLCByZWNvcmQsIHR0bE1zKSB7XG4gICAgICBjb25zdCBleHBpcmVzQXQgPSBEYXRlLm5vdygpICsgdHRsTXM7XG4gICAgICBjb25zdCBzZWMgPSB0dGxTZWModHRsTXMpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgcmVkaXMuc2V0KHRrZXkodGlja2V0SWQpLCBKU09OLnN0cmluZ2lmeShyZWNvcmQpLCAnRVgnLCBzZWMpO1xuICAgICAgICAvLyBUcmFjayBmb3IgdGhlIGNhcHMuIFNjb3JlID0gZXhwaXJ5IHNvIHBydW5pbmcgaXMgYSByYW5nZSBkZWxldGUuXG4gICAgICAgIGF3YWl0IHJlZGlzLnphZGQob3JnS2V5KHJlY29yZC5vcmdJZCksIGV4cGlyZXNBdCwgdGlja2V0SWQpO1xuICAgICAgICBhd2FpdCByZWRpcy56YWRkKEFMTF9aU0VULCBleHBpcmVzQXQsIHRpY2tldElkKTtcbiAgICAgICAgLy8gQm91bmQgdGhlIHBlci1vcmcgWlNFVCdzIG93biBsaWZldGltZSBzbyBhbiBpZGxlIG9yZydzIGtleSBjYW4ndCBsZWFrLlxuICAgICAgICBhd2FpdCByZWRpcy5leHBpcmUob3JnS2V5KHJlY29yZC5vcmdJZCksIHNlYyk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1NTRSB0aWNrZXQgcHV0IGZhaWxlZCcsIHsgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSB9KTtcbiAgICAgIH1cbiAgICB9LFxuICAgIGFzeW5jIGNvbnN1bWUodGlja2V0SWQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJhdyA9IGF3YWl0IHJlZGlzLmdldCh0a2V5KHRpY2tldElkKSk7XG4gICAgICAgIC8vIFNpbmdsZS11c2U6IGRlbGV0ZSByZWdhcmRsZXNzLiBBIGNvbmN1cnJlbnQgZG91YmxlLXJlZGVlbTogYXQgbW9zdCBvbmVcbiAgICAgICAgLy8gc2F3IGEgbm9uLW51bGwgdmFsdWUgYmVmb3JlIHRoZSBkZWxldGU7IHRoZSBaUkVNIGNsZWFucyB0aGUgY291bnRlcnMuXG4gICAgICAgIGF3YWl0IHJlZGlzLmRlbCh0a2V5KHRpY2tldElkKSk7XG4gICAgICAgIGlmICghcmF3KSByZXR1cm4gbnVsbDtcbiAgICAgICAgY29uc3QgcmVjID0gSlNPTi5wYXJzZShyYXcpIGFzIFNTRVN0cmVhbVRpY2tldFJlY29yZDtcbiAgICAgICAgYXdhaXQgcmVkaXMuenJlbShvcmdLZXkocmVjLm9yZ0lkKSwgdGlja2V0SWQpO1xuICAgICAgICBhd2FpdCByZWRpcy56cmVtKEFMTF9aU0VULCB0aWNrZXRJZCk7XG4gICAgICAgIHJldHVybiByZWM7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1NTRSB0aWNrZXQgY29uc3VtZSBmYWlsZWQnLCB7IGVycm9yOiBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycikgfSk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgIH0sXG4gICAgYXN5bmMgY291bnRGb3JPcmcob3JnSWQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHJlZGlzLnpyZW1yYW5nZWJ5c2NvcmUob3JnS2V5KG9yZ0lkKSwgMCwgRGF0ZS5ub3coKSk7XG4gICAgICAgIHJldHVybiBhd2FpdCByZWRpcy56Y2FyZChvcmdLZXkob3JnSWQpKTtcbiAgICAgIH0gY2F0Y2ggeyByZXR1cm4gMDsgfVxuICAgIH0sXG4gICAgYXN5bmMgdG90YWwoKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCByZWRpcy56cmVtcmFuZ2VieXNjb3JlKEFMTF9aU0VULCAwLCBEYXRlLm5vdygpKTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHJlZGlzLnpjYXJkKEFMTF9aU0VUKTtcbiAgICAgIH0gY2F0Y2ggeyByZXR1cm4gMDsgfVxuICAgIH0sXG4gICAgYXN5bmMgYmluZFN0cmVhbU93bmVyKHJlcXVlc3RJZCwgb3JnSWQsIHR0bE1zKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCByZWRpcy5zZXQob3duZXJLZXkocmVxdWVzdElkKSwgb3JnSWQsICdFWCcsIHR0bFNlYyh0dGxNcykpO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdTU0Ugc3RyZWFtLW93bmVyIGJpbmQgZmFpbGVkJywgeyBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpIH0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgYXN5bmMgZ2V0U3RyZWFtT3duZXIocmVxdWVzdElkKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gYXdhaXQgcmVkaXMuZ2V0KG93bmVyS2V5KHJlcXVlc3RJZCkpO1xuICAgICAgfSBjYXRjaCB7IHJldHVybiBudWxsOyB9XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIFJlZGlzLWJhY2tlZCB0aWNrZXQgc3RvcmUgZnJvbSB0aGUgc2hhcmVkIGVudiBSZWRpcyAoc2FtZSB3aXJpbmcgYXNcbiAqIHRoZSByYXRlLWxpbWl0ZXIgLyBhdWRpdC1zcG9vbCkuIFJldHVybnMgbnVsbCB3aGVuIFJlZGlzIGlzbid0IGNvbmZpZ3VyZWQgc29cbiAqIHRoZSBjYWxsZXIga2VlcHMgdGhlIGluLW1lbW9yeSBkZWZhdWx0LiBOZXZlciB0aHJvd3MuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVFbnZSZWRpc1RpY2tldFN0b3JlKCk6IFNTRVN0cmVhbVRpY2tldFN0b3JlIHwgbnVsbCB7XG4gIGNvbnN0IGNsaWVudCA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PFJlZGlzVGlja2V0Q2xpZW50Pignc3NlLXRpY2tldCcpO1xuICBpZiAoIWNsaWVudCkgcmV0dXJuIG51bGw7XG4gIGxvZ2dlci5pbmZvKCdSZWRpcyBTU0UgdGlja2V0IHN0b3JlIGluaXRpYWxpemVkJyk7XG4gIHJldHVybiBjcmVhdGVSZWRpc1RpY2tldFN0b3JlKGNsaWVudCk7XG59XG4iXX0=
|