@pipeline-builder/api-core 4.1.2 → 4.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/errors/app-errors.d.ts +0 -4
- package/lib/errors/app-errors.js +1 -8
- package/lib/helpers/restore-helpers.d.ts +3 -3
- package/lib/helpers/restore-helpers.js +4 -4
- package/lib/helpers/sse-helpers.d.ts +8 -1
- package/lib/helpers/sse-helpers.js +11 -3
- package/lib/middleware/auth.d.ts +174 -43
- package/lib/middleware/auth.js +384 -179
- package/lib/middleware/index.d.ts +1 -0
- package/lib/middleware/index.js +2 -1
- package/lib/middleware/route-table.d.ts +104 -0
- package/lib/middleware/route-table.js +230 -0
- package/lib/middleware/step-up.d.ts +48 -16
- package/lib/middleware/step-up.js +74 -36
- package/lib/services/api-key-exchange.d.ts +25 -0
- package/lib/services/api-key-exchange.js +219 -0
- package/lib/services/audit-spool.d.ts +2 -3
- package/lib/services/audit-spool.js +3 -4
- package/lib/services/cache-service.d.ts +65 -21
- package/lib/services/cache-service.js +173 -78
- package/lib/services/compliance-event-subscriber.d.ts +5 -4
- package/lib/services/compliance-event-subscriber.js +8 -6
- package/lib/services/durable-event-bus.d.ts +22 -39
- package/lib/services/durable-event-bus.js +109 -47
- package/lib/services/env-redis.d.ts +114 -35
- package/lib/services/env-redis.js +215 -73
- package/lib/services/http-client.d.ts +10 -15
- package/lib/services/http-client.js +32 -27
- package/lib/services/index.d.ts +3 -0
- package/lib/services/index.js +4 -1
- package/lib/services/jwks-cache.d.ts +102 -0
- package/lib/services/jwks-cache.js +222 -0
- package/lib/services/leader-lock.d.ts +6 -3
- package/lib/services/leader-lock.js +20 -5
- package/lib/services/quota.d.ts +44 -10
- package/lib/services/quota.js +95 -51
- package/lib/services/remote-audit-client.d.ts +1 -1
- package/lib/services/remote-audit-client.js +32 -1
- package/lib/services/scheduler.d.ts +2 -3
- package/lib/services/scheduler.js +2 -2
- package/lib/services/service-boot.d.ts +4 -2
- package/lib/services/service-boot.js +7 -3
- package/lib/services/service-keys.d.ts +83 -0
- package/lib/services/service-keys.js +354 -0
- package/lib/services/sse-ticket-store.d.ts +55 -22
- package/lib/services/sse-ticket-store.js +122 -79
- package/lib/services/token-revocation.d.ts +1 -1
- package/lib/services/token-revocation.js +31 -19
- package/lib/testing/route-coverage.d.ts +64 -0
- package/lib/testing/route-coverage.js +183 -0
- package/lib/testing/service-tokens.d.ts +66 -0
- package/lib/testing/service-tokens.js +137 -0
- package/lib/testing/tier-mock.d.ts +2 -2
- package/lib/testing/tier-mock.js +3 -3
- package/lib/testing/user-tokens.d.ts +75 -0
- package/lib/testing/user-tokens.js +115 -0
- package/lib/types/catalog-metadata.d.ts +0 -2
- package/lib/types/catalog-metadata.js +1 -3
- package/lib/types/common.d.ts +103 -4
- package/lib/types/common.js +11 -1
- package/lib/types/error-codes.d.ts +15 -0
- package/lib/types/error-codes.js +22 -1
- package/lib/types/permissions.d.ts +35 -1
- package/lib/types/permissions.js +70 -2
- package/lib/utils/api-key.d.ts +34 -0
- package/lib/utils/api-key.js +75 -0
- package/lib/utils/env.d.ts +0 -2
- package/lib/utils/env.js +1 -6
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.js +4 -1
- package/lib/utils/jwk.d.ts +98 -0
- package/lib/utils/jwk.js +139 -0
- package/lib/utils/logger.d.ts +4 -1
- package/lib/utils/logger.js +1 -22
- package/lib/utils/secret-encryption.d.ts +7 -2
- package/lib/utils/secret-encryption.js +47 -4
- package/lib/utils/secret-rotation.d.ts +17 -0
- package/lib/utils/secret-rotation.js +54 -0
- package/lib/validation/common-schemas.js +5 -4
- package/package.json +20 -1
|
@@ -1,48 +1,59 @@
|
|
|
1
1
|
// Copyright 2026 Pipeline Builder Contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { verifyUserJwt, isServiceAccountPrincipal, isServicePrincipal } from './auth.js';
|
|
4
|
+
import { tagRouteGate } from './route-table.js';
|
|
5
|
+
import { createEnvRedisClient, createRedisReadyGate } from '../services/env-redis.js';
|
|
5
6
|
import { getHeaderString } from '../utils/headers.js';
|
|
6
7
|
import { createLogger } from '../utils/logger.js';
|
|
7
8
|
import { sendError } from '../utils/response.js';
|
|
8
9
|
const logger = createLogger('step-up');
|
|
9
10
|
const HEADER = 'x-step-up-token';
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* audience when configured, AND the `type: 'step-up'` + `jti` claims — a plain
|
|
14
|
-
* access token shares the secret and `sub`, so without asserting the step-up
|
|
15
|
-
* shape it would bypass the gate. Throws on any failure. Does NOT bind to a
|
|
16
|
-
* caller — `requireStepUp` does the `sub` match and single-use consume.
|
|
12
|
+
* The factors that count as a SECOND FACTOR, for routes that may only be
|
|
13
|
+
* unlocked by one (#8: impersonation, KMS, IdP settings, platform admin).
|
|
17
14
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* its previous-secret retry had no expiry/not-before carve-out, so an EXPIRED
|
|
23
|
-
* token was retried and surfaced as a signature error instead of an expiry one.
|
|
15
|
+
* A password or a provider re-auth proves the same single factor the session was
|
|
16
|
+
* already opened with, so on those routes they are not enough: the point is that
|
|
17
|
+
* someone holding only a stolen session — or only a stolen password — cannot
|
|
18
|
+
* proceed.
|
|
24
19
|
*/
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
export const STRONG_STEP_UP_METHODS = ['webauthn', 'totp'];
|
|
21
|
+
/**
|
|
22
|
+
* Verify a step-up token: a valid ES256 signature from one of platform's
|
|
23
|
+
* published signing keys, issuer and audience when configured, AND the
|
|
24
|
+
* `type: 'step-up'` + `jti` claims — a plain access token is signed by the same
|
|
25
|
+
* key and carries the same `sub`, so without asserting the step-up shape it
|
|
26
|
+
* would bypass the gate. Throws on any failure (an unfetchable key set included:
|
|
27
|
+
* an unverifiable token is not a step-up). Does NOT bind to a caller —
|
|
28
|
+
* `requireStepUp` does the `sub` match and single-use consume.
|
|
29
|
+
*
|
|
30
|
+
* Delegates to `requireAuth`'s own primitive (`verifyUserJwt`) rather than
|
|
31
|
+
* re-implementing it. The hand-rolled copy this replaced dropped TWO of its
|
|
32
|
+
* guards: it never pinned issuer/audience, and its previous-secret retry had no
|
|
33
|
+
* expiry/not-before carve-out, so an EXPIRED token surfaced as a signature error.
|
|
34
|
+
*/
|
|
35
|
+
export async function verifyStepUpToken(token) {
|
|
36
|
+
const payload = await verifyUserJwt(token);
|
|
29
37
|
if (payload.type !== 'step-up' || !payload.jti || !payload.sub) {
|
|
30
38
|
throw new Error('INVALID_STEP_UP_TOKEN');
|
|
31
39
|
}
|
|
32
40
|
return payload;
|
|
33
41
|
}
|
|
34
|
-
// -- Single-use jti store -----------------------------------------------------
|
|
35
|
-
// Redis SET NX EX when Redis is configured (cross-instance single-use); else a
|
|
36
|
-
// bounded process-local map with TTL sweep (per-process). A step-up token is
|
|
37
|
-
// 60s-lived. When Redis IS configured it's the authoritative store: a Redis
|
|
38
|
-
// error FAILS CLOSED (throws → STEP_UP_INVALID, user re-verifies) rather than
|
|
39
|
-
// degrading to the per-instance mem guard, which would accept a cross-instance
|
|
40
|
-
// replay during the outage. The mem guard is used ONLY when Redis is absent.
|
|
41
42
|
let _redis;
|
|
42
|
-
|
|
43
|
+
let _redisReady;
|
|
44
|
+
/**
|
|
45
|
+
* The jti client, built lazily. The env client has no offline queue, so a SET on
|
|
46
|
+
* a connection that isn't up yet is rejected — which used to fail the FIRST
|
|
47
|
+
* step-up on every pod. Wait (bounded, never rejecting) for readiness before
|
|
48
|
+
* handing it out; a genuinely down Redis still fails closed on the SET below.
|
|
49
|
+
*/
|
|
50
|
+
async function redis() {
|
|
43
51
|
if (_redis === undefined) {
|
|
44
52
|
_redis = createEnvRedisClient('step-up-jti');
|
|
53
|
+
_redisReady = _redis ? createRedisReadyGate(_redis) : undefined;
|
|
45
54
|
}
|
|
55
|
+
if (_redisReady)
|
|
56
|
+
await _redisReady();
|
|
46
57
|
return _redis;
|
|
47
58
|
}
|
|
48
59
|
const memJti = new Map(); // jti -> expiry epoch ms
|
|
@@ -80,7 +91,7 @@ function evictOldestJti() {
|
|
|
80
91
|
*/
|
|
81
92
|
export async function consumeStepUpJti(jti, expEpochSeconds) {
|
|
82
93
|
const ttlSeconds = Math.max(1, expEpochSeconds - Math.floor(Date.now() / 1000));
|
|
83
|
-
const client = redis();
|
|
94
|
+
const client = await redis();
|
|
84
95
|
if (client) {
|
|
85
96
|
// Redis configured ⇒ authoritative cross-instance store. SET NX EX → 'OK'
|
|
86
97
|
// when newly set, null when already consumed. A thrown Redis error is NOT
|
|
@@ -96,23 +107,43 @@ export async function consumeStepUpJti(jti, expEpochSeconds) {
|
|
|
96
107
|
memJti.set(jti, Date.now() + ttlSeconds * 1000);
|
|
97
108
|
return true;
|
|
98
109
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
export function requireStepUp(reqOrOptions, res, next) {
|
|
111
|
+
// The direct form RETURNS the promise (Express ignores it; a test awaits it),
|
|
112
|
+
// so composing the gate as `router.post('/x', requireStepUp, h)` behaves
|
|
113
|
+
// exactly as it did before the options form existed.
|
|
114
|
+
if (res && next && 'headers' in reqOrOptions) {
|
|
115
|
+
return _requireStepUp({}, reqOrOptions, res, next);
|
|
116
|
+
}
|
|
117
|
+
const options = reqOrOptions;
|
|
118
|
+
return tagRouteGate((reqInner, resInner, nextInner) => {
|
|
119
|
+
void _requireStepUp(options, reqInner, resInner, nextInner);
|
|
120
|
+
}, { kind: 'stepUp', ...(options.methods ? { methods: [...options.methods] } : {}) });
|
|
121
|
+
}
|
|
122
|
+
tagRouteGate(requireStepUp, { kind: 'stepUp' });
|
|
123
|
+
async function _requireStepUp(options, req, res, next) {
|
|
104
124
|
// Verified internal service principals are EXEMPT: step-up is a re-verify-the-
|
|
105
125
|
// HUMAN gate (they replay a password-reverify token), which a service token
|
|
106
126
|
// structurally cannot produce. Services already bypass `requirePermission` on
|
|
107
127
|
// the same trust basis (a signed service JWT), and internal callers of a
|
|
108
128
|
// step-up-gated route — e.g. the platform org-cascade's `DELETE /quotas/:orgId`
|
|
109
129
|
// or the billing→quota entitlement sync — would otherwise be hard-blocked.
|
|
110
|
-
// Safe: a service token can only be minted with
|
|
111
|
-
//
|
|
130
|
+
// Safe: a service token can only be minted with a SERVICE's own ES256 key
|
|
131
|
+
// (#14), which no external client holds, so this can't be spoofed to skip
|
|
132
|
+
// step-up.
|
|
112
133
|
if (isServicePrincipal(req)) {
|
|
113
134
|
next();
|
|
114
135
|
return;
|
|
115
136
|
}
|
|
137
|
+
// An ORG SERVICE ACCOUNT is the opposite case: it is subject to every gate a
|
|
138
|
+
// member is, and there is no person behind it to re-verify — so it can NEVER
|
|
139
|
+
// pass step-up. Refuse explicitly (rather than letting it fall through to the
|
|
140
|
+
// `sub`-bound token check, which would fail with a confusing STEP_UP_MISMATCH)
|
|
141
|
+
// so "a machine credential never satisfies an assurance requirement" is a
|
|
142
|
+
// property of this gate, not a side effect of how step-up tokens are minted.
|
|
143
|
+
if (isServiceAccountPrincipal(req)) {
|
|
144
|
+
sendError(res, 403, 'A service account cannot perform this action — it requires a person to re-verify', 'STEP_UP_NOT_AVAILABLE');
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
116
147
|
const callerSub = req.user?.sub;
|
|
117
148
|
if (!callerSub) {
|
|
118
149
|
sendError(res, 401, 'Authentication required', 'UNAUTHORIZED');
|
|
@@ -125,7 +156,7 @@ export async function requireStepUp(req, res, next) {
|
|
|
125
156
|
}
|
|
126
157
|
let payload;
|
|
127
158
|
try {
|
|
128
|
-
payload = verifyStepUpToken(token);
|
|
159
|
+
payload = await verifyStepUpToken(token);
|
|
129
160
|
}
|
|
130
161
|
catch {
|
|
131
162
|
sendError(res, 401, 'Step-up token invalid or expired', 'STEP_UP_INVALID');
|
|
@@ -135,6 +166,13 @@ export async function requireStepUp(req, res, next) {
|
|
|
135
166
|
sendError(res, 401, 'Step-up token does not match session', 'STEP_UP_MISMATCH');
|
|
136
167
|
return;
|
|
137
168
|
}
|
|
169
|
+
// Factor restriction (#8). Checked BEFORE the jti is consumed, so a token
|
|
170
|
+
// earned by the wrong factor can still be spent on a route that accepts it —
|
|
171
|
+
// burning it here would make the refusal destructive as well as confusing.
|
|
172
|
+
if (options.methods && !(payload.method && options.methods.includes(payload.method))) {
|
|
173
|
+
sendError(res, 401, 'This action must be confirmed with a passkey or an authenticator code', 'STEP_UP_METHOD_REQUIRED', { methods: [...options.methods] });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
138
176
|
try {
|
|
139
177
|
if (!(await consumeStepUpJti(payload.jti, payload.exp))) {
|
|
140
178
|
sendError(res, 401, 'Step-up token already used or expired', 'STEP_UP_REPLAY');
|
|
@@ -148,4 +186,4 @@ export async function requireStepUp(req, res, next) {
|
|
|
148
186
|
}
|
|
149
187
|
next();
|
|
150
188
|
}
|
|
151
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC11cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWRkbGV3YXJlL3N0ZXAtdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQW9CdEMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLHFCQUFxQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzdGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN2QyxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQztBQVdqQzs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FBQyxLQUFhO0lBQzdDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVU7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7SUFDNUYsTUFBTSxPQUFPLEdBQUcscUJBQXFCLENBQUMsS0FBSyxFQUFFLHFCQUFxQixFQUFFLENBQWtDLENBQUM7SUFFdkcsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLFNBQVMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDL0QsTUFBTSxJQUFJLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FBRUQsZ0ZBQWdGO0FBQ2hGLCtFQUErRTtBQUMvRSw2RUFBNkU7QUFDN0UsNEVBQTRF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QUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ3hELFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLHVDQUF1QyxFQUFFLGdCQUFnQixDQUFDLENBQUM7WUFDL0UsT0FBTztRQUNULENBQUM7SUFDSCxDQUFDO0lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNiLE1BQU0sQ0FBQyxLQUFLLENBQUMsNkJBQTZCLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNwRSxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSw2QkFBNkIsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1FBQ3RFLE9BQU87SUFDVCxDQUFDO0lBRUQsSUFBSSxFQUFFLENBQUM7QUFDVCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogYHJlcXVpcmVTdGVwVXBgIOKAlCBhIHNoYXJlZCBzdGVwLXVwIChyZWNlbnQtcGFzc3dvcmQtcmV2ZXJpZnkpIGdhdGUgZm9yIHRoZVxuICogc3RhdGVsZXNzIGFwaSBzZXJ2aWNlcywgbWlycm9yaW5nIHRoZSBwbGF0Zm9ybSBtaWRkbGV3YXJlIG9mIHRoZSBzYW1lIG5hbWUuXG4gKlxuICogRmxvdzogdGhlIFVJIGhpdHMgYFBPU1QgL2FwaS9hdXRoL3N0ZXAtdXBgIChwbGF0Zm9ybSkgdG8gcmUtdmVyaWZ5IHRoZSB1c2VyJ3NcbiAqIHBhc3N3b3JkOyBwbGF0Zm9ybSBtaW50cyBhIHNob3J0LWxpdmVkICg2MHMpIGBzdGVwLXVwYCBKV1Qgc2lnbmVkIHdpdGggdGhlXG4gKiBTQU1FIGBKV1RfU0VDUkVUYCB0aGVzZSBzZXJ2aWNlcyBhbHJlYWR5IHZlcmlmeSBhY2Nlc3MgdG9rZW5zIHdpdGguIFRoZSBVSVxuICogcmVwbGF5cyBpdCBhcyB0aGUgYFgtU3RlcC1VcC1Ub2tlbmAgaGVhZGVyIG9uIHRoZSBkZXN0cnVjdGl2ZS9zZW5zaXRpdmUgY2FsbDtcbiAqIHRoaXMgbWlkZGxld2FyZSB2ZXJpZmllcyBpdCwgYmluZHMgaXQgdG8gdGhlIGNhbGxlciAoYHN1YmAgbWF0Y2gpLCBhbmRcbiAqIGNvbnN1bWVzIGl0cyBganRpYCBvbmNlIChSZWRpcy1iYWNrZWQgY3Jvc3MtaW5zdGFuY2Ugd2hlbiBjb25maWd1cmVkLCBlbHNlIGFcbiAqIGJvdW5kZWQgcHJvY2Vzcy1sb2NhbCBzZXQpIHNvIGEgcmVwbGF5IGluc2lkZSB0aGUgVFRMIGlzIHJlamVjdGVkLlxuICpcbiAqIFdIWSBoZXJlOiByZXN0b3JlIGVuZHBvaW50cyBvbiB0aGUgYXBpIHNlcnZpY2VzIChwaXBlbGluZS9wbHVnaW4vLi4uKSBhcmVcbiAqIHN0ZXAtdXAtZ2F0ZWQsIGJ1dCBzdGVwLXVwIHByZXZpb3VzbHkgbGl2ZWQgb25seSBpbiBgcGxhdGZvcm1gLiBUaGlzIHBvcnRzIHRoZVxuICogdmVyaWZ5ICsgc2luZ2xlLXVzZSBqdGkgaW50byBhcGktY29yZSBzbyBhbnkgc2VydmljZSBjYW4gcmVxdWlyZSBpdC5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBOZXh0RnVuY3Rpb24gfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IGJ1aWxkSnd0VmVyaWZ5T3B0aW9ucywgdmVyaWZ5Snd0V2l0aFJvdGF0aW9uLCBpc1NlcnZpY2VQcmluY2lwYWwgfSBmcm9tICcuL2F1dGguanMnO1xuaW1wb3J0IHsgY3JlYXRlRW52UmVkaXNDbGllbnQgfSBmcm9tICcuLi9zZXJ2aWNlcy9lbnYtcmVkaXMuanMnO1xuaW1wb3J0IHsgZ2V0SGVhZGVyU3RyaW5nIH0gZnJvbSAnLi4vdXRpbHMvaGVhZGVycy5qcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgc2VuZEVycm9yIH0gZnJvbSAnLi4vdXRpbHMvcmVzcG9uc2UuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3N0ZXAtdXAnKTtcbmNvbnN0IEhFQURFUiA9ICd4LXN0ZXAtdXAtdG9rZW4nO1xuXG4vKiogUGF5bG9hZCBvZiBhIHBsYXRmb3JtLWlzc3VlZCBzdGVwLXVwIHRva2VuIChzZWUgcGxhdGZvcm0gaXNzdWVTdGVwVXBUb2tlbikuICovXG5leHBvcnQgaW50ZXJmYWNlIFN0ZXBVcFRva2VuUGF5bG9hZCB7XG4gIHR5cGU6ICdzdGVwLXVwJztcbiAgc3ViOiBzdHJpbmc7XG4gIGp0aTogc3RyaW5nO1xuICBpYXQ6IG51bWJlcjtcbiAgZXhwOiBudW1iZXI7XG59XG5cbi8qKlxuICogVmVyaWZ5IGEgc3RlcC11cCB0b2tlbjogdmFsaWQgc2lnbmF0dXJlIChwcmltYXJ5IGBKV1RfU0VDUkVUYCwgb3JcbiAqIGBKV1RfU0VDUkVUX1BSRVZJT1VTYCBkdXJpbmcgYSByb3RhdGlvbiksIGFsbG93LWxpc3RlZCBhbGdvcml0aG0sIGlzc3VlciBhbmRcbiAqIGF1ZGllbmNlIHdoZW4gY29uZmlndXJlZCwgQU5EIHRoZSBgdHlwZTogJ3N0ZXAtdXAnYCArIGBqdGlgIGNsYWltcyDigJQgYSBwbGFpblxuICogYWNjZXNzIHRva2VuIHNoYXJlcyB0aGUgc2VjcmV0IGFuZCBgc3ViYCwgc28gd2l0aG91dCBhc3NlcnRpbmcgdGhlIHN0ZXAtdXBcbiAqIHNoYXBlIGl0IHdvdWxkIGJ5cGFzcyB0aGUgZ2F0ZS4gVGhyb3dzIG9uIGFueSBmYWlsdXJlLiBEb2VzIE5PVCBiaW5kIHRvIGFcbiAqIGNhbGxlciDigJQgYHJlcXVpcmVTdGVwVXBgIGRvZXMgdGhlIGBzdWJgIG1hdGNoIGFuZCBzaW5nbGUtdXNlIGNvbnN1bWUuXG4gKlxuICogRGVsZWdhdGVzIHRvIGByZXF1aXJlQXV0aGAncyBvd24gcHJpbWl0aXZlcyByYXRoZXIgdGhhbiByZS1pbXBsZW1lbnRpbmcgdGhlbS5cbiAqIFRoZSBoYW5kLXJvbGxlZCBjb3B5IHRoaXMgcmVwbGFjZWQgZHJvcHBlZCBUV08gb2YgdGhlaXIgZ3VhcmRzOiBpdCBuZXZlclxuICogcGlubmVkIGlzc3Vlci9hdWRpZW5jZSAoc28gYSBzdGVwLXVwIHRva2VuIGZyb20gYW55IG90aGVyIHN5c3RlbSBzaGFyaW5nXG4gKiBgSldUX1NFQ1JFVGAgd2FzIGFjY2VwdGVkIHdoZXJldmVyIGBKV1RfSVNTVUVSYC9gSldUX0FVRElFTkNFYCBhcmUgc2V0KSwgYW5kXG4gKiBpdHMgcHJldmlvdXMtc2VjcmV0IHJldHJ5IGhhZCBubyBleHBpcnkvbm90LWJlZm9yZSBjYXJ2ZS1vdXQsIHNvIGFuIEVYUElSRURcbiAqIHRva2VuIHdhcyByZXRyaWVkIGFuZCBzdXJmYWNlZCBhcyBhIHNpZ25hdHVyZSBlcnJvciBpbnN0ZWFkIG9mIGFuIGV4cGlyeSBvbmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2ZXJpZnlTdGVwVXBUb2tlbih0b2tlbjogc3RyaW5nKTogU3RlcFVwVG9rZW5QYXlsb2FkIHtcbiAgaWYgKCFwcm9jZXNzLmVudi5KV1RfU0VDUkVUKSB0aHJvdyBuZXcgRXJyb3IoJ0pXVF9TRUNSRVQgZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgcmVxdWlyZWQnKTtcbiAgY29uc3QgcGF5bG9hZCA9IHZlcmlmeUp3dFdpdGhSb3RhdGlvbih0b2tlbiwgYnVpbGRKd3RWZXJpZnlPcHRpb25zKCkpIGFzIHVua25vd24gYXMgU3RlcFVwVG9rZW5QYXlsb2FkO1xuXG4gIGlmIChwYXlsb2FkLnR5cGUgIT09ICdzdGVwLXVwJyB8fCAhcGF5bG9hZC5qdGkgfHwgIXBheWxvYWQuc3ViKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdJTlZBTElEX1NURVBfVVBfVE9LRU4nKTtcbiAgfVxuICByZXR1cm4gcGF5bG9hZDtcbn1cblxuLy8gLS0gU2luZ2xlLXVzZSBqdGkgc3RvcmUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFJlZGlzIFNFVCBOWCBFWCB3aGVuIFJlZGlzIGlzIGNvbmZpZ3VyZWQgKGNyb3NzLWluc3RhbmNlIHNpbmdsZS11c2UpOyBlbHNlIGFcbi8vIGJvdW5kZWQgcHJvY2Vzcy1sb2NhbCBtYXAgd2l0aCBUVEwgc3dlZXAgKHBlci1wcm9jZXNzKS4gQSBzdGVwLXVwIHRva2VuIGlzXG4vLyA2MHMtbGl2ZWQuIFdoZW4gUmVkaXMgSVMgY29uZmlndXJlZCBpdCdzIHRoZSBhdXRob3JpdGF0aXZlIHN0b3JlOiBhIFJlZGlzXG4vLyBlcnJvciBGQUlMUyBDTE9TRUQgKHRocm93cyDihpIgU1RFUF9VUF9JTlZBTElELCB1c2VyIHJlLXZlcmlmaWVzKSByYXRoZXIgdGhhblxuLy8gZGVncmFkaW5nIHRvIHRoZSBwZXItaW5zdGFuY2UgbWVtIGd1YXJkLCB3aGljaCB3b3VsZCBhY2NlcHQgYSBjcm9zcy1pbnN0YW5jZVxuLy8gcmVwbGF5IGR1cmluZyB0aGUgb3V0YWdlLiBUaGUgbWVtIGd1YXJkIGlzIHVzZWQgT05MWSB3aGVuIFJlZGlzIGlzIGFic2VudC5cblxubGV0IF9yZWRpczogeyBzZXQ6ICguLi5hcmdzOiB1bmtub3duW10pID0+IFByb21pc2U8dW5rbm93bj4gfSB8IG51bGwgfCB1bmRlZmluZWQ7XG5mdW5jdGlvbiByZWRpcygpOiB7IHNldDogKC4uLmFyZ3M6IHVua25vd25bXSkgPT4gUHJvbWlzZTx1bmtub3duPiB9IHwgbnVsbCB7XG4gIGlmIChfcmVkaXMgPT09IHVuZGVmaW5lZCkge1xuICAgIF9yZWRpcyA9IGNyZWF0ZUVudlJlZGlzQ2xpZW50PHsgc2V0OiAoLi4uYXJnczogdW5rbm93bltdKSA9PiBQcm9taXNlPHVua25vd24+IH0+KCdzdGVwLXVwLWp0aScpO1xuICB9XG4gIHJldHVybiBfcmVkaXM7XG59XG5cbmNvbnN0IG1lbUp0aSA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7IC8vIGp0aSAtPiBleHBpcnkgZXBvY2ggbXNcbmNvbnN0IG1lbVN3ZWVwID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBmb3IgKGNvbnN0IFtqdGksIGV4cF0gb2YgbWVtSnRpKSBpZiAoZXhwIDw9IG5vdykgbWVtSnRpLmRlbGV0ZShqdGkpO1xufSwgMzBfMDAwKTtcbm1lbVN3ZWVwLnVucmVmPy4oKTtcblxuLyoqXG4gKiBIYXJkIGNlaWxpbmcgb24gdGhlIHByb2Nlc3MtbG9jYWwganRpIHNldC5cbiAqXG4gKiBUaGUgMzBzIHN3ZWVwIGFsb25lIGRvZXMgTk9UIGJvdW5kIGl0OiBlbnRyaWVzIG9ubHkgbGVhdmUgb25jZSB0aGVpciB0b2tlbidzXG4gKiBUVEwgaGFzIHBhc3NlZCwgc28gYSBidXJzdCBvZiBzdGVwLXVwIHRyYWZmaWMgKG9yIGEgZmxvb2Qgb2YgZGlzdGluY3QgZm9yZ2VkXG4gKiBqdGlzIG9uIGEgUmVkaXMtbGVzcyBkZXBsb3ltZW50KSBncm93cyB0aGUgbWFwIGZhc3RlciB0aGFuIHRoZSBzd2VlcCBkcmFpbnNcbiAqIGl0LiBUaGlzIGlzIGEgcmVwbGF5IGd1YXJkLCBzbyBzaGVkZGluZyB0aGUgT0xERVNUIGVudHJpZXMgaXMgdGhlIHNhZmVcbiAqIGRpcmVjdGlvbjogdGhlIHdvcnN0IGNhc2UgaXMgdGhhdCBhIGxvbmctZXhwaXJlZCBqdGkgY291bGQgYmUgcmVwbGF5ZWQsIGFuZFxuICogZXhwaXJ5IGlzIGVuZm9yY2VkIGluZGVwZW5kZW50bHkgYnkgdGhlIHRva2VuJ3Mgb3duIGBleHBgLlxuICovXG5jb25zdCBNRU1fSlRJX01BWCA9IDEwXzAwMDtcblxuLyoqIERyb3AgdGhlIHNvb25lc3QtdG8tZXhwaXJlIGVudHJpZXMgdW50aWwgdGhlIG1hcCBpcyB1bmRlciB0aGUgY2VpbGluZy4gKi9cbmZ1bmN0aW9uIGV2aWN0T2xkZXN0SnRpKCk6IHZvaWQge1xuICBpZiAobWVtSnRpLnNpemUgPCBNRU1fSlRJX01BWCkgcmV0dXJuO1xuICBjb25zdCBieUV4cGlyeSA9IFsuLi5tZW1KdGkuZW50cmllcygpXS5zb3J0KChhLCBiKSA9PiBhWzFdIC0gYlsxXSk7XG4gIGNvbnN0IGV4Y2VzcyA9IG1lbUp0aS5zaXplIC0gTUVNX0pUSV9NQVggKyAxO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGV4Y2VzczsgaSArPSAxKSBtZW1KdGkuZGVsZXRlKGJ5RXhwaXJ5W2ldWzBdKTtcbn1cblxuLyoqXG4gKiBDb25zdW1lIGEgc3RlcC11cCBganRpYCBleGFjdGx5IG9uY2UuIFJldHVybnMgdHJ1ZSBpZiB0aGlzIGNhbGwgY2xhaW1lZCBpdFxuICogKGZpcnN0IHVzZSksIGZhbHNlIGlmIGl0IHdhcyBhbHJlYWR5IGNvbnN1bWVkIChyZXBsYXkpLiBgZXhwRXBvY2hTZWNvbmRzYCBpc1xuICogdGhlIHRva2VuJ3MgYGV4cGAsIHNvIHRoZSBtYXJrZXIgYXV0by1leHBpcmVzIHdpdGggdGhlIHRva2VuLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY29uc3VtZVN0ZXBVcEp0aShqdGk6IHN0cmluZywgZXhwRXBvY2hTZWNvbmRzOiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3QgdHRsU2Vjb25kcyA9IE1hdGgubWF4KDEsIGV4cEVwb2NoU2Vjb25kcyAtIE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApKTtcbiAgY29uc3QgY2xpZW50ID0gcmVkaXMoKTtcbiAgaWYgKGNsaWVudCkge1xuICAgIC8vIFJlZGlzIGNvbmZpZ3VyZWQg4oeSIGF1dGhvcml0YXRpdmUgY3Jvc3MtaW5zdGFuY2Ugc3RvcmUuIFNFVCBOWCBFWCDihpIgJ09LJ1xuICAgIC8vIHdoZW4gbmV3bHkgc2V0LCBudWxsIHdoZW4gYWxyZWFkeSBjb25zdW1lZC4gQSB0aHJvd24gUmVkaXMgZXJyb3IgaXMgTk9UXG4gICAgLy8gY2F1Z2h0IGhlcmU6IGl0IHByb3BhZ2F0ZXMgc28gYHJlcXVpcmVTdGVwVXBgIGZhaWxzIGNsb3NlZCAoU1RFUF9VUF9JTlZBTElEKVxuICAgIC8vIGluc3RlYWQgb2Ygc2lsZW50bHkgZGVncmFkaW5nIHRvIHRoZSBwZXItaW5zdGFuY2UgbWVtIGd1YXJkLlxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5zZXQoYHN0ZXB1cDpqdGk6JHtqdGl9YCwgJzEnLCAnRVgnLCB0dGxTZWNvbmRzLCAnTlgnKTtcbiAgICByZXR1cm4gcmVzID09PSAnT0snO1xuICB9XG4gIC8vIE5vIFJlZGlzIOKHkiBzaW5nbGUtaW5zdGFuY2UgZGVwbG95bWVudDsgdGhlIHByb2Nlc3MtbG9jYWwgbWFwIElTIHRoZSBzdG9yZS5cbiAgaWYgKG1lbUp0aS5oYXMoanRpKSkgcmV0dXJuIGZhbHNlO1xuICBldmljdE9sZGVzdEp0aSgpO1xuICBtZW1KdGkuc2V0KGp0aSwgRGF0ZS5ub3coKSArIHR0bFNlY29uZHMgKiAxMDAwKTtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogRXhwcmVzcyBtaWRkbGV3YXJlOiByZXF1aXJlIGEgdmFsaWQsIGNhbGxlci1ib3VuZCwgc2luZ2xlLXVzZSBzdGVwLXVwIHRva2VuIGluXG4gKiB0aGUgYFgtU3RlcC1VcC1Ub2tlbmAgaGVhZGVyLiBNdXN0IHJ1biBBRlRFUiBgcmVxdWlyZUF1dGhgIChuZWVkcyBgcmVxLnVzZXJgKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlcXVpcmVTdGVwVXAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgLy8gVmVyaWZpZWQgaW50ZXJuYWwgc2VydmljZSBwcmluY2lwYWxzIGFyZSBFWEVNUFQ6IHN0ZXAtdXAgaXMgYSByZS12ZXJpZnktdGhlLVxuICAvLyBIVU1BTiBnYXRlICh0aGV5IHJlcGxheSBhIHBhc3N3b3JkLXJldmVyaWZ5IHRva2VuKSwgd2hpY2ggYSBzZXJ2aWNlIHRva2VuXG4gIC8vIHN0cnVjdHVyYWxseSBjYW5ub3QgcHJvZHVjZS4gU2VydmljZXMgYWxyZWFkeSBieXBhc3MgYHJlcXVpcmVQZXJtaXNzaW9uYCBvblxuICAvLyB0aGUgc2FtZSB0cnVzdCBiYXNpcyAoYSBzaWduZWQgc2VydmljZSBKV1QpLCBhbmQgaW50ZXJuYWwgY2FsbGVycyBvZiBhXG4gIC8vIHN0ZXAtdXAtZ2F0ZWQgcm91dGUg4oCUIGUuZy4gdGhlIHBsYXRmb3JtIG9yZy1jYXNjYWRlJ3MgYERFTEVURSAvcXVvdGFzLzpvcmdJZGBcbiAgLy8gb3IgdGhlIGJpbGxpbmfihpJxdW90YSBlbnRpdGxlbWVudCBzeW5jIOKAlCB3b3VsZCBvdGhlcndpc2UgYmUgaGFyZC1ibG9ja2VkLlxuICAvLyBTYWZlOiBhIHNlcnZpY2UgdG9rZW4gY2FuIG9ubHkgYmUgbWludGVkIHdpdGggYEpXVF9TRUNSRVRgLCBzbyB0aGlzIGNhbid0IGJlXG4gIC8vIHNwb29mZWQgYnkgYW4gZXh0ZXJuYWwgY2xpZW50IHRvIHNraXAgc3RlcC11cC5cbiAgaWYgKGlzU2VydmljZVByaW5jaXBhbChyZXEpKSB7XG4gICAgbmV4dCgpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGNhbGxlclN1YiA9IChyZXEgYXMgUmVxdWVzdCAmIHsgdXNlcj86IHsgc3ViPzogc3RyaW5nIH0gfSkudXNlcj8uc3ViO1xuICBpZiAoIWNhbGxlclN1Yikge1xuICAgIHNlbmRFcnJvcihyZXMsIDQwMSwgJ0F1dGhlbnRpY2F0aW9uIHJlcXVpcmVkJywgJ1VOQVVUSE9SSVpFRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHRva2VuID0gZ2V0SGVhZGVyU3RyaW5nKHJlcS5oZWFkZXJzW0hFQURFUl0pO1xuICBpZiAoIXRva2VuKSB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB2ZXJpZmljYXRpb24gcmVxdWlyZWQnLCAnU1RFUF9VUF9SRVFVSVJFRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGxldCBwYXlsb2FkOiBTdGVwVXBUb2tlblBheWxvYWQ7XG4gIHRyeSB7XG4gICAgcGF5bG9hZCA9IHZlcmlmeVN0ZXBVcFRva2VuKHRva2VuKTtcbiAgfSBjYXRjaCB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBpbnZhbGlkIG9yIGV4cGlyZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgaWYgKHBheWxvYWQuc3ViICE9PSBjYWxsZXJTdWIpIHtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHRva2VuIGRvZXMgbm90IG1hdGNoIHNlc3Npb24nLCAnU1RFUF9VUF9NSVNNQVRDSCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgaWYgKCEoYXdhaXQgY29uc3VtZVN0ZXBVcEp0aShwYXlsb2FkLmp0aSwgcGF5bG9hZC5leHApKSkge1xuICAgICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBhbHJlYWR5IHVzZWQgb3IgZXhwaXJlZCcsICdTVEVQX1VQX1JFUExBWScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nZ2VyLmVycm9yKCdTdGVwLXVwIGp0aSBjb25zdW1lIGVycm9yZWQnLCB7IGVycm9yOiBTdHJpbmcoZXJyKSB9KTtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHZlcmlmaWNhdGlvbiBmYWlsZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgbmV4dCgpO1xufVxuIl19
|
|
189
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RlcC11cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWRkbGV3YXJlL3N0ZXAtdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQXFCdEMsT0FBTyxFQUFFLGFBQWEsRUFBRSx5QkFBeUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUN6RixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDaEQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLG9CQUFvQixFQUF3QixNQUFNLDBCQUEwQixDQUFDO0FBQzVHLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN2QyxNQUFNLE1BQU0sR0FBRyxpQkFBaUIsQ0FBQztBQVNqQzs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUE0QixDQUFDLFVBQVUsRUFBRSxNQUFNLENBQUMsQ0FBQztBQXFCcEY7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsaUJBQWlCLENBQUMsS0FBYTtJQUNuRCxNQUFNLE9BQU8sR0FBRyxNQUFNLGFBQWEsQ0FBcUIsS0FBSyxDQUFDLENBQUM7SUFFL0QsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLFNBQVMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDL0QsTUFBTSxJQUFJLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDO0FB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RTtJQUMxRSw2RUFBNkU7SUFDN0UsMkVBQTJFO0lBQzNFLElBQUksT0FBTyxDQUFDLE9BQU8sSUFBSSxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3JGLFNBQVMsQ0FDUCxHQUFHLEVBQUUsR0FBRyxFQUNSLHVFQUF1RSxFQUN2RSx5QkFBeUIsRUFDekIsRUFBRSxPQUFPLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUNsQyxDQUFDO1FBQ0YsT0FBTztJQUNULENBQUM7SUFFRCxJQUFJLENBQUM7UUFDSCxJQUFJLENBQUMsQ0FBQyxNQUFNLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN4RCxTQUFTLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSx1Q0FBdUMsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1lBQy9FLE9BQU87UUFDVCxDQUFDO0lBQ0gsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDcEUsU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsNkJBQTZCLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztRQUN0RSxPQUFPO0lBQ1QsQ0FBQztJQUVELElBQUksRUFBRSxDQUFDO0FBQ1QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIGByZXF1aXJlU3RlcFVwYCDigJQgYSBzaGFyZWQgc3RlcC11cCAocmVjZW50LXBhc3N3b3JkLXJldmVyaWZ5KSBnYXRlIGZvciB0aGVcbiAqIHN0YXRlbGVzcyBhcGkgc2VydmljZXMsIG1pcnJvcmluZyB0aGUgcGxhdGZvcm0gbWlkZGxld2FyZSBvZiB0aGUgc2FtZSBuYW1lLlxuICpcbiAqIEZsb3c6IHRoZSBVSSBoaXRzIGBQT1NUIC9hcGkvYXV0aC9zdGVwLXVwYCAocGxhdGZvcm0pIHRvIHJlLXZlcmlmeSB0aGUgdXNlcjtcbiAqIHBsYXRmb3JtIG1pbnRzIGEgc2hvcnQtbGl2ZWQgKDYwcykgYHN0ZXAtdXBgIEpXVCB3aXRoIHRoZSBTQU1FIEVTMjU2IHNpZ25pbmdcbiAqIGtleSBpdCBtaW50cyBhY2Nlc3MgdG9rZW5zIHdpdGgsIHNvIHRoZXNlIHNlcnZpY2VzIHZlcmlmeSBpdCB0aHJvdWdoIHRoZSBzYW1lXG4gKiBKV0tTLiBUaGUgVUlcbiAqIHJlcGxheXMgaXQgYXMgdGhlIGBYLVN0ZXAtVXAtVG9rZW5gIGhlYWRlciBvbiB0aGUgZGVzdHJ1Y3RpdmUvc2Vuc2l0aXZlIGNhbGw7XG4gKiB0aGlzIG1pZGRsZXdhcmUgdmVyaWZpZXMgaXQsIGJpbmRzIGl0IHRvIHRoZSBjYWxsZXIgKGBzdWJgIG1hdGNoKSwgYW5kXG4gKiBjb25zdW1lcyBpdHMgYGp0aWAgb25jZSAoUmVkaXMtYmFja2VkIGNyb3NzLWluc3RhbmNlIHdoZW4gY29uZmlndXJlZCwgZWxzZSBhXG4gKiBib3VuZGVkIHByb2Nlc3MtbG9jYWwgc2V0KSBzbyBhIHJlcGxheSBpbnNpZGUgdGhlIFRUTCBpcyByZWplY3RlZC5cbiAqXG4gKiBXSFkgaGVyZTogcmVzdG9yZSBlbmRwb2ludHMgb24gdGhlIGFwaSBzZXJ2aWNlcyAocGlwZWxpbmUvcGx1Z2luLy4uLikgYXJlXG4gKiBzdGVwLXVwLWdhdGVkLCBidXQgc3RlcC11cCBwcmV2aW91c2x5IGxpdmVkIG9ubHkgaW4gYHBsYXRmb3JtYC4gVGhpcyBwb3J0cyB0aGVcbiAqIHZlcmlmeSArIHNpbmdsZS11c2UganRpIGludG8gYXBpLWNvcmUgc28gYW55IHNlcnZpY2UgY2FuIHJlcXVpcmUgaXQuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyB2ZXJpZnlVc2VySnd0LCBpc1NlcnZpY2VBY2NvdW50UHJpbmNpcGFsLCBpc1NlcnZpY2VQcmluY2lwYWwgfSBmcm9tICcuL2F1dGguanMnO1xuaW1wb3J0IHsgdGFnUm91dGVHYXRlIH0gZnJvbSAnLi9yb3V0ZS10YWJsZS5qcyc7XG5pbXBvcnQgeyBjcmVhdGVFbnZSZWRpc0NsaWVudCwgY3JlYXRlUmVkaXNSZWFkeUdhdGUsIHR5cGUgUmVhZHlBd2FyZVJlZGlzIH0gZnJvbSAnLi4vc2VydmljZXMvZW52LXJlZGlzLmpzJztcbmltcG9ydCB7IGdldEhlYWRlclN0cmluZyB9IGZyb20gJy4uL3V0aWxzL2hlYWRlcnMuanMnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IHNlbmRFcnJvciB9IGZyb20gJy4uL3V0aWxzL3Jlc3BvbnNlLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdzdGVwLXVwJyk7XG5jb25zdCBIRUFERVIgPSAneC1zdGVwLXVwLXRva2VuJztcblxuLyoqXG4gKiBIb3cgYSBzdGVwLXVwIHRva2VuIHdhcyBlYXJuZWQuIE1pcnJvcnMgcGxhdGZvcm0ncyBgU3RlcFVwTWV0aG9kYDsga2VwdCBoZXJlXG4gKiBhcyBpdHMgb3duIHVuaW9uIHNvIGEgdmVyaWZpZXIgY2FuIG5hbWUgdGhlIGZhY3RvcnMgaXQgYWNjZXB0cyB3aXRob3V0XG4gKiBpbXBvcnRpbmcgcGxhdGZvcm0uXG4gKi9cbmV4cG9ydCB0eXBlIFN0ZXBVcE1ldGhvZCA9ICdwYXNzd29yZCcgfCAnd2ViYXV0aG4nIHwgJ3RvdHAnIHwgJ3JlYXV0aCc7XG5cbi8qKlxuICogVGhlIGZhY3RvcnMgdGhhdCBjb3VudCBhcyBhIFNFQ09ORCBGQUNUT1IsIGZvciByb3V0ZXMgdGhhdCBtYXkgb25seSBiZVxuICogdW5sb2NrZWQgYnkgb25lICgjODogaW1wZXJzb25hdGlvbiwgS01TLCBJZFAgc2V0dGluZ3MsIHBsYXRmb3JtIGFkbWluKS5cbiAqXG4gKiBBIHBhc3N3b3JkIG9yIGEgcHJvdmlkZXIgcmUtYXV0aCBwcm92ZXMgdGhlIHNhbWUgc2luZ2xlIGZhY3RvciB0aGUgc2Vzc2lvbiB3YXNcbiAqIGFscmVhZHkgb3BlbmVkIHdpdGgsIHNvIG9uIHRob3NlIHJvdXRlcyB0aGV5IGFyZSBub3QgZW5vdWdoOiB0aGUgcG9pbnQgaXMgdGhhdFxuICogc29tZW9uZSBob2xkaW5nIG9ubHkgYSBzdG9sZW4gc2Vzc2lvbiDigJQgb3Igb25seSBhIHN0b2xlbiBwYXNzd29yZCDigJQgY2Fubm90XG4gKiBwcm9jZWVkLlxuICovXG5leHBvcnQgY29uc3QgU1RST05HX1NURVBfVVBfTUVUSE9EUzogcmVhZG9ubHkgU3RlcFVwTWV0aG9kW10gPSBbJ3dlYmF1dGhuJywgJ3RvdHAnXTtcblxuLyoqIFBheWxvYWQgb2YgYSBwbGF0Zm9ybS1pc3N1ZWQgc3RlcC11cCB0b2tlbiAoc2VlIHBsYXRmb3JtIGlzc3VlU3RlcFVwVG9rZW4pLiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGVwVXBUb2tlblBheWxvYWQge1xuICB0eXBlOiAnc3RlcC11cCc7XG4gIHN1Yjogc3RyaW5nO1xuICBqdGk6IHN0cmluZztcbiAgLyoqIFdoaWNoIGZhY3RvciBlYXJuZWQgaXQuIEFic2VudCBvbmx5IG9uIGEgdG9rZW4gbWludGVkIGJlZm9yZSB0aGUgY2xhaW1cbiAgICogIGV4aXN0ZWQsIHdoaWNoIGEgbWV0aG9kLXJlc3RyaWN0ZWQgZ2F0ZSB0cmVhdHMgYXMgXCJub3QgcHJvdmVuXCIuICovXG4gIG1ldGhvZD86IFN0ZXBVcE1ldGhvZDtcbiAgaWF0OiBudW1iZXI7XG4gIGV4cDogbnVtYmVyO1xufVxuXG4vKiogT3B0aW9ucyBmb3Ige0BsaW5rIHJlcXVpcmVTdGVwVXB9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXF1aXJlU3RlcFVwT3B0aW9ucyB7XG4gIC8qKiBSZXN0cmljdCB3aGljaCBmYWN0b3JzIG1heSBlYXJuIHRoZSBzdGVwLXVwIChlLmcuIHtAbGluayBTVFJPTkdfU1RFUF9VUF9NRVRIT0RTfSkuXG4gICAqICBPbWl0dGVkIG1lYW5zIGFueSBmYWN0b3IgdGhlIHBsYXRmb3JtIHdpbGwgaXNzdWUgYSB0b2tlbiBmb3IuICovXG4gIG1ldGhvZHM/OiByZWFkb25seSBTdGVwVXBNZXRob2RbXTtcbn1cblxuLyoqXG4gKiBWZXJpZnkgYSBzdGVwLXVwIHRva2VuOiBhIHZhbGlkIEVTMjU2IHNpZ25hdHVyZSBmcm9tIG9uZSBvZiBwbGF0Zm9ybSdzXG4gKiBwdWJsaXNoZWQgc2lnbmluZyBrZXlzLCBpc3N1ZXIgYW5kIGF1ZGllbmNlIHdoZW4gY29uZmlndXJlZCwgQU5EIHRoZVxuICogYHR5cGU6ICdzdGVwLXVwJ2AgKyBganRpYCBjbGFpbXMg4oCUIGEgcGxhaW4gYWNjZXNzIHRva2VuIGlzIHNpZ25lZCBieSB0aGUgc2FtZVxuICoga2V5IGFuZCBjYXJyaWVzIHRoZSBzYW1lIGBzdWJgLCBzbyB3aXRob3V0IGFzc2VydGluZyB0aGUgc3RlcC11cCBzaGFwZSBpdFxuICogd291bGQgYnlwYXNzIHRoZSBnYXRlLiBUaHJvd3Mgb24gYW55IGZhaWx1cmUgKGFuIHVuZmV0Y2hhYmxlIGtleSBzZXQgaW5jbHVkZWQ6XG4gKiBhbiB1bnZlcmlmaWFibGUgdG9rZW4gaXMgbm90IGEgc3RlcC11cCkuIERvZXMgTk9UIGJpbmQgdG8gYSBjYWxsZXIg4oCUXG4gKiBgcmVxdWlyZVN0ZXBVcGAgZG9lcyB0aGUgYHN1YmAgbWF0Y2ggYW5kIHNpbmdsZS11c2UgY29uc3VtZS5cbiAqXG4gKiBEZWxlZ2F0ZXMgdG8gYHJlcXVpcmVBdXRoYCdzIG93biBwcmltaXRpdmUgKGB2ZXJpZnlVc2VySnd0YCkgcmF0aGVyIHRoYW5cbiAqIHJlLWltcGxlbWVudGluZyBpdC4gVGhlIGhhbmQtcm9sbGVkIGNvcHkgdGhpcyByZXBsYWNlZCBkcm9wcGVkIFRXTyBvZiBpdHNcbiAqIGd1YXJkczogaXQgbmV2ZXIgcGlubmVkIGlzc3Vlci9hdWRpZW5jZSwgYW5kIGl0cyBwcmV2aW91cy1zZWNyZXQgcmV0cnkgaGFkIG5vXG4gKiBleHBpcnkvbm90LWJlZm9yZSBjYXJ2ZS1vdXQsIHNvIGFuIEVYUElSRUQgdG9rZW4gc3VyZmFjZWQgYXMgYSBzaWduYXR1cmUgZXJyb3IuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB2ZXJpZnlTdGVwVXBUb2tlbih0b2tlbjogc3RyaW5nKTogUHJvbWlzZTxTdGVwVXBUb2tlblBheWxvYWQ+IHtcbiAgY29uc3QgcGF5bG9hZCA9IGF3YWl0IHZlcmlmeVVzZXJKd3Q8U3RlcFVwVG9rZW5QYXlsb2FkPih0b2tlbik7XG5cbiAgaWYgKHBheWxvYWQudHlwZSAhPT0gJ3N0ZXAtdXAnIHx8ICFwYXlsb2FkLmp0aSB8fCAhcGF5bG9hZC5zdWIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0lOVkFMSURfU1RFUF9VUF9UT0tFTicpO1xuICB9XG4gIHJldHVybiBwYXlsb2FkO1xufVxuXG4vLyAtLSBTaW5nbGUtdXNlIGp0aSBzdG9yZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gUmVkaXMgU0VUIE5YIEVYIHdoZW4gUmVkaXMgaXMgY29uZmlndXJlZCAoY3Jvc3MtaW5zdGFuY2Ugc2luZ2xlLXVzZSk7IGVsc2UgYVxuLy8gYm91bmRlZCBwcm9jZXNzLWxvY2FsIG1hcCB3aXRoIFRUTCBzd2VlcCAocGVyLXByb2Nlc3MpLiBBIHN0ZXAtdXAgdG9rZW4gaXNcbi8vIDYwcy1saXZlZC4gV2hlbiBSZWRpcyBJUyBjb25maWd1cmVkIGl0J3MgdGhlIGF1dGhvcml0YXRpdmUgc3RvcmU6IGEgUmVkaXNcbi8vIGVycm9yIEZBSUxTIENMT1NFRCAodGhyb3dzIOKGkiBTVEVQX1VQX0lOVkFMSUQsIHVzZXIgcmUtdmVyaWZpZXMpIHJhdGhlciB0aGFuXG4vLyBkZWdyYWRpbmcgdG8gdGhlIHBlci1pbnN0YW5jZSBtZW0gZ3VhcmQsIHdoaWNoIHdvdWxkIGFjY2VwdCBhIGNyb3NzLWluc3RhbmNlXG4vLyByZXBsYXkgZHVyaW5nIHRoZSBvdXRhZ2UuIFRoZSBtZW0gZ3VhcmQgaXMgdXNlZCBPTkxZIHdoZW4gUmVkaXMgaXMgYWJzZW50LlxuXG50eXBlIEp0aVJlZGlzID0gUmVhZHlBd2FyZVJlZGlzICYgeyBzZXQ6ICguLi5hcmdzOiB1bmtub3duW10pID0+IFByb21pc2U8dW5rbm93bj4gfTtcbmxldCBfcmVkaXM6IEp0aVJlZGlzIHwgbnVsbCB8IHVuZGVmaW5lZDtcbmxldCBfcmVkaXNSZWFkeTogKCgpID0+IFByb21pc2U8dm9pZD4pIHwgdW5kZWZpbmVkO1xuLyoqXG4gKiBUaGUganRpIGNsaWVudCwgYnVpbHQgbGF6aWx5LiBUaGUgZW52IGNsaWVudCBoYXMgbm8gb2ZmbGluZSBxdWV1ZSwgc28gYSBTRVQgb25cbiAqIGEgY29ubmVjdGlvbiB0aGF0IGlzbid0IHVwIHlldCBpcyByZWplY3RlZCDigJQgd2hpY2ggdXNlZCB0byBmYWlsIHRoZSBGSVJTVFxuICogc3RlcC11cCBvbiBldmVyeSBwb2QuIFdhaXQgKGJvdW5kZWQsIG5ldmVyIHJlamVjdGluZykgZm9yIHJlYWRpbmVzcyBiZWZvcmVcbiAqIGhhbmRpbmcgaXQgb3V0OyBhIGdlbnVpbmVseSBkb3duIFJlZGlzIHN0aWxsIGZhaWxzIGNsb3NlZCBvbiB0aGUgU0VUIGJlbG93LlxuICovXG5hc3luYyBmdW5jdGlvbiByZWRpcygpOiBQcm9taXNlPEp0aVJlZGlzIHwgbnVsbD4ge1xuICBpZiAoX3JlZGlzID09PSB1bmRlZmluZWQpIHtcbiAgICBfcmVkaXMgPSBjcmVhdGVFbnZSZWRpc0NsaWVudDxKdGlSZWRpcz4oJ3N0ZXAtdXAtanRpJyk7XG4gICAgX3JlZGlzUmVhZHkgPSBfcmVkaXMgPyBjcmVhdGVSZWRpc1JlYWR5R2F0ZShfcmVkaXMpIDogdW5kZWZpbmVkO1xuICB9XG4gIGlmIChfcmVkaXNSZWFkeSkgYXdhaXQgX3JlZGlzUmVhZHkoKTtcbiAgcmV0dXJuIF9yZWRpcztcbn1cblxuY29uc3QgbWVtSnRpID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTsgLy8ganRpIC0+IGV4cGlyeSBlcG9jaCBtc1xuY29uc3QgbWVtU3dlZXAgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gIGZvciAoY29uc3QgW2p0aSwgZXhwXSBvZiBtZW1KdGkpIGlmIChleHAgPD0gbm93KSBtZW1KdGkuZGVsZXRlKGp0aSk7XG59LCAzMF8wMDApO1xubWVtU3dlZXAudW5yZWY/LigpO1xuXG4vKipcbiAqIEhhcmQgY2VpbGluZyBvbiB0aGUgcHJvY2Vzcy1sb2NhbCBqdGkgc2V0LlxuICpcbiAqIFRoZSAzMHMgc3dlZXAgYWxvbmUgZG9lcyBOT1QgYm91bmQgaXQ6IGVudHJpZXMgb25seSBsZWF2ZSBvbmNlIHRoZWlyIHRva2VuJ3NcbiAqIFRUTCBoYXMgcGFzc2VkLCBzbyBhIGJ1cnN0IG9mIHN0ZXAtdXAgdHJhZmZpYyAob3IgYSBmbG9vZCBvZiBkaXN0aW5jdCBmb3JnZWRcbiAqIGp0aXMgb24gYSBSZWRpcy1sZXNzIGRlcGxveW1lbnQpIGdyb3dzIHRoZSBtYXAgZmFzdGVyIHRoYW4gdGhlIHN3ZWVwIGRyYWluc1xuICogaXQuIFRoaXMgaXMgYSByZXBsYXkgZ3VhcmQsIHNvIHNoZWRkaW5nIHRoZSBPTERFU1QgZW50cmllcyBpcyB0aGUgc2FmZVxuICogZGlyZWN0aW9uOiB0aGUgd29yc3QgY2FzZSBpcyB0aGF0IGEgbG9uZy1leHBpcmVkIGp0aSBjb3VsZCBiZSByZXBsYXllZCwgYW5kXG4gKiBleHBpcnkgaXMgZW5mb3JjZWQgaW5kZXBlbmRlbnRseSBieSB0aGUgdG9rZW4ncyBvd24gYGV4cGAuXG4gKi9cbmNvbnN0IE1FTV9KVElfTUFYID0gMTBfMDAwO1xuXG4vKiogRHJvcCB0aGUgc29vbmVzdC10by1leHBpcmUgZW50cmllcyB1bnRpbCB0aGUgbWFwIGlzIHVuZGVyIHRoZSBjZWlsaW5nLiAqL1xuZnVuY3Rpb24gZXZpY3RPbGRlc3RKdGkoKTogdm9pZCB7XG4gIGlmIChtZW1KdGkuc2l6ZSA8IE1FTV9KVElfTUFYKSByZXR1cm47XG4gIGNvbnN0IGJ5RXhwaXJ5ID0gWy4uLm1lbUp0aS5lbnRyaWVzKCldLnNvcnQoKGEsIGIpID0+IGFbMV0gLSBiWzFdKTtcbiAgY29uc3QgZXhjZXNzID0gbWVtSnRpLnNpemUgLSBNRU1fSlRJX01BWCArIDE7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZXhjZXNzOyBpICs9IDEpIG1lbUp0aS5kZWxldGUoYnlFeHBpcnlbaV1bMF0pO1xufVxuXG4vKipcbiAqIENvbnN1bWUgYSBzdGVwLXVwIGBqdGlgIGV4YWN0bHkgb25jZS4gUmV0dXJucyB0cnVlIGlmIHRoaXMgY2FsbCBjbGFpbWVkIGl0XG4gKiAoZmlyc3QgdXNlKSwgZmFsc2UgaWYgaXQgd2FzIGFscmVhZHkgY29uc3VtZWQgKHJlcGxheSkuIGBleHBFcG9jaFNlY29uZHNgIGlzXG4gKiB0aGUgdG9rZW4ncyBgZXhwYCwgc28gdGhlIG1hcmtlciBhdXRvLWV4cGlyZXMgd2l0aCB0aGUgdG9rZW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjb25zdW1lU3RlcFVwSnRpKGp0aTogc3RyaW5nLCBleHBFcG9jaFNlY29uZHM6IG51bWJlcik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBjb25zdCB0dGxTZWNvbmRzID0gTWF0aC5tYXgoMSwgZXhwRXBvY2hTZWNvbmRzIC0gTWF0aC5mbG9vcihEYXRlLm5vdygpIC8gMTAwMCkpO1xuICBjb25zdCBjbGllbnQgPSBhd2FpdCByZWRpcygpO1xuICBpZiAoY2xpZW50KSB7XG4gICAgLy8gUmVkaXMgY29uZmlndXJlZCDih5IgYXV0aG9yaXRhdGl2ZSBjcm9zcy1pbnN0YW5jZSBzdG9yZS4gU0VUIE5YIEVYIOKGkiAnT0snXG4gICAgLy8gd2hlbiBuZXdseSBzZXQsIG51bGwgd2hlbiBhbHJlYWR5IGNvbnN1bWVkLiBBIHRocm93biBSZWRpcyBlcnJvciBpcyBOT1RcbiAgICAvLyBjYXVnaHQgaGVyZTogaXQgcHJvcGFnYXRlcyBzbyBgcmVxdWlyZVN0ZXBVcGAgZmFpbHMgY2xvc2VkIChTVEVQX1VQX0lOVkFMSUQpXG4gICAgLy8gaW5zdGVhZCBvZiBzaWxlbnRseSBkZWdyYWRpbmcgdG8gdGhlIHBlci1pbnN0YW5jZSBtZW0gZ3VhcmQuXG4gICAgY29uc3QgcmVzID0gYXdhaXQgY2xpZW50LnNldChgc3RlcHVwOmp0aToke2p0aX1gLCAnMScsICdFWCcsIHR0bFNlY29uZHMsICdOWCcpO1xuICAgIHJldHVybiByZXMgPT09ICdPSyc7XG4gIH1cbiAgLy8gTm8gUmVkaXMg4oeSIHNpbmdsZS1pbnN0YW5jZSBkZXBsb3ltZW50OyB0aGUgcHJvY2Vzcy1sb2NhbCBtYXAgSVMgdGhlIHN0b3JlLlxuICBpZiAobWVtSnRpLmhhcyhqdGkpKSByZXR1cm4gZmFsc2U7XG4gIGV2aWN0T2xkZXN0SnRpKCk7XG4gIG1lbUp0aS5zZXQoanRpLCBEYXRlLm5vdygpICsgdHRsU2Vjb25kcyAqIDEwMDApO1xuICByZXR1cm4gdHJ1ZTtcbn1cblxuLyoqXG4gKiBFeHByZXNzIG1pZGRsZXdhcmU6IHJlcXVpcmUgYSB2YWxpZCwgY2FsbGVyLWJvdW5kLCBzaW5nbGUtdXNlIHN0ZXAtdXAgdG9rZW4gaW5cbiAqIHRoZSBgWC1TdGVwLVVwLVRva2VuYCBoZWFkZXIuIE11c3QgcnVuIEFGVEVSIGByZXF1aXJlQXV0aGAgKG5lZWRzIGByZXEudXNlcmApLlxuICpcbiAqIFVzZSBkaXJlY3RseSAoYHJvdXRlci5wb3N0KCcveCcsIHJlcXVpcmVBdXRoLCByZXF1aXJlU3RlcFVwLCBoKWApIGZvciB0aGVcbiAqIG9yZGluYXJ5IGFueS1mYWN0b3IgZ2F0ZSwgb3IgY2FsbCBpdCB3aXRoIGB7IG1ldGhvZHMgfWAgdG8gZGVtYW5kIHRoYXQgdGhlXG4gKiBjb25maXJtYXRpb24gd2FzIGVhcm5lZCBieSBhIHNwZWNpZmljIGZhY3RvciDigJQgYHJlcXVpcmVTdGVwVXAoeyBtZXRob2RzOlxuICogU1RST05HX1NURVBfVVBfTUVUSE9EUyB9KWAgb24gdGhlIHJvdXRlcyB3aGVyZSBhIHBhc3N3b3JkIHJlLXByb21wdCBpcyBub3QgYVxuICogbWVhbmluZ2Z1bCBzZWNvbmQgcHJvb2YuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXF1aXJlU3RlcFVwKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKTogUHJvbWlzZTx2b2lkPjtcbmV4cG9ydCBmdW5jdGlvbiByZXF1aXJlU3RlcFVwKG9wdGlvbnM6IFJlcXVpcmVTdGVwVXBPcHRpb25zKTogKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKSA9PiB2b2lkO1xuZXhwb3J0IGZ1bmN0aW9uIHJlcXVpcmVTdGVwVXAoXG4gIHJlcU9yT3B0aW9uczogUmVxdWVzdCB8IFJlcXVpcmVTdGVwVXBPcHRpb25zLFxuICByZXM/OiBSZXNwb25zZSxcbiAgbmV4dD86IE5leHRGdW5jdGlvbixcbik6IFByb21pc2U8dm9pZD4gfCAoKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKSA9PiB2b2lkKSB7XG4gIC8vIFRoZSBkaXJlY3QgZm9ybSBSRVRVUk5TIHRoZSBwcm9taXNlIChFeHByZXNzIGlnbm9yZXMgaXQ7IGEgdGVzdCBhd2FpdHMgaXQpLFxuICAvLyBzbyBjb21wb3NpbmcgdGhlIGdhdGUgYXMgYHJvdXRlci5wb3N0KCcveCcsIHJlcXVpcmVTdGVwVXAsIGgpYCBiZWhhdmVzXG4gIC8vIGV4YWN0bHkgYXMgaXQgZGlkIGJlZm9yZSB0aGUgb3B0aW9ucyBmb3JtIGV4aXN0ZWQuXG4gIGlmIChyZXMgJiYgbmV4dCAmJiAnaGVhZGVycycgaW4gcmVxT3JPcHRpb25zKSB7XG4gICAgcmV0dXJuIF9yZXF1aXJlU3RlcFVwKHt9LCByZXFPck9wdGlvbnMgYXMgUmVxdWVzdCwgcmVzLCBuZXh0KTtcbiAgfVxuICBjb25zdCBvcHRpb25zID0gcmVxT3JPcHRpb25zIGFzIFJlcXVpcmVTdGVwVXBPcHRpb25zO1xuICByZXR1cm4gdGFnUm91dGVHYXRlKFxuICAgIChyZXFJbm5lcjogUmVxdWVzdCwgcmVzSW5uZXI6IFJlc3BvbnNlLCBuZXh0SW5uZXI6IE5leHRGdW5jdGlvbikgPT4ge1xuICAgICAgdm9pZCBfcmVxdWlyZVN0ZXBVcChvcHRpb25zLCByZXFJbm5lciwgcmVzSW5uZXIsIG5leHRJbm5lcik7XG4gICAgfSxcbiAgICB7IGtpbmQ6ICdzdGVwVXAnLCAuLi4ob3B0aW9ucy5tZXRob2RzID8geyBtZXRob2RzOiBbLi4ub3B0aW9ucy5tZXRob2RzXSB9IDoge30pIH0sXG4gICk7XG59XG50YWdSb3V0ZUdhdGUocmVxdWlyZVN0ZXBVcCwgeyBraW5kOiAnc3RlcFVwJyB9KTtcblxuYXN5bmMgZnVuY3Rpb24gX3JlcXVpcmVTdGVwVXAob3B0aW9uczogUmVxdWlyZVN0ZXBVcE9wdGlvbnMsIHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIFZlcmlmaWVkIGludGVybmFsIHNlcnZpY2UgcHJpbmNpcGFscyBhcmUgRVhFTVBUOiBzdGVwLXVwIGlzIGEgcmUtdmVyaWZ5LXRoZS1cbiAgLy8gSFVNQU4gZ2F0ZSAodGhleSByZXBsYXkgYSBwYXNzd29yZC1yZXZlcmlmeSB0b2tlbiksIHdoaWNoIGEgc2VydmljZSB0b2tlblxuICAvLyBzdHJ1Y3R1cmFsbHkgY2Fubm90IHByb2R1Y2UuIFNlcnZpY2VzIGFscmVhZHkgYnlwYXNzIGByZXF1aXJlUGVybWlzc2lvbmAgb25cbiAgLy8gdGhlIHNhbWUgdHJ1c3QgYmFzaXMgKGEgc2lnbmVkIHNlcnZpY2UgSldUKSwgYW5kIGludGVybmFsIGNhbGxlcnMgb2YgYVxuICAvLyBzdGVwLXVwLWdhdGVkIHJvdXRlIOKAlCBlLmcuIHRoZSBwbGF0Zm9ybSBvcmctY2FzY2FkZSdzIGBERUxFVEUgL3F1b3Rhcy86b3JnSWRgXG4gIC8vIG9yIHRoZSBiaWxsaW5n4oaScXVvdGEgZW50aXRsZW1lbnQgc3luYyDigJQgd291bGQgb3RoZXJ3aXNlIGJlIGhhcmQtYmxvY2tlZC5cbiAgLy8gU2FmZTogYSBzZXJ2aWNlIHRva2VuIGNhbiBvbmx5IGJlIG1pbnRlZCB3aXRoIGEgU0VSVklDRSdzIG93biBFUzI1NiBrZXlcbiAgLy8gKCMxNCksIHdoaWNoIG5vIGV4dGVybmFsIGNsaWVudCBob2xkcywgc28gdGhpcyBjYW4ndCBiZSBzcG9vZmVkIHRvIHNraXBcbiAgLy8gc3RlcC11cC5cbiAgaWYgKGlzU2VydmljZVByaW5jaXBhbChyZXEpKSB7XG4gICAgbmV4dCgpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIEFuIE9SRyBTRVJWSUNFIEFDQ09VTlQgaXMgdGhlIG9wcG9zaXRlIGNhc2U6IGl0IGlzIHN1YmplY3QgdG8gZXZlcnkgZ2F0ZSBhXG4gIC8vIG1lbWJlciBpcywgYW5kIHRoZXJlIGlzIG5vIHBlcnNvbiBiZWhpbmQgaXQgdG8gcmUtdmVyaWZ5IOKAlCBzbyBpdCBjYW4gTkVWRVJcbiAgLy8gcGFzcyBzdGVwLXVwLiBSZWZ1c2UgZXhwbGljaXRseSAocmF0aGVyIHRoYW4gbGV0dGluZyBpdCBmYWxsIHRocm91Z2ggdG8gdGhlXG4gIC8vIGBzdWJgLWJvdW5kIHRva2VuIGNoZWNrLCB3aGljaCB3b3VsZCBmYWlsIHdpdGggYSBjb25mdXNpbmcgU1RFUF9VUF9NSVNNQVRDSClcbiAgLy8gc28gXCJhIG1hY2hpbmUgY3JlZGVudGlhbCBuZXZlciBzYXRpc2ZpZXMgYW4gYXNzdXJhbmNlIHJlcXVpcmVtZW50XCIgaXMgYVxuICAvLyBwcm9wZXJ0eSBvZiB0aGlzIGdhdGUsIG5vdCBhIHNpZGUgZWZmZWN0IG9mIGhvdyBzdGVwLXVwIHRva2VucyBhcmUgbWludGVkLlxuICBpZiAoaXNTZXJ2aWNlQWNjb3VudFByaW5jaXBhbChyZXEpKSB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAzLCAnQSBzZXJ2aWNlIGFjY291bnQgY2Fubm90IHBlcmZvcm0gdGhpcyBhY3Rpb24g4oCUIGl0IHJlcXVpcmVzIGEgcGVyc29uIHRvIHJlLXZlcmlmeScsICdTVEVQX1VQX05PVF9BVkFJTEFCTEUnKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCBjYWxsZXJTdWIgPSAocmVxIGFzIFJlcXVlc3QgJiB7IHVzZXI/OiB7IHN1Yj86IHN0cmluZyB9IH0pLnVzZXI/LnN1YjtcbiAgaWYgKCFjYWxsZXJTdWIpIHtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdBdXRoZW50aWNhdGlvbiByZXF1aXJlZCcsICdVTkFVVEhPUklaRUQnKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBjb25zdCB0b2tlbiA9IGdldEhlYWRlclN0cmluZyhyZXEuaGVhZGVyc1tIRUFERVJdKTtcbiAgaWYgKCF0b2tlbikge1xuICAgIHNlbmRFcnJvcihyZXMsIDQwMSwgJ1N0ZXAtdXAgdmVyaWZpY2F0aW9uIHJlcXVpcmVkJywgJ1NURVBfVVBfUkVRVUlSRUQnKTtcbiAgICByZXR1cm47XG4gIH1cblxuICBsZXQgcGF5bG9hZDogU3RlcFVwVG9rZW5QYXlsb2FkO1xuICB0cnkge1xuICAgIHBheWxvYWQgPSBhd2FpdCB2ZXJpZnlTdGVwVXBUb2tlbih0b2tlbik7XG4gIH0gY2F0Y2gge1xuICAgIHNlbmRFcnJvcihyZXMsIDQwMSwgJ1N0ZXAtdXAgdG9rZW4gaW52YWxpZCBvciBleHBpcmVkJywgJ1NURVBfVVBfSU5WQUxJRCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGlmIChwYXlsb2FkLnN1YiAhPT0gY2FsbGVyU3ViKSB7XG4gICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBkb2VzIG5vdCBtYXRjaCBzZXNzaW9uJywgJ1NURVBfVVBfTUlTTUFUQ0gnKTtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBGYWN0b3IgcmVzdHJpY3Rpb24gKCM4KS4gQ2hlY2tlZCBCRUZPUkUgdGhlIGp0aSBpcyBjb25zdW1lZCwgc28gYSB0b2tlblxuICAvLyBlYXJuZWQgYnkgdGhlIHdyb25nIGZhY3RvciBjYW4gc3RpbGwgYmUgc3BlbnQgb24gYSByb3V0ZSB0aGF0IGFjY2VwdHMgaXQg4oCUXG4gIC8vIGJ1cm5pbmcgaXQgaGVyZSB3b3VsZCBtYWtlIHRoZSByZWZ1c2FsIGRlc3RydWN0aXZlIGFzIHdlbGwgYXMgY29uZnVzaW5nLlxuICBpZiAob3B0aW9ucy5tZXRob2RzICYmICEocGF5bG9hZC5tZXRob2QgJiYgb3B0aW9ucy5tZXRob2RzLmluY2x1ZGVzKHBheWxvYWQubWV0aG9kKSkpIHtcbiAgICBzZW5kRXJyb3IoXG4gICAgICByZXMsIDQwMSxcbiAgICAgICdUaGlzIGFjdGlvbiBtdXN0IGJlIGNvbmZpcm1lZCB3aXRoIGEgcGFzc2tleSBvciBhbiBhdXRoZW50aWNhdG9yIGNvZGUnLFxuICAgICAgJ1NURVBfVVBfTUVUSE9EX1JFUVVJUkVEJyxcbiAgICAgIHsgbWV0aG9kczogWy4uLm9wdGlvbnMubWV0aG9kc10gfSxcbiAgICApO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRyeSB7XG4gICAgaWYgKCEoYXdhaXQgY29uc3VtZVN0ZXBVcEp0aShwYXlsb2FkLmp0aSwgcGF5bG9hZC5leHApKSkge1xuICAgICAgc2VuZEVycm9yKHJlcywgNDAxLCAnU3RlcC11cCB0b2tlbiBhbHJlYWR5IHVzZWQgb3IgZXhwaXJlZCcsICdTVEVQX1VQX1JFUExBWScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgbG9nZ2VyLmVycm9yKCdTdGVwLXVwIGp0aSBjb25zdW1lIGVycm9yZWQnLCB7IGVycm9yOiBTdHJpbmcoZXJyKSB9KTtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDEsICdTdGVwLXVwIHZlcmlmaWNhdGlvbiBmYWlsZWQnLCAnU1RFUF9VUF9JTlZBTElEJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgbmV4dCgpO1xufVxuIl19
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The key was presented to platform and refused (unknown, revoked, expired). */
|
|
2
|
+
export declare class ApiKeyRejectedError extends Error {
|
|
3
|
+
constructor(message?: string);
|
|
4
|
+
}
|
|
5
|
+
/** Platform could not be reached (or answered 5xx) — the key's validity is unknown. */
|
|
6
|
+
export declare class ApiKeyExchangeUnavailableError extends Error {
|
|
7
|
+
constructor(message?: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Name this process in the exchange call's service token. Called from
|
|
11
|
+
* `wireServiceSecurity` at boot (and by the plugin service, which wires its
|
|
12
|
+
* boot security by hand).
|
|
13
|
+
*/
|
|
14
|
+
export declare function setApiKeyExchangeServiceName(name: string): void;
|
|
15
|
+
/**
|
|
16
|
+
* The JWT for `key`, from cache when possible. Throws {@link ApiKeyRejectedError}
|
|
17
|
+
* when platform refused the key and {@link ApiKeyExchangeUnavailableError} when
|
|
18
|
+
* it could not be asked.
|
|
19
|
+
*/
|
|
20
|
+
export declare function exchangeApiKey(key: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Drop every cached exchange. For tests and for a process that wants to force
|
|
23
|
+
* re-validation; production relies on the TTLs above.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resetApiKeyExchangeCache(): void;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Trade an opaque access key for a short-lived JWT, once, and cache it.
|
|
5
|
+
*
|
|
6
|
+
* Only platform, billing and quota have MongoDB, so the stateless services
|
|
7
|
+
* cannot look up a key hash — and asking platform to validate EVERY request
|
|
8
|
+
* would put it on the hot path of the whole fleet. Instead this module does the
|
|
9
|
+
* exchange once per key (`POST /auth/token/exchange`) and caches the returned
|
|
10
|
+
* JWT in-process until it expires, so `requireAuth` only ever verifies a JWT.
|
|
11
|
+
*
|
|
12
|
+
* Properties that matter:
|
|
13
|
+
*
|
|
14
|
+
* - **Revocation latency is bounded by the token TTL** (5 minutes). A revoked
|
|
15
|
+
* key can no longer be exchanged, and the cached token it already produced
|
|
16
|
+
* dies on its own.
|
|
17
|
+
* - **Early, jittered refresh.** The cached token is re-exchanged in the
|
|
18
|
+
* background at ~75% of its life (±20% jitter) so no request ever blocks on
|
|
19
|
+
* the exchange, and a fleet of pods holding the same key doesn't stampede
|
|
20
|
+
* platform at the same instant.
|
|
21
|
+
* - **One in-flight exchange per key.** Concurrent requests share a promise.
|
|
22
|
+
* - **Short negative cache.** A key platform rejected is remembered for 30s, so
|
|
23
|
+
* a misconfigured client (or a scanner) can't turn every request into a
|
|
24
|
+
* platform round-trip.
|
|
25
|
+
* - **Platform down is a 503, never a 200.** The caller maps
|
|
26
|
+
* {@link ApiKeyExchangeUnavailableError} to "try again", with a metric — it is
|
|
27
|
+
* deliberately NOT fail-open: an unverifiable credential is not an identity.
|
|
28
|
+
*
|
|
29
|
+
* The exchange request itself carries this service's own signed service token,
|
|
30
|
+
* which is what exempts it from platform's IP-keyed `/auth` rate limiter (every
|
|
31
|
+
* pod's exchanges would otherwise share one bucket) and names the caller in
|
|
32
|
+
* platform's audit trail.
|
|
33
|
+
*/
|
|
34
|
+
import { getServiceAuthHeader, SYSTEM_ORG_ID } from '../middleware/auth.js';
|
|
35
|
+
import { InternalHttpClient } from '../services/http-client.js';
|
|
36
|
+
import { hashApiKey } from '../utils/api-key.js';
|
|
37
|
+
import { createLogger } from '../utils/logger.js';
|
|
38
|
+
import { emitCounter } from '../utils/metric-emitter.js';
|
|
39
|
+
const logger = createLogger('api-key-exchange');
|
|
40
|
+
/** Most distinct keys held in the process-wide cache before the oldest are dropped. */
|
|
41
|
+
const MAX_CACHED_KEYS = 1000;
|
|
42
|
+
/** How long a platform REJECTION is remembered (ms). */
|
|
43
|
+
const NEGATIVE_TTL_MS = 30_000;
|
|
44
|
+
/** Fraction of a token's life after which a background re-exchange is started. */
|
|
45
|
+
const REFRESH_AT_FRACTION = 0.75;
|
|
46
|
+
/** ± jitter applied to the refresh point, as a fraction of the token's life.
|
|
47
|
+
* Wide enough to de-synchronize a fleet, narrow enough to leave a clear tail of
|
|
48
|
+
* still-valid token after the latest possible refresh. */
|
|
49
|
+
const REFRESH_JITTER_FRACTION = 0.1;
|
|
50
|
+
/** The key was presented to platform and refused (unknown, revoked, expired). */
|
|
51
|
+
export class ApiKeyRejectedError extends Error {
|
|
52
|
+
constructor(message = 'Access key is invalid, revoked or expired') {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = 'ApiKeyRejectedError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Platform could not be reached (or answered 5xx) — the key's validity is unknown. */
|
|
58
|
+
export class ApiKeyExchangeUnavailableError extends Error {
|
|
59
|
+
constructor(message = 'Access-key verification is temporarily unavailable') {
|
|
60
|
+
super(message);
|
|
61
|
+
this.name = 'ApiKeyExchangeUnavailableError';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Cache of successful exchanges, keyed by `sha256(key)` — never by the key itself. */
|
|
65
|
+
const tokenCache = new Map();
|
|
66
|
+
/** Cache of rejections, keyed the same way; value is the epoch ms it expires. */
|
|
67
|
+
const rejectionCache = new Map();
|
|
68
|
+
/** In-flight exchanges, so concurrent requests for one key share a round-trip. */
|
|
69
|
+
const inFlight = new Map();
|
|
70
|
+
/** The service name minted into the exchange call's own service token. */
|
|
71
|
+
let serviceName = process.env.SERVICE_NAME || 'unknown';
|
|
72
|
+
/**
|
|
73
|
+
* Name this process in the exchange call's service token. Called from
|
|
74
|
+
* `wireServiceSecurity` at boot (and by the plugin service, which wires its
|
|
75
|
+
* boot security by hand).
|
|
76
|
+
*/
|
|
77
|
+
export function setApiKeyExchangeServiceName(name) {
|
|
78
|
+
serviceName = name;
|
|
79
|
+
}
|
|
80
|
+
/** Platform's in-cluster address — the same env pair every other caller uses. */
|
|
81
|
+
function platformClient() {
|
|
82
|
+
return new InternalHttpClient({
|
|
83
|
+
host: process.env.PLATFORM_SERVICE_HOST || 'platform',
|
|
84
|
+
port: parseInt(process.env.PLATFORM_SERVICE_PORT || '3000', 10),
|
|
85
|
+
timeout: parseInt(process.env.API_KEY_EXCHANGE_TIMEOUT_MS || '3000', 10),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** The epoch ms at which a token with `ttlSeconds` of life should be refreshed early. */
|
|
89
|
+
function jitteredRefreshAt(issuedAt, ttlSeconds) {
|
|
90
|
+
const lifeMs = ttlSeconds * 1000;
|
|
91
|
+
const jitter = (Math.random() * 2 - 1) * REFRESH_JITTER_FRACTION;
|
|
92
|
+
// Clamp into (0, 1) so the refresh point is always before expiry and never in
|
|
93
|
+
// the past, whatever TTL platform returns.
|
|
94
|
+
const fraction = Math.min(0.95, Math.max(0.1, REFRESH_AT_FRACTION + jitter));
|
|
95
|
+
return issuedAt + lifeMs * fraction;
|
|
96
|
+
}
|
|
97
|
+
/** Drop the oldest entries once the cache is over its cap (keys are long-lived, tokens are not). */
|
|
98
|
+
function evictIfFull() {
|
|
99
|
+
if (tokenCache.size <= MAX_CACHED_KEYS)
|
|
100
|
+
return;
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
for (const [hash, entry] of tokenCache) {
|
|
103
|
+
if (entry.expiresAt <= now)
|
|
104
|
+
tokenCache.delete(hash);
|
|
105
|
+
}
|
|
106
|
+
// Map preserves insertion order — drop from the front until back under the cap.
|
|
107
|
+
while (tokenCache.size > MAX_CACHED_KEYS) {
|
|
108
|
+
const oldest = tokenCache.keys().next();
|
|
109
|
+
if (oldest.done)
|
|
110
|
+
break;
|
|
111
|
+
tokenCache.delete(oldest.value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** One real round-trip to platform. Throws the two typed errors above. */
|
|
115
|
+
async function performExchange(key, hash) {
|
|
116
|
+
const client = platformClient();
|
|
117
|
+
let status;
|
|
118
|
+
let body;
|
|
119
|
+
try {
|
|
120
|
+
const res = await client.post('/auth/token/exchange', { key }, {
|
|
121
|
+
headers: {
|
|
122
|
+
Authorization: getServiceAuthHeader({ serviceName, role: 'member', orgId: SYSTEM_ORG_ID }),
|
|
123
|
+
},
|
|
124
|
+
// The exchange is a pure read of the key record plus a token mint — safe to
|
|
125
|
+
// retry, and a lost retry only costs one extra mint.
|
|
126
|
+
idempotent: true,
|
|
127
|
+
});
|
|
128
|
+
status = res.statusCode;
|
|
129
|
+
body = res.body;
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
emitCounter('api_key_exchange_total', { result: 'unavailable' });
|
|
133
|
+
emitCounter('api_key_exchange_failures_total', { reason: 'unavailable' });
|
|
134
|
+
logger.warn('Access-key exchange failed to reach platform', {
|
|
135
|
+
error: err instanceof Error ? err.message : String(err),
|
|
136
|
+
});
|
|
137
|
+
throw new ApiKeyExchangeUnavailableError();
|
|
138
|
+
}
|
|
139
|
+
if (status === 401 || status === 403 || status === 400 || status === 404) {
|
|
140
|
+
rejectionCache.set(hash, Date.now() + NEGATIVE_TTL_MS);
|
|
141
|
+
emitCounter('api_key_exchange_total', { result: 'rejected' });
|
|
142
|
+
emitCounter('api_key_exchange_failures_total', { reason: 'rejected' });
|
|
143
|
+
throw new ApiKeyRejectedError();
|
|
144
|
+
}
|
|
145
|
+
const accessToken = body?.data?.accessToken;
|
|
146
|
+
if (status >= 400 || typeof accessToken !== 'string' || accessToken.length === 0) {
|
|
147
|
+
// 429 and 5xx land here: platform is up but can't answer right now, which is
|
|
148
|
+
// "unknown", not "invalid" — never cache it as a rejection.
|
|
149
|
+
emitCounter('api_key_exchange_total', { result: 'unavailable' });
|
|
150
|
+
emitCounter('api_key_exchange_failures_total', { reason: status === 429 ? 'throttled' : 'server_error' });
|
|
151
|
+
throw new ApiKeyExchangeUnavailableError();
|
|
152
|
+
}
|
|
153
|
+
const ttlSeconds = typeof body?.data?.expiresIn === 'number' && body.data.expiresIn > 0
|
|
154
|
+
? body.data.expiresIn
|
|
155
|
+
: 60;
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
const entry = {
|
|
158
|
+
token: accessToken,
|
|
159
|
+
expiresAt: now + ttlSeconds * 1000,
|
|
160
|
+
refreshAt: jitteredRefreshAt(now, ttlSeconds),
|
|
161
|
+
};
|
|
162
|
+
tokenCache.set(hash, entry);
|
|
163
|
+
rejectionCache.delete(hash);
|
|
164
|
+
evictIfFull();
|
|
165
|
+
emitCounter('api_key_exchange_total', { result: 'success' });
|
|
166
|
+
return entry;
|
|
167
|
+
}
|
|
168
|
+
/** Exchange, sharing a single in-flight round-trip per key. */
|
|
169
|
+
function exchangeOnce(key, hash) {
|
|
170
|
+
const pending = inFlight.get(hash);
|
|
171
|
+
if (pending)
|
|
172
|
+
return pending;
|
|
173
|
+
const promise = performExchange(key, hash).finally(() => inFlight.delete(hash));
|
|
174
|
+
inFlight.set(hash, promise);
|
|
175
|
+
return promise;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The JWT for `key`, from cache when possible. Throws {@link ApiKeyRejectedError}
|
|
179
|
+
* when platform refused the key and {@link ApiKeyExchangeUnavailableError} when
|
|
180
|
+
* it could not be asked.
|
|
181
|
+
*/
|
|
182
|
+
export async function exchangeApiKey(key) {
|
|
183
|
+
const hash = hashApiKey(key);
|
|
184
|
+
const now = Date.now();
|
|
185
|
+
const rejectedUntil = rejectionCache.get(hash);
|
|
186
|
+
if (rejectedUntil !== undefined) {
|
|
187
|
+
if (rejectedUntil > now) {
|
|
188
|
+
emitCounter('api_key_exchange_cache_total', { result: 'rejected' });
|
|
189
|
+
throw new ApiKeyRejectedError();
|
|
190
|
+
}
|
|
191
|
+
rejectionCache.delete(hash);
|
|
192
|
+
}
|
|
193
|
+
const cached = tokenCache.get(hash);
|
|
194
|
+
if (cached && cached.expiresAt > now) {
|
|
195
|
+
if (cached.refreshAt <= now && !inFlight.has(hash)) {
|
|
196
|
+
// Early refresh: warm the cache WITHOUT making this request wait. A failed
|
|
197
|
+
// background refresh is not this request's problem — the cached token is
|
|
198
|
+
// still valid, and the next request past expiry exchanges inline.
|
|
199
|
+
emitCounter('api_key_exchange_cache_total', { result: 'refresh' });
|
|
200
|
+
void exchangeOnce(key, hash).catch(() => { });
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
emitCounter('api_key_exchange_cache_total', { result: 'hit' });
|
|
204
|
+
}
|
|
205
|
+
return cached.token;
|
|
206
|
+
}
|
|
207
|
+
emitCounter('api_key_exchange_cache_total', { result: 'miss' });
|
|
208
|
+
return (await exchangeOnce(key, hash)).token;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Drop every cached exchange. For tests and for a process that wants to force
|
|
212
|
+
* re-validation; production relies on the TTLs above.
|
|
213
|
+
*/
|
|
214
|
+
export function resetApiKeyExchangeCache() {
|
|
215
|
+
tokenCache.clear();
|
|
216
|
+
rejectionCache.clear();
|
|
217
|
+
inFlight.clear();
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLWtleS1leGNoYW5nZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlcy9hcGkta2V5LWV4Y2hhbmdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThCRztBQUVILE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM1RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDakQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2xELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUV6RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsa0JBQWtCLENBQUMsQ0FBQztBQUVoRCx1RkFBdUY7QUFDdkYsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDO0FBRTdCLHdEQUF3RDtBQUN4RCxNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUM7QUFFL0Isa0ZBQWtGO0FBQ2xGLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0FBRWpDOzsyREFFMkQ7QUFDM0QsTUFBTSx1QkFBdUIsR0FBRyxHQUFHLENBQUM7QUFFcEMsaUZBQWlGO0FBQ2pGLE1BQU0sT0FBTyxtQkFBb0IsU0FBUSxLQUFLO0lBQzVDLFlBQVksT0FBTyxHQUFHLDJDQUEyQztRQUMvRCxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixJQUFJLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO0lBQ3BDLENBQUM7Q0FDRjtBQUVELHVGQUF1RjtBQUN2RixNQUFNLE9BQU8sOEJBQStCLFNBQVEsS0FBSztJQUN2RCxZQUFZLE9BQU8sR0FBRyxvREFBb0Q7UUFDeEUsS0FBSyxD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SSxNQUFNLElBQUksTUFBTSxDQUFDLFNBQVMsR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNyQyxJQUFJLE1BQU0sQ0FBQyxTQUFTLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ25ELDJFQUEyRTtZQUMzRSx5RUFBeUU7WUFDekUsa0VBQWtFO1lBQ2xFLFdBQVcsQ0FBQyw4QkFBOEIsRUFBRSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1lBQ25FLEtBQUssWUFBWSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQTZDLENBQUMsQ0FBQyxDQUFDO1FBQzFGLENBQUM7YUFBTSxDQUFDO1lBQ04sV0FBVyxDQUFDLDhCQUE4QixFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDakUsQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQztJQUN0QixDQUFDO0lBRUQsV0FBVyxDQUFDLDhCQUE4QixFQUFFLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFDaEUsT0FBTyxDQUFDLE1BQU0sWUFBWSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztBQUMvQyxDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLHdCQUF3QjtJQUN0QyxVQUFVLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDbkIsY0FBYyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3ZCLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQztBQUNuQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogVHJhZGUgYW4gb3BhcXVlIGFjY2VzcyBrZXkgZm9yIGEgc2hvcnQtbGl2ZWQgSldULCBvbmNlLCBhbmQgY2FjaGUgaXQuXG4gKlxuICogT25seSBwbGF0Zm9ybSwgYmlsbGluZyBhbmQgcXVvdGEgaGF2ZSBNb25nb0RCLCBzbyB0aGUgc3RhdGVsZXNzIHNlcnZpY2VzXG4gKiBjYW5ub3QgbG9vayB1cCBhIGtleSBoYXNoIOKAlCBhbmQgYXNraW5nIHBsYXRmb3JtIHRvIHZhbGlkYXRlIEVWRVJZIHJlcXVlc3RcbiAqIHdvdWxkIHB1dCBpdCBvbiB0aGUgaG90IHBhdGggb2YgdGhlIHdob2xlIGZsZWV0LiBJbnN0ZWFkIHRoaXMgbW9kdWxlIGRvZXMgdGhlXG4gKiBleGNoYW5nZSBvbmNlIHBlciBrZXkgKGBQT1NUIC9hdXRoL3Rva2VuL2V4Y2hhbmdlYCkgYW5kIGNhY2hlcyB0aGUgcmV0dXJuZWRcbiAqIEpXVCBpbi1wcm9jZXNzIHVudGlsIGl0IGV4cGlyZXMsIHNvIGByZXF1aXJlQXV0aGAgb25seSBldmVyIHZlcmlmaWVzIGEgSldULlxuICpcbiAqIFByb3BlcnRpZXMgdGhhdCBtYXR0ZXI6XG4gKlxuICogLSAqKlJldm9jYXRpb24gbGF0ZW5jeSBpcyBib3VuZGVkIGJ5IHRoZSB0b2tlbiBUVEwqKiAoNSBtaW51dGVzKS4gQSByZXZva2VkXG4gKiAgIGtleSBjYW4gbm8gbG9uZ2VyIGJlIGV4Y2hhbmdlZCwgYW5kIHRoZSBjYWNoZWQgdG9rZW4gaXQgYWxyZWFkeSBwcm9kdWNlZFxuICogICBkaWVzIG9uIGl0cyBvd24uXG4gKiAtICoqRWFybHksIGppdHRlcmVkIHJlZnJlc2guKiogVGhlIGNhY2hlZCB0b2tlbiBpcyByZS1leGNoYW5nZWQgaW4gdGhlXG4gKiAgIGJhY2tncm91bmQgYXQgfjc1JSBvZiBpdHMgbGlmZSAowrEyMCUgaml0dGVyKSBzbyBubyByZXF1ZXN0IGV2ZXIgYmxvY2tzIG9uXG4gKiAgIHRoZSBleGNoYW5nZSwgYW5kIGEgZmxlZXQgb2YgcG9kcyBob2xkaW5nIHRoZSBzYW1lIGtleSBkb2Vzbid0IHN0YW1wZWRlXG4gKiAgIHBsYXRmb3JtIGF0IHRoZSBzYW1lIGluc3RhbnQuXG4gKiAtICoqT25lIGluLWZsaWdodCBleGNoYW5nZSBwZXIga2V5LioqIENvbmN1cnJlbnQgcmVxdWVzdHMgc2hhcmUgYSBwcm9taXNlLlxuICogLSAqKlNob3J0IG5lZ2F0aXZlIGNhY2hlLioqIEEga2V5IHBsYXRmb3JtIHJlamVjdGVkIGlzIHJlbWVtYmVyZWQgZm9yIDMwcywgc29cbiAqICAgYSBtaXNjb25maWd1cmVkIGNsaWVudCAob3IgYSBzY2FubmVyKSBjYW4ndCB0dXJuIGV2ZXJ5IHJlcXVlc3QgaW50byBhXG4gKiAgIHBsYXRmb3JtIHJvdW5kLXRyaXAuXG4gKiAtICoqUGxhdGZvcm0gZG93biBpcyBhIDUwMywgbmV2ZXIgYSAyMDAuKiogVGhlIGNhbGxlciBtYXBzXG4gKiAgIHtAbGluayBBcGlLZXlFeGNoYW5nZVVuYXZhaWxhYmxlRXJyb3J9IHRvIFwidHJ5IGFnYWluXCIsIHdpdGggYSBtZXRyaWMg4oCUIGl0IGlzXG4gKiAgIGRlbGliZXJhdGVseSBOT1QgZmFpbC1vcGVuOiBhbiB1bnZlcmlmaWFibGUgY3JlZGVudGlhbCBpcyBub3QgYW4gaWRlbnRpdHkuXG4gKlxuICogVGhlIGV4Y2hhbmdlIHJlcXVlc3QgaXRzZWxmIGNhcnJpZXMgdGhpcyBzZXJ2aWNlJ3Mgb3duIHNpZ25lZCBzZXJ2aWNlIHRva2VuLFxuICogd2hpY2ggaXMgd2hhdCBleGVtcHRzIGl0IGZyb20gcGxhdGZvcm0ncyBJUC1rZXllZCBgL2F1dGhgIHJhdGUgbGltaXRlciAoZXZlcnlcbiAqIHBvZCdzIGV4Y2hhbmdlcyB3b3VsZCBvdGhlcndpc2Ugc2hhcmUgb25lIGJ1Y2tldCkgYW5kIG5hbWVzIHRoZSBjYWxsZXIgaW5cbiAqIHBsYXRmb3JtJ3MgYXVkaXQgdHJhaWwuXG4gKi9cblxuaW1wb3J0IHsgZ2V0U2VydmljZUF1dGhIZWFkZXIsIFNZU1RFTV9PUkdfSUQgfSBmcm9tICcuLi9taWRkbGV3YXJlL2F1dGguanMnO1xuaW1wb3J0IHsgSW50ZXJuYWxIdHRwQ2xpZW50IH0gZnJvbSAnLi4vc2VydmljZXMvaHR0cC1jbGllbnQuanMnO1xuaW1wb3J0IHsgaGFzaEFwaUtleSB9IGZyb20gJy4uL3V0aWxzL2FwaS1rZXkuanMnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IGVtaXRDb3VudGVyIH0gZnJvbSAnLi4vdXRpbHMvbWV0cmljLWVtaXR0ZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ2FwaS1rZXktZXhjaGFuZ2UnKTtcblxuLyoqIE1vc3QgZGlzdGluY3Qga2V5cyBoZWxkIGluIHRoZSBwcm9jZXNzLXdpZGUgY2FjaGUgYmVmb3JlIHRoZSBvbGRlc3QgYXJlIGRyb3BwZWQuICovXG5jb25zdCBNQVhfQ0FDSEVEX0tFWVMgPSAxMDAwO1xuXG4vKiogSG93IGxvbmcgYSBwbGF0Zm9ybSBSRUpFQ1RJT04gaXMgcmVtZW1iZXJlZCAobXMpLiAqL1xuY29uc3QgTkVHQVRJVkVfVFRMX01TID0gMzBfMDAwO1xuXG4vKiogRnJhY3Rpb24gb2YgYSB0b2tlbidzIGxpZmUgYWZ0ZXIgd2hpY2ggYSBiYWNrZ3JvdW5kIHJlLWV4Y2hhbmdlIGlzIHN0YXJ0ZWQuICovXG5jb25zdCBSRUZSRVNIX0FUX0ZSQUNUSU9OID0gMC43NTtcblxuLyoqIMKxIGppdHRlciBhcHBsaWVkIHRvIHRoZSByZWZyZXNoIHBvaW50LCBhcyBhIGZyYWN0aW9uIG9mIHRoZSB0b2tlbidzIGxpZmUuXG4gKiAgV2lkZSBlbm91Z2ggdG8gZGUtc3luY2hyb25pemUgYSBmbGVldCwgbmFycm93IGVub3VnaCB0byBsZWF2ZSBhIGNsZWFyIHRhaWwgb2ZcbiAqICBzdGlsbC12YWxpZCB0b2tlbiBhZnRlciB0aGUgbGF0ZXN0IHBvc3NpYmxlIHJlZnJlc2guICovXG5jb25zdCBSRUZSRVNIX0pJVFRFUl9GUkFDVElPTiA9IDAuMTtcblxuLyoqIFRoZSBrZXkgd2FzIHByZXNlbnRlZCB0byBwbGF0Zm9ybSBhbmQgcmVmdXNlZCAodW5rbm93biwgcmV2b2tlZCwgZXhwaXJlZCkuICovXG5leHBvcnQgY2xhc3MgQXBpS2V5UmVqZWN0ZWRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3IobWVzc2FnZSA9ICdBY2Nlc3Mga2V5IGlzIGludmFsaWQsIHJldm9rZWQgb3IgZXhwaXJlZCcpIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnQXBpS2V5UmVqZWN0ZWRFcnJvcic7XG4gIH1cbn1cblxuLyoqIFBsYXRmb3JtIGNvdWxkIG5vdCBiZSByZWFjaGVkIChvciBhbnN3ZXJlZCA1eHgpIOKAlCB0aGUga2V5J3MgdmFsaWRpdHkgaXMgdW5rbm93bi4gKi9cbmV4cG9ydCBjbGFzcyBBcGlLZXlFeGNoYW5nZVVuYXZhaWxhYmxlRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UgPSAnQWNjZXNzLWtleSB2ZXJpZmljYXRpb24gaXMgdGVtcG9yYXJpbHkgdW5hdmFpbGFibGUnKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ0FwaUtleUV4Y2hhbmdlVW5hdmFpbGFibGVFcnJvcic7XG4gIH1cbn1cblxuaW50ZXJmYWNlIENhY2hlRW50cnkge1xuICAvKiogVGhlIGV4Y2hhbmdlZCBKV1QuICovXG4gIHRva2VuOiBzdHJpbmc7XG4gIC8qKiBFcG9jaCBtcyBhdCB3aGljaCB0aGUgSldUIHN0b3BzIGJlaW5nIHVzYWJsZS4gKi9cbiAgZXhwaXJlc0F0OiBudW1iZXI7XG4gIC8qKiBFcG9jaCBtcyBhZnRlciB3aGljaCBhIGJhY2tncm91bmQgcmUtZXhjaGFuZ2Ugc2hvdWxkIGJlIHN0YXJ0ZWQuICovXG4gIHJlZnJlc2hBdDogbnVtYmVyO1xufVxuXG4vKiogQ2FjaGUgb2Ygc3VjY2Vzc2Z1bCBleGNoYW5nZXMsIGtleWVkIGJ5IGBzaGEyNTYoa2V5KWAg4oCUIG5ldmVyIGJ5IHRoZSBrZXkgaXRzZWxmLiAqL1xuY29uc3QgdG9rZW5DYWNoZSA9IG5ldyBNYXA8c3RyaW5nLCBDYWNoZUVudHJ5PigpO1xuLyoqIENhY2hlIG9mIHJlamVjdGlvbnMsIGtleWVkIHRoZSBzYW1lIHdheTsgdmFsdWUgaXMgdGhlIGVwb2NoIG1zIGl0IGV4cGlyZXMuICovXG5jb25zdCByZWplY3Rpb25DYWNoZSA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7XG4vKiogSW4tZmxpZ2h0IGV4Y2hhbmdlcywgc28gY29uY3VycmVudCByZXF1ZXN0cyBmb3Igb25lIGtleSBzaGFyZSBhIHJvdW5kLXRyaXAuICovXG5jb25zdCBpbkZsaWdodCA9IG5ldyBNYXA8c3RyaW5nLCBQcm9taXNlPENhY2hlRW50cnk+PigpO1xuXG4vKiogVGhlIHNlcnZpY2UgbmFtZSBtaW50ZWQgaW50byB0aGUgZXhjaGFuZ2UgY2FsbCdzIG93biBzZXJ2aWNlIHRva2VuLiAqL1xubGV0IHNlcnZpY2VOYW1lID0gcHJvY2Vzcy5lbnYuU0VSVklDRV9OQU1FIHx8ICd1bmtub3duJztcblxuLyoqXG4gKiBOYW1lIHRoaXMgcHJvY2VzcyBpbiB0aGUgZXhjaGFuZ2UgY2FsbCdzIHNlcnZpY2UgdG9rZW4uIENhbGxlZCBmcm9tXG4gKiBgd2lyZVNlcnZpY2VTZWN1cml0eWAgYXQgYm9vdCAoYW5kIGJ5IHRoZSBwbHVnaW4gc2VydmljZSwgd2hpY2ggd2lyZXMgaXRzXG4gKiBib290IHNlY3VyaXR5IGJ5IGhhbmQpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0QXBpS2V5RXhjaGFuZ2VTZXJ2aWNlTmFtZShuYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgc2VydmljZU5hbWUgPSBuYW1lO1xufVxuXG4vKiogUGxhdGZvcm0ncyBpbi1jbHVzdGVyIGFkZHJlc3Mg4oCUIHRoZSBzYW1lIGVudiBwYWlyIGV2ZXJ5IG90aGVyIGNhbGxlciB1c2VzLiAqL1xuZnVuY3Rpb24gcGxhdGZvcm1DbGllbnQoKTogSW50ZXJuYWxIdHRwQ2xpZW50IHtcbiAgcmV0dXJuIG5ldyBJbnRlcm5hbEh0dHBDbGllbnQoe1xuICAgIGhvc3Q6IHByb2Nlc3MuZW52LlBMQVRGT1JNX1NFUlZJQ0VfSE9TVCB8fCAncGxhdGZvcm0nLFxuICAgIHBvcnQ6IHBhcnNlSW50KHByb2Nlc3MuZW52LlBMQVRGT1JNX1NFUlZJQ0VfUE9SVCB8fCAnMzAwMCcsIDEwKSxcbiAgICB0aW1lb3V0OiBwYXJzZUludChwcm9jZXNzLmVudi5BUElfS0VZX0VYQ0hBTkdFX1RJTUVPVVRfTVMgfHwgJzMwMDAnLCAxMCksXG4gIH0pO1xufVxuXG4vKiogVGhlIGVwb2NoIG1zIGF0IHdoaWNoIGEgdG9rZW4gd2l0aCBgdHRsU2Vjb25kc2Agb2YgbGlmZSBzaG91bGQgYmUgcmVmcmVzaGVkIGVhcmx5LiAqL1xuZnVuY3Rpb24gaml0dGVyZWRSZWZyZXNoQXQoaXNzdWVkQXQ6IG51bWJlciwgdHRsU2Vjb25kczogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3QgbGlmZU1zID0gdHRsU2Vjb25kcyAqIDEwMDA7XG4gIGNvbnN0IGppdHRlciA9IChNYXRoLnJhbmRvbSgpICogMiAtIDEpICogUkVGUkVTSF9KSVRURVJfRlJBQ1RJT047XG4gIC8vIENsYW1wIGludG8gKDAsIDEpIHNvIHRoZSByZWZyZXNoIHBvaW50IGlzIGFsd2F5cyBiZWZvcmUgZXhwaXJ5IGFuZCBuZXZlciBpblxuICAvLyB0aGUgcGFzdCwgd2hhdGV2ZXIgVFRMIHBsYXRmb3JtIHJldHVybnMuXG4gIGNvbnN0IGZyYWN0aW9uID0gTWF0aC5taW4oMC45NSwgTWF0aC5tYXgoMC4xLCBSRUZSRVNIX0FUX0ZSQUNUSU9OICsgaml0dGVyKSk7XG4gIHJldHVybiBpc3N1ZWRBdCArIGxpZmVNcyAqIGZyYWN0aW9uO1xufVxuXG4vKiogRHJvcCB0aGUgb2xkZXN0IGVudHJpZXMgb25jZSB0aGUgY2FjaGUgaXMgb3ZlciBpdHMgY2FwIChrZXlzIGFyZSBsb25nLWxpdmVkLCB0b2tlbnMgYXJlIG5vdCkuICovXG5mdW5jdGlvbiBldmljdElmRnVsbCgpOiB2b2lkIHtcbiAgaWYgKHRva2VuQ2FjaGUuc2l6ZSA8PSBNQVhfQ0FDSEVEX0tFWVMpIHJldHVybjtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgZm9yIChjb25zdCBbaGFzaCwgZW50cnldIG9mIHRva2VuQ2FjaGUpIHtcbiAgICBpZiAoZW50cnkuZXhwaXJlc0F0IDw9IG5vdykgdG9rZW5DYWNoZS5kZWxldGUoaGFzaCk7XG4gIH1cbiAgLy8gTWFwIHByZXNlcnZlcyBpbnNlcnRpb24gb3JkZXIg4oCUIGRyb3AgZnJvbSB0aGUgZnJvbnQgdW50aWwgYmFjayB1bmRlciB0aGUgY2FwLlxuICB3aGlsZSAodG9rZW5DYWNoZS5zaXplID4gTUFYX0NBQ0hFRF9LRVlTKSB7XG4gICAgY29uc3Qgb2xkZXN0ID0gdG9rZW5DYWNoZS5rZXlzKCkubmV4dCgpO1xuICAgIGlmIChvbGRlc3QuZG9uZSkgYnJlYWs7XG4gICAgdG9rZW5DYWNoZS5kZWxldGUob2xkZXN0LnZhbHVlKTtcbiAgfVxufVxuXG5pbnRlcmZhY2UgRXhjaGFuZ2VSZXNwb25zZSB7XG4gIHN1Y2Nlc3M/OiBib29sZWFuO1xuICBkYXRhPzogeyBhY2Nlc3NUb2tlbj86IHN0cmluZzsgZXhwaXJlc0luPzogbnVtYmVyIH07XG59XG5cbi8qKiBPbmUgcmVhbCByb3VuZC10cmlwIHRvIHBsYXRmb3JtLiBUaHJvd3MgdGhlIHR3byB0eXBlZCBlcnJvcnMgYWJvdmUuICovXG5hc3luYyBmdW5jdGlvbiBwZXJmb3JtRXhjaGFuZ2Uoa2V5OiBzdHJpbmcsIGhhc2g6IHN0cmluZyk6IFByb21pc2U8Q2FjaGVFbnRyeT4ge1xuICBjb25zdCBjbGllbnQgPSBwbGF0Zm9ybUNsaWVudCgpO1xuICBsZXQgc3RhdHVzOiBudW1iZXI7XG4gIGxldCBib2R5OiBFeGNoYW5nZVJlc3BvbnNlO1xuICB0cnkge1xuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGNsaWVudC5wb3N0PEV4Y2hhbmdlUmVzcG9uc2U+KCcvYXV0aC90b2tlbi9leGNoYW5nZScsIHsga2V5IH0sIHtcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgQXV0aG9yaXphdGlvbjogZ2V0U2VydmljZUF1dGhIZWFkZXIoeyBzZXJ2aWNlTmFtZSwgcm9sZTogJ21lbWJlcicsIG9yZ0lkOiBTWVNURU1fT1JHX0lEIH0pLFxuICAgICAgfSxcbiAgICAgIC8vIFRoZSBleGNoYW5nZSBpcyBhIHB1cmUgcmVhZCBvZiB0aGUga2V5IHJlY29yZCBwbHVzIGEgdG9rZW4gbWludCDigJQgc2FmZSB0b1xuICAgICAgLy8gcmV0cnksIGFuZCBhIGxvc3QgcmV0cnkgb25seSBjb3N0cyBvbmUgZXh0cmEgbWludC5cbiAgICAgIGlkZW1wb3RlbnQ6IHRydWUsXG4gICAgfSk7XG4gICAgc3RhdHVzID0gcmVzLnN0YXR1c0NvZGU7XG4gICAgYm9keSA9IHJlcy5ib2R5O1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBlbWl0Q291bnRlcignYXBpX2tleV9leGNoYW5nZV90b3RhbCcsIHsgcmVzdWx0OiAndW5hdmFpbGFibGUnIH0pO1xuICAgIGVtaXRDb3VudGVyKCdhcGlfa2V5X2V4Y2hhbmdlX2ZhaWx1cmVzX3RvdGFsJywgeyByZWFzb246ICd1bmF2YWlsYWJsZScgfSk7XG4gICAgbG9nZ2VyLndhcm4oJ0FjY2Vzcy1rZXkgZXhjaGFuZ2UgZmFpbGVkIHRvIHJlYWNoIHBsYXRmb3JtJywge1xuICAgICAgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSxcbiAgICB9KTtcbiAgICB0aHJvdyBuZXcgQXBpS2V5RXhjaGFuZ2VVbmF2YWlsYWJsZUVycm9yKCk7XG4gIH1cblxuICBpZiAoc3RhdHVzID09PSA0MDEgfHwgc3RhdHVzID09PSA0MDMgfHwgc3RhdHVzID09PSA0MDAgfHwgc3RhdHVzID09PSA0MDQpIHtcbiAgICByZWplY3Rpb25DYWNoZS5zZXQoaGFzaCwgRGF0ZS5ub3coKSArIE5FR0FUSVZFX1RUTF9NUyk7XG4gICAgZW1pdENvdW50ZXIoJ2FwaV9rZXlfZXhjaGFuZ2VfdG90YWwnLCB7IHJlc3VsdDogJ3JlamVjdGVkJyB9KTtcbiAgICBlbWl0Q291bnRlcignYXBpX2tleV9leGNoYW5nZV9mYWlsdXJlc190b3RhbCcsIHsgcmVhc29uOiAncmVqZWN0ZWQnIH0pO1xuICAgIHRocm93IG5ldyBBcGlLZXlSZWplY3RlZEVycm9yKCk7XG4gIH1cblxuICBjb25zdCBhY2Nlc3NUb2tlbiA9IGJvZHk/LmRhdGE/LmFjY2Vzc1Rva2VuO1xuICBpZiAoc3RhdHVzID49IDQwMCB8fCB0eXBlb2YgYWNjZXNzVG9rZW4gIT09ICdzdHJpbmcnIHx8IGFjY2Vzc1Rva2VuLmxlbmd0aCA9PT0gMCkge1xuICAgIC8vIDQyOSBhbmQgNXh4IGxhbmQgaGVyZTogcGxhdGZvcm0gaXMgdXAgYnV0IGNhbid0IGFuc3dlciByaWdodCBub3csIHdoaWNoIGlzXG4gICAgLy8gXCJ1bmtub3duXCIsIG5vdCBcImludmFsaWRcIiDigJQgbmV2ZXIgY2FjaGUgaXQgYXMgYSByZWplY3Rpb24uXG4gICAgZW1pdENvdW50ZXIoJ2FwaV9rZXlfZXhjaGFuZ2VfdG90YWwnLCB7IHJlc3VsdDogJ3VuYXZhaWxhYmxlJyB9KTtcbiAgICBlbWl0Q291bnRlcignYXBpX2tleV9leGNoYW5nZV9mYWlsdXJlc190b3RhbCcsIHsgcmVhc29uOiBzdGF0dXMgPT09IDQyOSA/ICd0aHJvdHRsZWQnIDogJ3NlcnZlcl9lcnJvcicgfSk7XG4gICAgdGhyb3cgbmV3IEFwaUtleUV4Y2hhbmdlVW5hdmFpbGFibGVFcnJvcigpO1xuICB9XG5cbiAgY29uc3QgdHRsU2Vjb25kcyA9IHR5cGVvZiBib2R5Py5kYXRhPy5leHBpcmVzSW4gPT09ICdudW1iZXInICYmIGJvZHkuZGF0YS5leHBpcmVzSW4gPiAwXG4gICAgPyBib2R5LmRhdGEuZXhwaXJlc0luXG4gICAgOiA2MDtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgY29uc3QgZW50cnk6IENhY2hlRW50cnkgPSB7XG4gICAgdG9rZW46IGFjY2Vzc1Rva2VuLFxuICAgIGV4cGlyZXNBdDogbm93ICsgdHRsU2Vjb25kcyAqIDEwMDAsXG4gICAgcmVmcmVzaEF0OiBqaXR0ZXJlZFJlZnJlc2hBdChub3csIHR0bFNlY29uZHMpLFxuICB9O1xuICB0b2tlbkNhY2hlLnNldChoYXNoLCBlbnRyeSk7XG4gIHJlamVjdGlvbkNhY2hlLmRlbGV0ZShoYXNoKTtcbiAgZXZpY3RJZkZ1bGwoKTtcbiAgZW1pdENvdW50ZXIoJ2FwaV9rZXlfZXhjaGFuZ2VfdG90YWwnLCB7IHJlc3VsdDogJ3N1Y2Nlc3MnIH0pO1xuICByZXR1cm4gZW50cnk7XG59XG5cbi8qKiBFeGNoYW5nZSwgc2hhcmluZyBhIHNpbmdsZSBpbi1mbGlnaHQgcm91bmQtdHJpcCBwZXIga2V5LiAqL1xuZnVuY3Rpb24gZXhjaGFuZ2VPbmNlKGtleTogc3RyaW5nLCBoYXNoOiBzdHJpbmcpOiBQcm9taXNlPENhY2hlRW50cnk+IHtcbiAgY29uc3QgcGVuZGluZyA9IGluRmxpZ2h0LmdldChoYXNoKTtcbiAgaWYgKHBlbmRpbmcpIHJldHVybiBwZW5kaW5nO1xuICBjb25zdCBwcm9taXNlID0gcGVyZm9ybUV4Y2hhbmdlKGtleSwgaGFzaCkuZmluYWxseSgoKSA9PiBpbkZsaWdodC5kZWxldGUoaGFzaCkpO1xuICBpbkZsaWdodC5zZXQoaGFzaCwgcHJvbWlzZSk7XG4gIHJldHVybiBwcm9taXNlO1xufVxuXG4vKipcbiAqIFRoZSBKV1QgZm9yIGBrZXlgLCBmcm9tIGNhY2hlIHdoZW4gcG9zc2libGUuIFRocm93cyB7QGxpbmsgQXBpS2V5UmVqZWN0ZWRFcnJvcn1cbiAqIHdoZW4gcGxhdGZvcm0gcmVmdXNlZCB0aGUga2V5IGFuZCB7QGxpbmsgQXBpS2V5RXhjaGFuZ2VVbmF2YWlsYWJsZUVycm9yfSB3aGVuXG4gKiBpdCBjb3VsZCBub3QgYmUgYXNrZWQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleGNoYW5nZUFwaUtleShrZXk6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IGhhc2ggPSBoYXNoQXBpS2V5KGtleSk7XG4gIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG5cbiAgY29uc3QgcmVqZWN0ZWRVbnRpbCA9IHJlamVjdGlvbkNhY2hlLmdldChoYXNoKTtcbiAgaWYgKHJlamVjdGVkVW50aWwgIT09IHVuZGVmaW5lZCkge1xuICAgIGlmIChyZWplY3RlZFVudGlsID4gbm93KSB7XG4gICAgICBlbWl0Q291bnRlcignYXBpX2tleV9leGNoYW5nZV9jYWNoZV90b3RhbCcsIHsgcmVzdWx0OiAncmVqZWN0ZWQnIH0pO1xuICAgICAgdGhyb3cgbmV3IEFwaUtleVJlamVjdGVkRXJyb3IoKTtcbiAgICB9XG4gICAgcmVqZWN0aW9uQ2FjaGUuZGVsZXRlKGhhc2gpO1xuICB9XG5cbiAgY29uc3QgY2FjaGVkID0gdG9rZW5DYWNoZS5nZXQoaGFzaCk7XG4gIGlmIChjYWNoZWQgJiYgY2FjaGVkLmV4cGlyZXNBdCA+IG5vdykge1xuICAgIGlmIChjYWNoZWQucmVmcmVzaEF0IDw9IG5vdyAmJiAhaW5GbGlnaHQuaGFzKGhhc2gpKSB7XG4gICAgICAvLyBFYXJseSByZWZyZXNoOiB3YXJtIHRoZSBjYWNoZSBXSVRIT1VUIG1ha2luZyB0aGlzIHJlcXVlc3Qgd2FpdC4gQSBmYWlsZWRcbiAgICAgIC8vIGJhY2tncm91bmQgcmVmcmVzaCBpcyBub3QgdGhpcyByZXF1ZXN0J3MgcHJvYmxlbSDigJQgdGhlIGNhY2hlZCB0b2tlbiBpc1xuICAgICAgLy8gc3RpbGwgdmFsaWQsIGFuZCB0aGUgbmV4dCByZXF1ZXN0IHBhc3QgZXhwaXJ5IGV4Y2hhbmdlcyBpbmxpbmUuXG4gICAgICBlbWl0Q291bnRlcignYXBpX2tleV9leGNoYW5nZV9jYWNoZV90b3RhbCcsIHsgcmVzdWx0OiAncmVmcmVzaCcgfSk7XG4gICAgICB2b2lkIGV4Y2hhbmdlT25jZShrZXksIGhhc2gpLmNhdGNoKCgpID0+IHsgLyogbG9nZ2VkICsgY291bnRlZCBpbiBwZXJmb3JtRXhjaGFuZ2UgKi8gfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGVtaXRDb3VudGVyKCdhcGlfa2V5X2V4Y2hhbmdlX2NhY2hlX3RvdGFsJywgeyByZXN1bHQ6ICdoaXQnIH0pO1xuICAgIH1cbiAgICByZXR1cm4gY2FjaGVkLnRva2VuO1xuICB9XG5cbiAgZW1pdENvdW50ZXIoJ2FwaV9rZXlfZXhjaGFuZ2VfY2FjaGVfdG90YWwnLCB7IHJlc3VsdDogJ21pc3MnIH0pO1xuICByZXR1cm4gKGF3YWl0IGV4Y2hhbmdlT25jZShrZXksIGhhc2gpKS50b2tlbjtcbn1cblxuLyoqXG4gKiBEcm9wIGV2ZXJ5IGNhY2hlZCBleGNoYW5nZS4gRm9yIHRlc3RzIGFuZCBmb3IgYSBwcm9jZXNzIHRoYXQgd2FudHMgdG8gZm9yY2VcbiAqIHJlLXZhbGlkYXRpb247IHByb2R1Y3Rpb24gcmVsaWVzIG9uIHRoZSBUVExzIGFib3ZlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRBcGlLZXlFeGNoYW5nZUNhY2hlKCk6IHZvaWQge1xuICB0b2tlbkNhY2hlLmNsZWFyKCk7XG4gIHJlamVjdGlvbkNhY2hlLmNsZWFyKCk7XG4gIGluRmxpZ2h0LmNsZWFyKCk7XG59XG4iXX0=
|
|
@@ -67,10 +67,9 @@ export declare function createRedisAuditSpool(redis: RedisListClient, opts?: {
|
|
|
67
67
|
key?: string;
|
|
68
68
|
}): AuditSpool;
|
|
69
69
|
/**
|
|
70
|
-
* Build a Redis-backed audit spool from the ambient `REDIS_URL` / `
|
|
70
|
+
* Build a Redis-backed audit spool from the ambient `REDIS_URL` / `REDIS_SENTINELS`
|
|
71
71
|
* env via the shared `createEnvRedisClient`. Returns `null` when Redis is not
|
|
72
|
-
* configured
|
|
73
|
-
* spool (pre-spool best-effort behavior), never crashing. The shared helper loads
|
|
72
|
+
* configured — the caller then runs without a spool (best-effort behavior). The shared helper loads
|
|
74
73
|
* ioredis via `createRequire`, so this stays importable where Redis isn't present.
|
|
75
74
|
*/
|
|
76
75
|
export declare function createEnvRedisAuditSpool(opts?: {
|