@pipeline-builder/api-server 4.4.3 → 4.4.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/lib/api/ai-call-reservation.d.ts +95 -0
- package/lib/api/ai-call-reservation.js +121 -0
- package/lib/api/app-factory.js +13 -5
- package/lib/api/check-quota.d.ts +47 -3
- package/lib/api/check-quota.js +203 -49
- package/lib/api/idempotency-middleware.d.ts +22 -0
- package/lib/api/idempotency-middleware.js +75 -23
- package/lib/api/index.d.ts +3 -2
- package/lib/api/index.js +4 -3
- package/lib/api/meter-quota.d.ts +42 -5
- package/lib/api/meter-quota.js +63 -11
- package/lib/api/metrics.d.ts +9 -0
- package/lib/api/metrics.js +14 -1
- package/lib/api/quota-reservation.d.ts +42 -3
- package/lib/api/quota-reservation.js +46 -19
- package/lib/api/server.js +5 -2
- package/lib/testing/exports.json +13 -0
- package/package.json +6 -5
package/lib/api/check-quota.js
CHANGED
|
@@ -1,33 +1,160 @@
|
|
|
1
1
|
// Copyright 2026 Pipeline Builder Contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import {
|
|
3
|
+
import { armDeferredQuotaCheck, callQuotaExemption, createLogger, emitCounter, enforcedQuotaLimit, envInt, getQuotaServiceAuthHeader, isQuotaExemptRoute, meteredQuotaTypeFor, sendQuotaExceeded, errorMessage, tagRouteGate } from '@pipeline-builder/api-core';
|
|
4
4
|
import { getContext } from './get-context.js';
|
|
5
|
+
import { hasCompletedIdempotentResponse } from './idempotency-middleware.js';
|
|
5
6
|
const logger = createLogger('check-quota');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
// PER-POD CHECK CACHE.
|
|
8
|
+
//
|
|
9
|
+
// The gate used to ask the quota service on EVERY metered request — a GET, a
|
|
10
|
+
// JWT verify and a hierarchy resolve over there, per request here. A reading is
|
|
11
|
+
// now reused per (org, quota type):
|
|
12
|
+
// - an ALLOWED reading for `QUOTA_CHECK_CACHE_TTL_MS` (default 5000; 0 turns
|
|
13
|
+
// the cache off), and only while this pod has passed fewer requests on it
|
|
14
|
+
// than the reading had headroom for (`enforcedQuotaLimit − used`), so one
|
|
15
|
+
// pod cannot run far past a nearly-full counter on a stale "yes";
|
|
16
|
+
// - a DENIED reading until the period resets, capped at
|
|
17
|
+
// `QUOTA_CHECK_DENIED_CACHE_MS` (default 60000) so an upgrade, a bundle
|
|
18
|
+
// purchase or an operator reset is honoured within a minute;
|
|
19
|
+
// - a fail-open sentinel is NEVER cached — recovery is seen on the next request.
|
|
20
|
+
// Staleness is bounded and accepted: the counter itself never exceeds the cap
|
|
21
|
+
// (the metered increment admits only what fits and records the rest as
|
|
22
|
+
// overshoot), so a stale "yes" costs at most a few unbilled-over-cap requests
|
|
23
|
+
// per pod per TTL, never an unbounded run. Concurrent misses for one key share
|
|
24
|
+
// a single in-flight read.
|
|
25
|
+
/** TTL (ms) of a cached ALLOWED check. `0` disables the cache. */
|
|
26
|
+
export const QUOTA_CHECK_CACHE_TTL_MS = envInt('QUOTA_CHECK_CACHE_TTL_MS', 5000, { min: 0 });
|
|
27
|
+
/** Longest (ms) a DENIED check is reused before re-asking (it never outlives `resetAt`). */
|
|
28
|
+
export const QUOTA_CHECK_DENIED_CACHE_MS = envInt('QUOTA_CHECK_DENIED_CACHE_MS', 60000, { min: 0 });
|
|
29
|
+
/** Bound on cached (org, type) readings per pod. */
|
|
30
|
+
const CHECK_CACHE_MAX_ENTRIES = 10_000;
|
|
31
|
+
const checkCache = new Map();
|
|
32
|
+
const inFlightChecks = new Map();
|
|
33
|
+
/** Drop every cached check (tests; also a safe operational reset). */
|
|
34
|
+
export function clearQuotaCheckCache() {
|
|
35
|
+
checkCache.clear();
|
|
36
|
+
inFlightChecks.clear();
|
|
37
|
+
}
|
|
38
|
+
/** A usable cached reading for `key`, counting this request against its headroom. */
|
|
39
|
+
function cachedCheck(key, quotaType) {
|
|
40
|
+
const hit = checkCache.get(key);
|
|
41
|
+
if (!hit)
|
|
42
|
+
return null;
|
|
43
|
+
if (hit.expiresAt <= Date.now()) {
|
|
44
|
+
checkCache.delete(key);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (!hit.status.allowed)
|
|
48
|
+
return hit.status;
|
|
49
|
+
if (hit.status.limit !== -1) {
|
|
50
|
+
const headroom = enforcedQuotaLimit(quotaType, hit.status.limit) - hit.status.used;
|
|
51
|
+
if (hit.passes >= headroom) {
|
|
52
|
+
checkCache.delete(key);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
hit.passes += 1;
|
|
57
|
+
return hit.status;
|
|
58
|
+
}
|
|
59
|
+
function rememberCheck(key, status) {
|
|
60
|
+
if (!status || status.failOpen)
|
|
61
|
+
return;
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
let expiresAt;
|
|
64
|
+
if (status.allowed) {
|
|
65
|
+
if (QUOTA_CHECK_CACHE_TTL_MS === 0)
|
|
66
|
+
return;
|
|
67
|
+
expiresAt = now + QUOTA_CHECK_CACHE_TTL_MS;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (QUOTA_CHECK_DENIED_CACHE_MS === 0)
|
|
71
|
+
return;
|
|
72
|
+
const resetAt = Date.parse(status.resetAt);
|
|
73
|
+
expiresAt = Math.min(now + QUOTA_CHECK_DENIED_CACHE_MS, Number.isFinite(resetAt) ? resetAt : now);
|
|
74
|
+
if (expiresAt <= now)
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (checkCache.size >= CHECK_CACHE_MAX_ENTRIES) {
|
|
78
|
+
// Map preserves insertion order, so the first key is the oldest reading.
|
|
79
|
+
const oldest = checkCache.keys().next();
|
|
80
|
+
if (!oldest.done)
|
|
81
|
+
checkCache.delete(oldest.value);
|
|
82
|
+
}
|
|
83
|
+
// The request that fetched the reading is its first pass.
|
|
84
|
+
checkCache.set(key, { status, expiresAt, passes: status.allowed ? 1 : 0 });
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* One quota read for (org, type), shared by every concurrent miss on this pod.
|
|
88
|
+
* A request that waited on someone else's read then goes back through the
|
|
89
|
+
* cache, so it is counted against the reading's headroom like any other pass
|
|
90
|
+
* (`joined` stops it from waiting a second time).
|
|
91
|
+
*/
|
|
92
|
+
async function readCheck(quotaService, orgId, quotaType, joined = false) {
|
|
93
|
+
const key = `${orgId}\u0000${quotaType}`;
|
|
94
|
+
const cached = cachedCheck(key, quotaType);
|
|
95
|
+
if (cached)
|
|
96
|
+
return cached;
|
|
97
|
+
const pending = inFlightChecks.get(key);
|
|
98
|
+
if (pending) {
|
|
99
|
+
if (joined)
|
|
100
|
+
return pending;
|
|
101
|
+
const status = await pending;
|
|
102
|
+
// A fail-open (or uncacheable) reading has nothing to account against.
|
|
103
|
+
return checkCache.has(key) ? readCheck(quotaService, orgId, quotaType, true) : status;
|
|
104
|
+
}
|
|
105
|
+
// Authenticate as THIS service, not by forwarding the user's token: the user
|
|
106
|
+
// token 403s at the quota read route for custom roles without `quotas:read`,
|
|
107
|
+
// and (being non-service) counts against the quota service's per-IP limiter
|
|
108
|
+
// keyed on THIS pod's IP — both of which silently fail the gate open under load.
|
|
109
|
+
const read = quotaService.check(orgId, quotaType, getQuotaServiceAuthHeader(orgId))
|
|
110
|
+
.then((status) => {
|
|
111
|
+
rememberCheck(key, status);
|
|
112
|
+
return status;
|
|
113
|
+
})
|
|
114
|
+
.finally(() => inFlightChecks.delete(key));
|
|
115
|
+
inFlightChecks.set(key, read);
|
|
116
|
+
return read;
|
|
117
|
+
}
|
|
19
118
|
/**
|
|
20
119
|
* Create middleware that checks a specific quota type before proceeding.
|
|
21
120
|
*
|
|
22
|
-
* On quota exceeded, returns a 429 response with quota details
|
|
121
|
+
* On quota exceeded, returns a 429 response with quota details (quotaType,
|
|
122
|
+
* limit, used, resetAt, upgradeUrl — see api-core `sendQuotaExceeded`).
|
|
23
123
|
* On quota service failure, fails open (allows the request).
|
|
24
124
|
*
|
|
125
|
+
* `apiCalls` is split per request by principal: an org service account or a
|
|
126
|
+
* personal access key is checked against `automationCalls` instead
|
|
127
|
+
* ({@link meteredQuotaTypeFor}), the
|
|
128
|
+
* same type `meterQuotaOnSuccess` increments — so mount both with `'apiCalls'`.
|
|
129
|
+
*
|
|
130
|
+
* Skipped (passes through) exactly where the meter skips, so the gate never
|
|
131
|
+
* refuses a call the meter would not bill (`callQuotaExemption`):
|
|
132
|
+
* - a SERVICE PRINCIPAL (peer-service traffic is never metered, so a tenant at
|
|
133
|
+
* its limit must not break the internal calls its own earlier actions caused);
|
|
134
|
+
* - for `apiCalls`: an IMPERSONATION session (vendor staff diagnosing an
|
|
135
|
+
* exhausted org must not be locked out by it) and an ON-BEHALF hop (a user
|
|
136
|
+
* token forwarded by ask/pipeline with a signed marker — the originating
|
|
137
|
+
* request was already gated and metered);
|
|
138
|
+
* - an `apiCalls` request on the fleet-wide `QUOTA_EXEMPT_ROUTES` (console
|
|
139
|
+
* polling — an exhausted org must still render the banner saying so);
|
|
140
|
+
* - an idempotent RETRY whose original already completed (the replay is served
|
|
141
|
+
* from the idempotency cache, does no work and is never metered; refusing it
|
|
142
|
+
* would 429 a client for asking what its own earlier request returned).
|
|
143
|
+
*
|
|
144
|
+
* Readings are cached per pod (see the cache notes above), and the read itself
|
|
145
|
+
* has a short budget (api-core `QUOTA_CHECK_TIMEOUT_MS`, no retry) — a slow
|
|
146
|
+
* quota service fails open quickly instead of stalling every request.
|
|
147
|
+
*
|
|
148
|
+
* Mountable at the app-routes level (`app.use(prefix, …auth, checkQuota(...))`):
|
|
149
|
+
* the org comes from the request context when one is attached, else from the
|
|
150
|
+
* verified `req.user.organizationId` — the same fallback the meter uses.
|
|
151
|
+
*
|
|
25
152
|
* @param quotaService - Quota service client
|
|
26
153
|
* @param quotaType - Which quota to check (e.g. 'apiCalls', 'pipelines', 'plugins')
|
|
27
154
|
* @returns Express middleware
|
|
28
155
|
*/
|
|
29
|
-
export function checkQuota(quotaService,
|
|
30
|
-
return async (req, res, next) => {
|
|
156
|
+
export function checkQuota(quotaService, baseQuotaType) {
|
|
157
|
+
return tagRouteGate(async (req, res, next) => {
|
|
31
158
|
// The check is sent with THIS service's credentials, so it must only run for
|
|
32
159
|
// a VERIFIED caller — otherwise the org would come from the spoofable
|
|
33
160
|
// header-derived identity. Mounted after requireAuth everywhere; an
|
|
@@ -36,41 +163,35 @@ export function checkQuota(quotaService, quotaType) {
|
|
|
36
163
|
logger.warn('Quota check skipped: request is not authenticated');
|
|
37
164
|
return next();
|
|
38
165
|
}
|
|
166
|
+
const exemption = callQuotaExemption(req);
|
|
167
|
+
if (exemption === 'service' || (exemption && baseQuotaType === 'apiCalls'))
|
|
168
|
+
return next();
|
|
169
|
+
if (baseQuotaType === 'apiCalls' && isQuotaExemptRoute(req.method, requestPath(req)))
|
|
170
|
+
return next();
|
|
171
|
+
const quotaType = meteredQuotaTypeFor(req, baseQuotaType);
|
|
39
172
|
let orgId;
|
|
40
173
|
try {
|
|
41
|
-
|
|
42
|
-
orgId = ctx.identity.orgId;
|
|
174
|
+
orgId = getContext(req).identity.orgId;
|
|
43
175
|
}
|
|
44
176
|
catch {
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
if (!orgId) {
|
|
50
|
-
sendError(res, 400, 'Organization ID is required for quota check', ErrorCode.VALIDATION_ERROR);
|
|
51
|
-
return;
|
|
177
|
+
// No request context (platform attaches none): the verified auth org — the
|
|
178
|
+
// same fallback `meterQuotaOnSuccess` uses, so both read one counter.
|
|
179
|
+
orgId = req.user.organizationId;
|
|
52
180
|
}
|
|
181
|
+
// No org, nothing to bill (the meter skips it too). Not a 400: run deferred
|
|
182
|
+
// from `requireAuth` this precedes the route's own `requireOrgId`, and a
|
|
183
|
+
// route that needs no org (a person between orgs switching into one) must
|
|
184
|
+
// not be refused by a quota gate.
|
|
185
|
+
if (!orgId)
|
|
186
|
+
return next();
|
|
187
|
+
// A retry of a completed idempotent mutation is answered from the replay
|
|
188
|
+
// cache without running the handler — nothing to gate (one store read, only
|
|
189
|
+
// for requests that carry an Idempotency-Key).
|
|
190
|
+
if (await hasCompletedIdempotentResponse(req))
|
|
191
|
+
return next();
|
|
192
|
+
let quotaStatus;
|
|
53
193
|
try {
|
|
54
|
-
|
|
55
|
-
// user token 403s at the quota read route for custom roles without
|
|
56
|
-
// `quotas:read`, and (being non-service) counts against the quota
|
|
57
|
-
// service's per-IP limiter keyed on THIS pod's IP — both of which
|
|
58
|
-
// silently fail the gate open under load.
|
|
59
|
-
const quotaStatus = await quotaService.check(orgId, quotaType, getQuotaServiceAuthHeader(orgId));
|
|
60
|
-
if (!quotaStatus.allowed) {
|
|
61
|
-
logger.warn(`${quotaType} quota exceeded`, {
|
|
62
|
-
orgId,
|
|
63
|
-
limit: quotaStatus.limit,
|
|
64
|
-
used: quotaStatus.used,
|
|
65
|
-
});
|
|
66
|
-
// Route through the shared helper so the check-gate 429 emits the same
|
|
67
|
-
// Retry-After + X-Quota-* headers as the reserve-path 429 (the inline
|
|
68
|
-
// 429 this replaced omitted them). Custom message keeps the
|
|
69
|
-
// "contact your administrator" copy.
|
|
70
|
-
sendQuotaExceeded(res, quotaType, { type: quotaType, limit: quotaStatus.limit, used: quotaStatus.used, remaining: quotaStatus.remaining }, quotaStatus.resetAt, `${QUOTA_LABELS[quotaType]} quota exceeded. Please contact your administrator to increase your quota.`);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
next();
|
|
194
|
+
quotaStatus = await readCheck(quotaService, orgId, quotaType);
|
|
74
195
|
}
|
|
75
196
|
catch (error) {
|
|
76
197
|
// Fail open — allow the request if quota service is unavailable
|
|
@@ -83,8 +204,41 @@ export function checkQuota(quotaService, quotaType) {
|
|
|
83
204
|
// is ALERTABLE from one series regardless of which layer fell open. Only
|
|
84
205
|
// a log line here meant every org was silently un-billed for the duration.
|
|
85
206
|
emitCounter('quota_fail_open_total', { operation: 'check', reason: 'exception', quotaType });
|
|
86
|
-
next();
|
|
207
|
+
return next();
|
|
87
208
|
}
|
|
88
|
-
|
|
209
|
+
if (!quotaStatus.allowed) {
|
|
210
|
+
logger.warn(`${quotaType} quota exceeded`, {
|
|
211
|
+
orgId,
|
|
212
|
+
limit: quotaStatus.limit,
|
|
213
|
+
used: quotaStatus.used,
|
|
214
|
+
});
|
|
215
|
+
// Route through the shared helper so the check-gate 429 emits the same
|
|
216
|
+
// Retry-After + X-Quota-* headers and the same body (which quota, the
|
|
217
|
+
// reset date, the upgrade page) as the reserve-path 429.
|
|
218
|
+
sendQuotaExceeded(res, quotaType, { type: quotaType, limit: quotaStatus.limit, used: quotaStatus.used, remaining: quotaStatus.remaining }, quotaStatus.resetAt);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// Outside the try: a synchronous throw downstream must reach Express's error
|
|
222
|
+
// handling, not be mistaken for a quota-service failure and run next() twice.
|
|
223
|
+
next();
|
|
224
|
+
}, { kind: 'quota', mode: 'check', quotaType: baseQuotaType });
|
|
225
|
+
}
|
|
226
|
+
/** The service-local request path, without the query string. */
|
|
227
|
+
export function requestPath(req) {
|
|
228
|
+
return (req.originalUrl ?? req.url ?? '').split('?')[0] ?? '';
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The DEFERRED form of {@link checkQuota}: arms the check at mount time and lets
|
|
232
|
+
* `requireAuth` run it once the caller is verified (api-core
|
|
233
|
+
* `armDeferredQuotaCheck`). For a prefix mount ahead of routers that
|
|
234
|
+
* authenticate per route — use {@link apiCallsQuota}, which pairs it with the
|
|
235
|
+
* meter. Tagged as the check gate, so the route table records it.
|
|
236
|
+
*/
|
|
237
|
+
export function deferredCheckQuota(quotaService, quotaType) {
|
|
238
|
+
const check = checkQuota(quotaService, quotaType);
|
|
239
|
+
return tagRouteGate((_req, res, next) => {
|
|
240
|
+
armDeferredQuotaCheck(res, check);
|
|
241
|
+
next();
|
|
242
|
+
}, { kind: 'quota', mode: 'check', quotaType });
|
|
89
243
|
}
|
|
90
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2stcXVvdGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL2NoZWNrLXF1b3RhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLHlCQUF5QixFQUFFLFNBQVMsRUFBRSxpQkFBaUIsRUFBRSxZQUFZLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUd6SixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFOUMsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0FBRTNDLHlEQUF5RDtBQUN6RCxNQUFNLFlBQVksR0FBOEI7SUFDOUMsUUFBUSxFQUFFLFVBQVU7SUFDcEIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsU0FBUyxFQUFFLFVBQVU7SUFDckIsT0FBTyxFQUFFLFFBQVE7SUFDakIsWUFBWSxFQUFFLGtCQUFrQjtJQUNoQyxVQUFVLEVBQUUsV0FBVztJQUN2QixVQUFVLEVBQUUsWUFBWTtJQUN4QixpQkFBaUIsRUFBRSxtQkFBbUI7SUFDdEMsVUFBVSxFQUFFLG1CQUFtQjtJQUMvQixRQUFRLEVBQUUsZ0JBQWdCO0NBQzNCLENBQUM7QUFFRjs7Ozs7Ozs7O0dBU0c7QUFDSCxNQUFNLFVBQVUsVUFBVSxDQUN4QixZQUEwQixFQUMxQixTQUFvQjtJQUVwQixPQUFPLEtBQUssRUFBRSxHQUFZLEVBQUUsR0FBYSxFQUFFLElBQWtCLEVBQWlCLEVBQUU7UUFDOUUsNkVBQTZFO1FBQzdFLHNFQUFzRTtRQUN0RSxvRUFBb0U7UUFDcEUsNEVBQTRFO1FBQzVFLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDZCxNQUFNLENBQUMsSUFBSSxDQUFDLG1EQUFtRCxDQUFDLENBQUM7WUFDakUsT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUNoQixDQUFDO1FBRUQsSUFBSSxLQUF5QixDQUFDO1FBRTlCLElBQUksQ0FBQztZQUNILE1BQU0sR0FBRyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUM1QixLQUFLLEdBQUcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUM7UUFDN0IsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLGdFQUFnRTtZQUNoRSxNQUFNLENBQUMsSUFBSSxDQUFDLHNEQUFzRCxDQUFDLENBQUM7WUFDcEUsT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUNoQixDQUFDO1FBRUQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ1gsU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsNkNBQTZDLEVBQUUsU0FBUyxDQUFDLGdCQUFnQixDQUFDLENBQUM7WUFDL0YsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUM7WUFDSCx3RUFBd0U7WUFDeEUsbUVBQW1FO1lBQ25FLGtFQUFrRTtZQUNsRSxrRUFBa0U7WUFDbEUsMENBQTBDO1lBQzFDLE1BQU0sV0FBVyxHQUFHLE1BQU0sWUFBWSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsU0FBUyxFQUFFLHlCQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFFakcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDekIsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLFNBQVMsaUJBQWlCLEVBQUU7b0JBQ3pDLEtBQUs7b0JBQ0wsS0FBSyxFQUFFLFdBQVcsQ0FBQyxLQUFLO29CQUN4QixJQUFJLEVBQUUsV0FBVyxDQUFDLElBQUk7aUJBQ3ZCLENBQUMsQ0FBQztnQkFFSCx1RUFBdUU7Z0JBQ3ZFLHNFQUFzRTtnQkFDdEUsNERBQTREO2dCQUM1RCxxQ0FBcUM7Z0JBQ3JDLGlCQUFpQixDQUNmLEdBQUcsRUFDSCxTQUFTLEVBQ1QsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxXQUFXLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxXQUFXLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxXQUFXLENBQUMsU0FBUyxFQUFFLEVBQ3ZHLFdBQVcsQ0FBQyxPQUFPLEVBQ25CLEdBQUcsWUFBWSxDQUFDLFNBQVMsQ0FBQyw0RUFBNEUsQ0FDdkcsQ0FBQztnQkFDRixPQUFPO1lBQ1QsQ0FBQztZQUVELElBQUksRUFBRSxDQUFDO1FBQ1QsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixnRUFBZ0U7WUFDaEUsTUFBTSxDQUFDLElBQUksQ0FBQywwREFBMEQsRUFBRTtnQkFDdEUsS0FBSztnQkFDTCxTQUFTO2dCQUNULEtBQUssRUFBRSxZQUFZLENBQUMsS0FBSyxDQUFDO2FBQzNCLENBQUMsQ0FBQztZQUNILDBFQUEwRTtZQUMxRSx5RUFBeUU7WUFDekUsMkVBQTJFO1lBQzNFLFdBQVcsQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1lBQzdGLElBQUksRUFBRSxDQUFDO1FBQ1QsQ0FBQztJQUNILENBQUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgRXJyb3JDb2RlLCBjcmVhdGVMb2dnZXIsIGVtaXRDb3VudGVyLCBnZXRRdW90YVNlcnZpY2VBdXRoSGVhZGVyLCBzZW5kRXJyb3IsIHNlbmRRdW90YUV4Y2VlZGVkLCBlcnJvck1lc3NhZ2UgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFF1b3RhVHlwZSwgUXVvdGFTZXJ2aWNlIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0LCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyBnZXRDb250ZXh0IH0gZnJvbSAnLi9nZXQtY29udGV4dC5qcyc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignY2hlY2stcXVvdGEnKTtcblxuLyoqIEh1bWFuLXJlYWRhYmxlIGxhYmVscyBmb3IgcXVvdGEgZXhjZWVkZWQgbWVzc2FnZXMuICovXG5jb25zdCBRVU9UQV9MQUJFTFM6IFJlY29yZDxRdW90YVR5cGUsIHN0cmluZz4gPSB7XG4gIGFwaUNhbGxzOiAnQVBJIGNhbGwnLFxuICBhaUNhbGxzOiAnQUkgY2FsbCcsXG4gIHBpcGVsaW5lczogJ1BpcGVsaW5lJyxcbiAgcGx1Z2luczogJ1BsdWdpbicsXG4gIHN0b3JhZ2VCeXRlczogJ1JlZ2lzdHJ5IHN0b3JhZ2UnLFxuICBkYXNoYm9hcmRzOiAnRGFzaGJvYXJkJyxcbiAgYWxlcnRSdWxlczogJ0FsZXJ0IHJ1bGUnLFxuICBhbGVydERlc3RpbmF0aW9uczogJ0FsZXJ0IGRlc3RpbmF0aW9uJyxcbiAgaWRwQ29uZmlnczogJ0lkUCBjb25maWd1cmF0aW9uJyxcbiAgbGlzdGluZ3M6ICdQbHVnaW4gbGlzdGluZycsXG59O1xuXG4vKipcbiAqIENyZWF0ZSBtaWRkbGV3YXJlIHRoYXQgY2hlY2tzIGEgc3BlY2lmaWMgcXVvdGEgdHlwZSBiZWZvcmUgcHJvY2VlZGluZy5cbiAqXG4gKiBPbiBxdW90YSBleGNlZWRlZCwgcmV0dXJucyBhIDQyOSByZXNwb25zZSB3aXRoIHF1b3RhIGRldGFpbHMuXG4gKiBPbiBxdW90YSBzZXJ2aWNlIGZhaWx1cmUsIGZhaWxzIG9wZW4gKGFsbG93cyB0aGUgcmVxdWVzdCkuXG4gKlxuICogQHBhcmFtIHF1b3RhU2VydmljZSAtIFF1b3RhIHNlcnZpY2UgY2xpZW50XG4gKiBAcGFyYW0gcXVvdGFUeXBlIC0gV2hpY2ggcXVvdGEgdG8gY2hlY2sgKGUuZy4gJ2FwaUNhbGxzJywgJ3BpcGVsaW5lcycsICdwbHVnaW5zJylcbiAqIEByZXR1cm5zIEV4cHJlc3MgbWlkZGxld2FyZVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2hlY2tRdW90YShcbiAgcXVvdGFTZXJ2aWNlOiBRdW90YVNlcnZpY2UsXG4gIHF1b3RhVHlwZTogUXVvdGFUeXBlLFxuKSB7XG4gIHJldHVybiBhc3luYyAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAvLyBUaGUgY2hlY2sgaXMgc2VudCB3aXRoIFRISVMgc2VydmljZSdzIGNyZWRlbnRpYWxzLCBzbyBpdCBtdXN0IG9ubHkgcnVuIGZvclxuICAgIC8vIGEgVkVSSUZJRUQgY2FsbGVyIOKAlCBvdGhlcndpc2UgdGhlIG9yZyB3b3VsZCBjb21lIGZyb20gdGhlIHNwb29mYWJsZVxuICAgIC8vIGhlYWRlci1kZXJpdmVkIGlkZW50aXR5LiBNb3VudGVkIGFmdGVyIHJlcXVpcmVBdXRoIGV2ZXJ5d2hlcmU7IGFuXG4gICAgLy8gdW5hdXRoZW50aWNhdGVkIHJlcXVlc3QgaGVyZSBpcyBhIHdpcmluZyBidWcsIHNvIHNraXAgKGZhaWwgb3BlbikgbG91ZGx5LlxuICAgIGlmICghcmVxLnVzZXIpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdRdW90YSBjaGVjayBza2lwcGVkOiByZXF1ZXN0IGlzIG5vdCBhdXRoZW50aWNhdGVkJyk7XG4gICAgICByZXR1cm4gbmV4dCgpO1xuICAgIH1cblxuICAgIGxldCBvcmdJZDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGN0eCA9IGdldENvbnRleHQocmVxKTtcbiAgICAgIG9yZ0lkID0gY3R4LmlkZW50aXR5Lm9yZ0lkO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gQ29udGV4dCBtaWRkbGV3YXJlIG5vdCBhcHBsaWVkIOKAlCBmYWlsIG9wZW4gKGxvZyBhbmQgY29udGludWUpXG4gICAgICBsb2dnZXIud2FybignUXVvdGEgY2hlY2sgc2tpcHBlZDogcmVxdWVzdCBjb250ZXh0IG5vdCBpbml0aWFsaXplZCcpO1xuICAgICAgcmV0dXJuIG5leHQoKTtcbiAgICB9XG5cbiAgICBpZiAoIW9yZ0lkKSB7XG4gICAgICBzZW5kRXJyb3IocmVzLCA0MDAsICdPcmdhbml6YXRpb24gSUQgaXMgcmVxdWlyZWQgZm9yIHF1b3RhIGNoZWNrJywgRXJyb3JDb2RlLlZBTElEQVRJT05fRVJST1IpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICAvLyBBdXRoZW50aWNhdGUgYXMgVEhJUyBzZXJ2aWNlLCBub3QgYnkgZm9yd2FyZGluZyB0aGUgdXNlcidzIHRva2VuOiB0aGVcbiAgICAgIC8vIHVzZXIgdG9rZW4gNDAzcyBhdCB0aGUgcXVvdGEgcmVhZCByb3V0ZSBmb3IgY3VzdG9tIHJvbGVzIHdpdGhvdXRcbiAgICAgIC8vIGBxdW90YXM6cmVhZGAsIGFuZCAoYmVpbmcgbm9uLXNlcnZpY2UpIGNvdW50cyBhZ2FpbnN0IHRoZSBxdW90YVxuICAgICAgLy8gc2VydmljZSdzIHBlci1JUCBsaW1pdGVyIGtleWVkIG9uIFRISVMgcG9kJ3MgSVAg4oCUIGJvdGggb2Ygd2hpY2hcbiAgICAgIC8vIHNpbGVudGx5IGZhaWwgdGhlIGdhdGUgb3BlbiB1bmRlciBsb2FkLlxuICAgICAgY29uc3QgcXVvdGFTdGF0dXMgPSBhd2FpdCBxdW90YVNlcnZpY2UuY2hlY2sob3JnSWQsIHF1b3RhVHlwZSwgZ2V0UXVvdGFTZXJ2aWNlQXV0aEhlYWRlcihvcmdJZCkpO1xuXG4gICAgICBpZiAoIXF1b3RhU3RhdHVzLmFsbG93ZWQpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oYCR7cXVvdGFUeXBlfSBxdW90YSBleGNlZWRlZGAsIHtcbiAgICAgICAgICBvcmdJZCxcbiAgICAgICAgICBsaW1pdDogcXVvdGFTdGF0dXMubGltaXQsXG4gICAgICAgICAgdXNlZDogcXVvdGFTdGF0dXMudXNlZCxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gUm91dGUgdGhyb3VnaCB0aGUgc2hhcmVkIGhlbHBlciBzbyB0aGUgY2hlY2stZ2F0ZSA0MjkgZW1pdHMgdGhlIHNhbWVcbiAgICAgICAgLy8gUmV0cnktQWZ0ZXIgKyBYLVF1b3RhLSogaGVhZGVycyBhcyB0aGUgcmVzZXJ2ZS1wYXRoIDQyOSAodGhlIGlubGluZVxuICAgICAgICAvLyA0MjkgdGhpcyByZXBsYWNlZCBvbWl0dGVkIHRoZW0pLiBDdXN0b20gbWVzc2FnZSBrZWVwcyB0aGVcbiAgICAgICAgLy8gXCJjb250YWN0IHlvdXIgYWRtaW5pc3RyYXRvclwiIGNvcHkuXG4gICAgICAgIHNlbmRRdW90YUV4Y2VlZGVkKFxuICAgICAgICAgIHJlcyxcbiAgICAgICAgICBxdW90YVR5cGUsXG4gICAgICAgICAgeyB0eXBlOiBxdW90YVR5cGUsIGxpbWl0OiBxdW90YVN0YXR1cy5saW1pdCwgdXNlZDogcXVvdGFTdGF0dXMudXNlZCwgcmVtYWluaW5nOiBxdW90YVN0YXR1cy5yZW1haW5pbmcgfSxcbiAgICAgICAgICBxdW90YVN0YXR1cy5yZXNldEF0LFxuICAgICAgICAgIGAke1FVT1RBX0xBQkVMU1txdW90YVR5cGVdfSBxdW90YSBleGNlZWRlZC4gUGxlYXNlIGNvbnRhY3QgeW91ciBhZG1pbmlzdHJhdG9yIHRvIGluY3JlYXNlIHlvdXIgcXVvdGEuYCxcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBuZXh0KCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIEZhaWwgb3BlbiDigJQgYWxsb3cgdGhlIHJlcXVlc3QgaWYgcXVvdGEgc2VydmljZSBpcyB1bmF2YWlsYWJsZVxuICAgICAgbG9nZ2VyLndhcm4oJ1FVT1RBX0ZBSUxfT1BFTjogUXVvdGEgY2hlY2sgZXhjZXB0aW9uLCBhbGxvd2luZyByZXF1ZXN0Jywge1xuICAgICAgICBvcmdJZCxcbiAgICAgICAgcXVvdGFUeXBlLFxuICAgICAgICBlcnJvcjogZXJyb3JNZXNzYWdlKGVycm9yKSxcbiAgICAgIH0pO1xuICAgICAgLy8gU2FtZSBjb3VudGVyIHRoZSBhcGktY29yZSBxdW90YSBjbGllbnQgZW1pdHMsIHNvIGEgcXVvdGEtc2VydmljZSBvdXRhZ2VcbiAgICAgIC8vIGlzIEFMRVJUQUJMRSBmcm9tIG9uZSBzZXJpZXMgcmVnYXJkbGVzcyBvZiB3aGljaCBsYXllciBmZWxsIG9wZW4uIE9ubHlcbiAgICAgIC8vIGEgbG9nIGxpbmUgaGVyZSBtZWFudCBldmVyeSBvcmcgd2FzIHNpbGVudGx5IHVuLWJpbGxlZCBmb3IgdGhlIGR1cmF0aW9uLlxuICAgICAgZW1pdENvdW50ZXIoJ3F1b3RhX2ZhaWxfb3Blbl90b3RhbCcsIHsgb3BlcmF0aW9uOiAnY2hlY2snLCByZWFzb246ICdleGNlcHRpb24nLCBxdW90YVR5cGUgfSk7XG4gICAgICBuZXh0KCk7XG4gICAgfVxuICB9O1xufVxuIl19
|
|
244
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2stcXVvdGEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL2NoZWNrLXF1b3RhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLGtCQUFrQixFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxFQUFFLHlCQUF5QixFQUFFLGtCQUFrQixFQUFFLG1CQUFtQixFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUdqUSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDOUMsT0FBTyxFQUFFLDhCQUE4QixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFN0UsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0FBRTNDLHVCQUF1QjtBQUN2QixFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLGdGQUFnRjtBQUNoRixvQ0FBb0M7QUFDcEMsOEVBQThFO0FBQzlFLDZFQUE2RTtBQUM3RSw2RUFBNkU7QUFDN0UscUVBQXFFO0FBQ3JFLHlEQUF5RDtBQUN6RCwyRUFBMkU7QUFDM0UsZ0VBQWdFO0FBQ2hFLGtGQUFrRjtBQUNsRiw4RUFBOEU7QUFDOUUsdUVBQXVFO0FBQ3ZFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsMkJBQTJCO0FBRTNCLGtFQUFrRTtBQUNsRSxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxNQUFNLENBQUMsMEJBQTBCLEVBQUUsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7QUFFN0YsNEZBQTRGO0FBQzVGLE1BQU0sQ0FBQyxNQUFNLDJCQUEyQixHQUFHLE1BQU0sQ0FBQyw2QkFBNkIsRUFBRSxLQUFLLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUVwRyxvREFBb0Q7QUFDcEQsTUFBTSx1QkFBdUIsR0FBRyxNQUFNLENBQUM7QUFTdkMsTUFBTSxVQUFVLEdBQUcsSUFBSSxHQUFHLEVBQXVCLENBQUM7QUFDbEQsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLEVBQXFDLENBQUM7QUFFcEUsc0VBQXNFO0FBQ3RFLE1BQU0sVUFBVSxvQkFBb0I7SUFDbEMsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ25CLGNBQWMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztBQUN6QixDQUFDO0FBRUQscUZBQXFGO0FBQ3JGLFNBQVMsV0FBVyxDQUFDLEdBQVcsRUFBRSxTQUFvQjtJQUNwRCxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hDLElBQUksQ0FBQyxHQUFHO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDdEIsSUFBSSxHQUFHLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDO1FBQ2hDLFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsT0FBTztRQUFFLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQztJQUMzQyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDNUIsTUFBTSxRQUFRLEdBQUcsa0JBQWtCLENBQUMsU0FBUyxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7UUFDbkYsSUFBSSxHQUFHLENBQUMsTUFBTSxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQzNCLFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDdkIsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO0lBQ0gsQ0FBQztJQUNELEdBQUcsQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDO0lBQ2hCLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQztBQUNwQixDQUFDO0FBRUQsU0FBUyxhQUFhLENBQUMsR0FBVyxFQUFFLE1BQXdCO0lBQzFELElBQUksQ0FBQyxNQUFNLElBQUksTUFBTSxDQUFDLFFBQVE7UUFBRSxPQUFPO0lBQ3ZDLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN2QixJQUFJLFNBQWlCLENBQUM7SUFDdEIsSUFBSSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkIsSUFBSSx3QkFBd0IsS0FBSyxDQUFDO1lBQUUsT0FBTztRQUMzQyxTQUFTLEdBQUcsR0FBRyxHQUFHLHdCQUF3QixDQUFDO0lBQzdDLENBQUM7U0FBTSxDQUFDO1FBQ04sSUFBSSwyQkFBMkIsS0FBSyxDQUFDO1lBQUUsT0FBTztRQUM5QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMzQyxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUcsMkJBQTJCLEVBQUUsTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNsRyxJQUFJLFNBQVMsSUFBSSxHQUFHO1lBQUUsT0FBTztJQUMvQixDQUFDO0lBQ0QsSUFBSSxVQUFVLENBQUMsSUFBSSxJQUFJLHVCQUF1QixFQUFFLENBQUM7UUFDL0MseUVBQXlFO1FBQ3pFLE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN4QyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUk7WUFBRSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBQ0QsMERBQTBEO0lBQzFELFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQzdFLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILEtBQUssVUFBVSxTQUFTLENBQUMsWUFBMEIsRUFBRSxLQUFhLEVBQUUsU0FBb0IsRUFBRSxNQUFNLEdBQUcsS0FBSztJQUN0RyxNQUFNLEdBQUcsR0FBRyxHQUFHLEtBQUssU0FBUyxTQUFTLEVBQUUsQ0FBQztJQUN6QyxNQUFNLE1BQU0sR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQzNDLElBQUksTUFBTTtRQUFFLE9BQU8sTUFBTSxDQUFDO0lBQzFCLE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDeEMsSUFBSSxPQUFPLEVBQUUsQ0FBQztRQUNaLElBQUksTUFBTTtZQUFFLE9BQU8sT0FBTyxDQUFDO1FBQzNCLE1BQU0sTUFBTSxHQUFHLE1BQU0sT0FBTyxDQUFDO1FBQzdCLHVFQUF1RTtRQUN2RSxPQUFPLFVBQVUsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQ3hGLENBQUM7SUFDRCw2RUFBNkU7SUFDN0UsNkVBQTZFO0lBQzdFLDRFQUE0RTtJQUM1RSxpRkFBaUY7SUFDakYsTUFBTSxJQUFJLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsU0FBUyxFQUFFLHlCQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO1NBQ2hGLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFO1FBQ2YsYUFBYSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUMzQixPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDLENBQUM7U0FDRCxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQzdDLGNBQWMsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzlCLE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUNHO0FBQ0gsTUFBTSxVQUFVLFVBQVUsQ0FDeEIsWUFBMEIsRUFDMUIsYUFBd0I7SUFFeEIsT0FBTyxZQUFZLENBQUMsS0FBSyxFQUFFLEdBQVksRUFBRSxHQUFhLEVBQUUsSUFBa0IsRUFBaUIsRUFBRTtRQUMzRiw2RUFBNkU7UUFDN0Usc0VBQXNFO1FBQ3RFLG9FQUFvRTtRQUNwRSw0RUFBNEU7UUFDNUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNkLE1BQU0sQ0FBQyxJQUFJLENBQUMsbURBQW1ELENBQUMsQ0FBQztZQUNqRSxPQUFPLElBQUksRUFBRSxDQUFDO1FBQ2hCLENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxQyxJQUFJLFNBQVMsS0FBSyxTQUFTLElBQUksQ0FBQyxTQUFTLElBQUksYUFBYSxLQUFLLFVBQVUsQ0FBQztZQUFFLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDMUYsSUFBSSxhQUFhLEtBQUssVUFBVSxJQUFJLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQUUsT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUVwRyxNQUFNLFNBQVMsR0FBRyxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFDMUQsSUFBSSxLQUF5QixDQUFDO1FBRTlCLElBQUksQ0FBQztZQUNILEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQztRQUN6QyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsMkVBQTJFO1lBQzNFLHNFQUFzRTtZQUN0RSxLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUM7UUFDbEMsQ0FBQztRQUVELDRFQUE0RTtRQUM1RSx5RUFBeUU7UUFDekUsMEVBQTBFO1FBQzFFLGtDQUFrQztRQUNsQyxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFFMUIseUVBQXlFO1FBQ3pFLDRFQUE0RTtRQUM1RSwrQ0FBK0M7UUFDL0MsSUFBSSxNQUFNLDhCQUE4QixDQUFDLEdBQUcsQ0FBQztZQUFFLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFFN0QsSUFBSSxXQUE2QixDQUFDO1FBQ2xDLElBQUksQ0FBQztZQUNILFdBQVcsR0FBRyxNQUFNLFNBQVMsQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ2hFLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsZ0VBQWdFO1lBQ2hFLE1BQU0sQ0FBQyxJQUFJLENBQUMsMERBQTBELEVBQUU7Z0JBQ3RFLEtBQUs7Z0JBQ0wsU0FBUztnQkFDVCxLQUFLLEVBQUUsWUFBWSxDQUFDLEtBQUssQ0FBQzthQUMzQixDQUFDLENBQUM7WUFDSCwwRUFBMEU7WUFDMUUseUVBQXlFO1lBQ3pFLDJFQUEyRTtZQUMzRSxXQUFXLENBQUMsdUJBQXVCLEVBQUUsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztZQUM3RixPQUFPLElBQUksRUFBRSxDQUFDO1FBQ2hCLENBQUM7UUFFRCxJQUFJLENBQUMsV0FBVyxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pCLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxTQUFTLGlCQUFpQixFQUFFO2dCQUN6QyxLQUFLO2dCQUNMLEtBQUssRUFBRSxXQUFXLENBQUMsS0FBSztnQkFDeEIsSUFBSSxFQUFFLFdBQVcsQ0FBQyxJQUFJO2FBQ3ZCLENBQUMsQ0FBQztZQUVILHVFQUF1RTtZQUN2RSxzRUFBc0U7WUFDdEUseURBQXlEO1lBQ3pELGlCQUFpQixDQUNmLEdBQUcsRUFDSCxTQUFTLEVBQ1QsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxXQUFXLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxXQUFXLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxXQUFXLENBQUMsU0FBUyxFQUFFLEVBQ3ZHLFdBQVcsQ0FBQyxPQUFPLENBQ3BCLENBQUM7WUFDRixPQUFPO1FBQ1QsQ0FBQztRQUVELDZFQUE2RTtRQUM3RSw4RUFBOEU7UUFDOUUsSUFBSSxFQUFFLENBQUM7SUFDVCxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7QUFDakUsQ0FBQztBQUVELGdFQUFnRTtBQUNoRSxNQUFNLFVBQVUsV0FBVyxDQUFDLEdBQVk7SUFDdEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLElBQUksR0FBRyxDQUFDLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ2hFLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsWUFBMEIsRUFBRSxTQUFvQjtJQUNqRixNQUFNLEtBQUssR0FBRyxVQUFVLENBQUMsWUFBWSxFQUFFLFNBQVMsQ0FBbUIsQ0FBQztJQUNwRSxPQUFPLFlBQVksQ0FBQyxDQUFDLElBQWEsRUFBRSxHQUFhLEVBQUUsSUFBa0IsRUFBUSxFQUFFO1FBQzdFLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNsQyxJQUFJLEVBQUUsQ0FBQztJQUNULENBQUMsRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO0FBQ2xELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgYXJtRGVmZXJyZWRRdW90YUNoZWNrLCBjYWxsUXVvdGFFeGVtcHRpb24sIGNyZWF0ZUxvZ2dlciwgZW1pdENvdW50ZXIsIGVuZm9yY2VkUXVvdGFMaW1pdCwgZW52SW50LCBnZXRRdW90YVNlcnZpY2VBdXRoSGVhZGVyLCBpc1F1b3RhRXhlbXB0Um91dGUsIG1ldGVyZWRRdW90YVR5cGVGb3IsIHNlbmRRdW90YUV4Y2VlZGVkLCBlcnJvck1lc3NhZ2UsIHRhZ1JvdXRlR2F0ZSB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB0eXBlIHsgUXVvdGFDaGVja1Jlc3VsdCwgUXVvdGFUeXBlLCBRdW90YVNlcnZpY2UgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlcXVlc3RIYW5kbGVyLCBSZXNwb25zZSwgTmV4dEZ1bmN0aW9uIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyBnZXRDb250ZXh0IH0gZnJvbSAnLi9nZXQtY29udGV4dC5qcyc7XG5pbXBvcnQgeyBoYXNDb21wbGV0ZWRJZGVtcG90ZW50UmVzcG9uc2UgfSBmcm9tICcuL2lkZW1wb3RlbmN5LW1pZGRsZXdhcmUuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ2NoZWNrLXF1b3RhJyk7XG5cbi8vIFBFUi1QT0QgQ0hFQ0sgQ0FDSEUuXG4vL1xuLy8gVGhlIGdhdGUgdXNlZCB0byBhc2sgdGhlIHF1b3RhIHNlcnZpY2Ugb24gRVZFUlkgbWV0ZXJlZCByZXF1ZXN0IOKAlCBhIEdFVCwgYVxuLy8gSldUIHZlcmlmeSBhbmQgYSBoaWVyYXJjaHkgcmVzb2x2ZSBvdmVyIHRoZXJlLCBwZXIgcmVxdWVzdCBoZXJlLiBBIHJlYWRpbmcgaXNcbi8vIG5vdyByZXVzZWQgcGVyIChvcmcsIHF1b3RhIHR5cGUpOlxuLy8gIC0gYW4gQUxMT1dFRCByZWFkaW5nIGZvciBgUVVPVEFfQ0hFQ0tfQ0FDSEVfVFRMX01TYCAoZGVmYXVsdCA1MDAwOyAwIHR1cm5zXG4vLyAgICB0aGUgY2FjaGUgb2ZmKSwgYW5kIG9ubHkgd2hpbGUgdGhpcyBwb2QgaGFzIHBhc3NlZCBmZXdlciByZXF1ZXN0cyBvbiBpdFxuLy8gICAgdGhhbiB0aGUgcmVhZGluZyBoYWQgaGVhZHJvb20gZm9yIChgZW5mb3JjZWRRdW90YUxpbWl0IOKIkiB1c2VkYCksIHNvIG9uZVxuLy8gICAgcG9kIGNhbm5vdCBydW4gZmFyIHBhc3QgYSBuZWFybHktZnVsbCBjb3VudGVyIG9uIGEgc3RhbGUgXCJ5ZXNcIjtcbi8vICAtIGEgREVOSUVEIHJlYWRpbmcgdW50aWwgdGhlIHBlcmlvZCByZXNldHMsIGNhcHBlZCBhdFxuLy8gICAgYFFVT1RBX0NIRUNLX0RFTklFRF9DQUNIRV9NU2AgKGRlZmF1bHQgNjAwMDApIHNvIGFuIHVwZ3JhZGUsIGEgYnVuZGxlXG4vLyAgICBwdXJjaGFzZSBvciBhbiBvcGVyYXRvciByZXNldCBpcyBob25vdXJlZCB3aXRoaW4gYSBtaW51dGU7XG4vLyAgLSBhIGZhaWwtb3BlbiBzZW50aW5lbCBpcyBORVZFUiBjYWNoZWQg4oCUIHJlY292ZXJ5IGlzIHNlZW4gb24gdGhlIG5leHQgcmVxdWVzdC5cbi8vIFN0YWxlbmVzcyBpcyBib3VuZGVkIGFuZCBhY2NlcHRlZDogdGhlIGNvdW50ZXIgaXRzZWxmIG5ldmVyIGV4Y2VlZHMgdGhlIGNhcFxuLy8gKHRoZSBtZXRlcmVkIGluY3JlbWVudCBhZG1pdHMgb25seSB3aGF0IGZpdHMgYW5kIHJlY29yZHMgdGhlIHJlc3QgYXNcbi8vIG92ZXJzaG9vdCksIHNvIGEgc3RhbGUgXCJ5ZXNcIiBjb3N0cyBhdCBtb3N0IGEgZmV3IHVuYmlsbGVkLW92ZXItY2FwIHJlcXVlc3RzXG4vLyBwZXIgcG9kIHBlciBUVEwsIG5ldmVyIGFuIHVuYm91bmRlZCBydW4uIENvbmN1cnJlbnQgbWlzc2VzIGZvciBvbmUga2V5IHNoYXJlXG4vLyBhIHNpbmdsZSBpbi1mbGlnaHQgcmVhZC5cblxuLyoqIFRUTCAobXMpIG9mIGEgY2FjaGVkIEFMTE9XRUQgY2hlY2suIGAwYCBkaXNhYmxlcyB0aGUgY2FjaGUuICovXG5leHBvcnQgY29uc3QgUVVPVEFfQ0hFQ0tfQ0FDSEVfVFRMX01TID0gZW52SW50KCdRVU9UQV9DSEVDS19DQUNIRV9UVExfTVMnLCA1MDAwLCB7IG1pbjogMCB9KTtcblxuLyoqIExvbmdlc3QgKG1zKSBhIERFTklFRCBjaGVjayBpcyByZXVzZWQgYmVmb3JlIHJlLWFza2luZyAoaXQgbmV2ZXIgb3V0bGl2ZXMgYHJlc2V0QXRgKS4gKi9cbmV4cG9ydCBjb25zdCBRVU9UQV9DSEVDS19ERU5JRURfQ0FDSEVfTVMgPSBlbnZJbnQoJ1FVT1RBX0NIRUNLX0RFTklFRF9DQUNIRV9NUycsIDYwMDAwLCB7IG1pbjogMCB9KTtcblxuLyoqIEJvdW5kIG9uIGNhY2hlZCAob3JnLCB0eXBlKSByZWFkaW5ncyBwZXIgcG9kLiAqL1xuY29uc3QgQ0hFQ0tfQ0FDSEVfTUFYX0VOVFJJRVMgPSAxMF8wMDA7XG5cbmludGVyZmFjZSBDYWNoZWRDaGVjayB7XG4gIHN0YXR1czogUXVvdGFDaGVja1Jlc3VsdDtcbiAgZXhwaXJlc0F0OiBudW1iZXI7XG4gIC8qKiBSZXF1ZXN0cyB0aGlzIHBvZCBsZXQgdGhyb3VnaCBvbiB0aGlzIHJlYWRpbmcuICovXG4gIHBhc3NlczogbnVtYmVyO1xufVxuXG5jb25zdCBjaGVja0NhY2hlID0gbmV3IE1hcDxzdHJpbmcsIENhY2hlZENoZWNrPigpO1xuY29uc3QgaW5GbGlnaHRDaGVja3MgPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZTxRdW90YUNoZWNrUmVzdWx0Pj4oKTtcblxuLyoqIERyb3AgZXZlcnkgY2FjaGVkIGNoZWNrICh0ZXN0czsgYWxzbyBhIHNhZmUgb3BlcmF0aW9uYWwgcmVzZXQpLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyUXVvdGFDaGVja0NhY2hlKCk6IHZvaWQge1xuICBjaGVja0NhY2hlLmNsZWFyKCk7XG4gIGluRmxpZ2h0Q2hlY2tzLmNsZWFyKCk7XG59XG5cbi8qKiBBIHVzYWJsZSBjYWNoZWQgcmVhZGluZyBmb3IgYGtleWAsIGNvdW50aW5nIHRoaXMgcmVxdWVzdCBhZ2FpbnN0IGl0cyBoZWFkcm9vbS4gKi9cbmZ1bmN0aW9uIGNhY2hlZENoZWNrKGtleTogc3RyaW5nLCBxdW90YVR5cGU6IFF1b3RhVHlwZSk6IFF1b3RhQ2hlY2tSZXN1bHQgfCBudWxsIHtcbiAgY29uc3QgaGl0ID0gY2hlY2tDYWNoZS5nZXQoa2V5KTtcbiAgaWYgKCFoaXQpIHJldHVybiBudWxsO1xuICBpZiAoaGl0LmV4cGlyZXNBdCA8PSBEYXRlLm5vdygpKSB7XG4gICAgY2hlY2tDYWNoZS5kZWxldGUoa2V5KTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBpZiAoIWhpdC5zdGF0dXMuYWxsb3dlZCkgcmV0dXJuIGhpdC5zdGF0dXM7XG4gIGlmIChoaXQuc3RhdHVzLmxpbWl0ICE9PSAtMSkge1xuICAgIGNvbnN0IGhlYWRyb29tID0gZW5mb3JjZWRRdW90YUxpbWl0KHF1b3RhVHlwZSwgaGl0LnN0YXR1cy5saW1pdCkgLSBoaXQuc3RhdHVzLnVzZWQ7XG4gICAgaWYgKGhpdC5wYXNzZXMgPj0gaGVhZHJvb20pIHtcbiAgICAgIGNoZWNrQ2FjaGUuZGVsZXRlKGtleSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cbiAgaGl0LnBhc3NlcyArPSAxO1xuICByZXR1cm4gaGl0LnN0YXR1cztcbn1cblxuZnVuY3Rpb24gcmVtZW1iZXJDaGVjayhrZXk6IHN0cmluZywgc3RhdHVzOiBRdW90YUNoZWNrUmVzdWx0KTogdm9pZCB7XG4gIGlmICghc3RhdHVzIHx8IHN0YXR1cy5mYWlsT3BlbikgcmV0dXJuO1xuICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICBsZXQgZXhwaXJlc0F0OiBudW1iZXI7XG4gIGlmIChzdGF0dXMuYWxsb3dlZCkge1xuICAgIGlmIChRVU9UQV9DSEVDS19DQUNIRV9UVExfTVMgPT09IDApIHJldHVybjtcbiAgICBleHBpcmVzQXQgPSBub3cgKyBRVU9UQV9DSEVDS19DQUNIRV9UVExfTVM7XG4gIH0gZWxzZSB7XG4gICAgaWYgKFFVT1RBX0NIRUNLX0RFTklFRF9DQUNIRV9NUyA9PT0gMCkgcmV0dXJuO1xuICAgIGNvbnN0IHJlc2V0QXQgPSBEYXRlLnBhcnNlKHN0YXR1cy5yZXNldEF0KTtcbiAgICBleHBpcmVzQXQgPSBNYXRoLm1pbihub3cgKyBRVU9UQV9DSEVDS19ERU5JRURfQ0FDSEVfTVMsIE51bWJlci5pc0Zpbml0ZShyZXNldEF0KSA/IHJlc2V0QXQgOiBub3cpO1xuICAgIGlmIChleHBpcmVzQXQgPD0gbm93KSByZXR1cm47XG4gIH1cbiAgaWYgKGNoZWNrQ2FjaGUuc2l6ZSA+PSBDSEVDS19DQUNIRV9NQVhfRU5UUklFUykge1xuICAgIC8vIE1hcCBwcmVzZXJ2ZXMgaW5zZXJ0aW9uIG9yZGVyLCBzbyB0aGUgZmlyc3Qga2V5IGlzIHRoZSBvbGRlc3QgcmVhZGluZy5cbiAgICBjb25zdCBvbGRlc3QgPSBjaGVja0NhY2hlLmtleXMoKS5uZXh0KCk7XG4gICAgaWYgKCFvbGRlc3QuZG9uZSkgY2hlY2tDYWNoZS5kZWxldGUob2xkZXN0LnZhbHVlKTtcbiAgfVxuICAvLyBUaGUgcmVxdWVzdCB0aGF0IGZldGNoZWQgdGhlIHJlYWRpbmcgaXMgaXRzIGZpcnN0IHBhc3MuXG4gIGNoZWNrQ2FjaGUuc2V0KGtleSwgeyBzdGF0dXMsIGV4cGlyZXNBdCwgcGFzc2VzOiBzdGF0dXMuYWxsb3dlZCA/IDEgOiAwIH0pO1xufVxuXG4vKipcbiAqIE9uZSBxdW90YSByZWFkIGZvciAob3JnLCB0eXBlKSwgc2hhcmVkIGJ5IGV2ZXJ5IGNvbmN1cnJlbnQgbWlzcyBvbiB0aGlzIHBvZC5cbiAqIEEgcmVxdWVzdCB0aGF0IHdhaXRlZCBvbiBzb21lb25lIGVsc2UncyByZWFkIHRoZW4gZ29lcyBiYWNrIHRocm91Z2ggdGhlXG4gKiBjYWNoZSwgc28gaXQgaXMgY291bnRlZCBhZ2FpbnN0IHRoZSByZWFkaW5nJ3MgaGVhZHJvb20gbGlrZSBhbnkgb3RoZXIgcGFzc1xuICogKGBqb2luZWRgIHN0b3BzIGl0IGZyb20gd2FpdGluZyBhIHNlY29uZCB0aW1lKS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcmVhZENoZWNrKHF1b3RhU2VydmljZTogUXVvdGFTZXJ2aWNlLCBvcmdJZDogc3RyaW5nLCBxdW90YVR5cGU6IFF1b3RhVHlwZSwgam9pbmVkID0gZmFsc2UpOiBQcm9taXNlPFF1b3RhQ2hlY2tSZXN1bHQ+IHtcbiAgY29uc3Qga2V5ID0gYCR7b3JnSWR9XFx1MDAwMCR7cXVvdGFUeXBlfWA7XG4gIGNvbnN0IGNhY2hlZCA9IGNhY2hlZENoZWNrKGtleSwgcXVvdGFUeXBlKTtcbiAgaWYgKGNhY2hlZCkgcmV0dXJuIGNhY2hlZDtcbiAgY29uc3QgcGVuZGluZyA9IGluRmxpZ2h0Q2hlY2tzLmdldChrZXkpO1xuICBpZiAocGVuZGluZykge1xuICAgIGlmIChqb2luZWQpIHJldHVybiBwZW5kaW5nO1xuICAgIGNvbnN0IHN0YXR1cyA9IGF3YWl0IHBlbmRpbmc7XG4gICAgLy8gQSBmYWlsLW9wZW4gKG9yIHVuY2FjaGVhYmxlKSByZWFkaW5nIGhhcyBub3RoaW5nIHRvIGFjY291bnQgYWdhaW5zdC5cbiAgICByZXR1cm4gY2hlY2tDYWNoZS5oYXMoa2V5KSA/IHJlYWRDaGVjayhxdW90YVNlcnZpY2UsIG9yZ0lkLCBxdW90YVR5cGUsIHRydWUpIDogc3RhdHVzO1xuICB9XG4gIC8vIEF1dGhlbnRpY2F0ZSBhcyBUSElTIHNlcnZpY2UsIG5vdCBieSBmb3J3YXJkaW5nIHRoZSB1c2VyJ3MgdG9rZW46IHRoZSB1c2VyXG4gIC8vIHRva2VuIDQwM3MgYXQgdGhlIHF1b3RhIHJlYWQgcm91dGUgZm9yIGN1c3RvbSByb2xlcyB3aXRob3V0IGBxdW90YXM6cmVhZGAsXG4gIC8vIGFuZCAoYmVpbmcgbm9uLXNlcnZpY2UpIGNvdW50cyBhZ2FpbnN0IHRoZSBxdW90YSBzZXJ2aWNlJ3MgcGVyLUlQIGxpbWl0ZXJcbiAgLy8ga2V5ZWQgb24gVEhJUyBwb2QncyBJUCDigJQgYm90aCBvZiB3aGljaCBzaWxlbnRseSBmYWlsIHRoZSBnYXRlIG9wZW4gdW5kZXIgbG9hZC5cbiAgY29uc3QgcmVhZCA9IHF1b3RhU2VydmljZS5jaGVjayhvcmdJZCwgcXVvdGFUeXBlLCBnZXRRdW90YVNlcnZpY2VBdXRoSGVhZGVyKG9yZ0lkKSlcbiAgICAudGhlbigoc3RhdHVzKSA9PiB7XG4gICAgICByZW1lbWJlckNoZWNrKGtleSwgc3RhdHVzKTtcbiAgICAgIHJldHVybiBzdGF0dXM7XG4gICAgfSlcbiAgICAuZmluYWxseSgoKSA9PiBpbkZsaWdodENoZWNrcy5kZWxldGUoa2V5KSk7XG4gIGluRmxpZ2h0Q2hlY2tzLnNldChrZXksIHJlYWQpO1xuICByZXR1cm4gcmVhZDtcbn1cblxuLyoqXG4gKiBDcmVhdGUgbWlkZGxld2FyZSB0aGF0IGNoZWNrcyBhIHNwZWNpZmljIHF1b3RhIHR5cGUgYmVmb3JlIHByb2NlZWRpbmcuXG4gKlxuICogT24gcXVvdGEgZXhjZWVkZWQsIHJldHVybnMgYSA0MjkgcmVzcG9uc2Ugd2l0aCBxdW90YSBkZXRhaWxzIChxdW90YVR5cGUsXG4gKiBsaW1pdCwgdXNlZCwgcmVzZXRBdCwgdXBncmFkZVVybCDigJQgc2VlIGFwaS1jb3JlIGBzZW5kUXVvdGFFeGNlZWRlZGApLlxuICogT24gcXVvdGEgc2VydmljZSBmYWlsdXJlLCBmYWlscyBvcGVuIChhbGxvd3MgdGhlIHJlcXVlc3QpLlxuICpcbiAqIGBhcGlDYWxsc2AgaXMgc3BsaXQgcGVyIHJlcXVlc3QgYnkgcHJpbmNpcGFsOiBhbiBvcmcgc2VydmljZSBhY2NvdW50IG9yIGFcbiAqIHBlcnNvbmFsIGFjY2VzcyBrZXkgaXMgY2hlY2tlZCBhZ2FpbnN0IGBhdXRvbWF0aW9uQ2FsbHNgIGluc3RlYWRcbiAqICh7QGxpbmsgbWV0ZXJlZFF1b3RhVHlwZUZvcn0pLCB0aGVcbiAqIHNhbWUgdHlwZSBgbWV0ZXJRdW90YU9uU3VjY2Vzc2AgaW5jcmVtZW50cyDigJQgc28gbW91bnQgYm90aCB3aXRoIGAnYXBpQ2FsbHMnYC5cbiAqXG4gKiBTa2lwcGVkIChwYXNzZXMgdGhyb3VnaCkgZXhhY3RseSB3aGVyZSB0aGUgbWV0ZXIgc2tpcHMsIHNvIHRoZSBnYXRlIG5ldmVyXG4gKiByZWZ1c2VzIGEgY2FsbCB0aGUgbWV0ZXIgd291bGQgbm90IGJpbGwgKGBjYWxsUXVvdGFFeGVtcHRpb25gKTpcbiAqIC0gYSBTRVJWSUNFIFBSSU5DSVBBTCAocGVlci1zZXJ2aWNlIHRyYWZmaWMgaXMgbmV2ZXIgbWV0ZXJlZCwgc28gYSB0ZW5hbnQgYXRcbiAqICAgaXRzIGxpbWl0IG11c3Qgbm90IGJyZWFrIHRoZSBpbnRlcm5hbCBjYWxscyBpdHMgb3duIGVhcmxpZXIgYWN0aW9ucyBjYXVzZWQpO1xuICogLSBmb3IgYGFwaUNhbGxzYDogYW4gSU1QRVJTT05BVElPTiBzZXNzaW9uICh2ZW5kb3Igc3RhZmYgZGlhZ25vc2luZyBhblxuICogICBleGhhdXN0ZWQgb3JnIG11c3Qgbm90IGJlIGxvY2tlZCBvdXQgYnkgaXQpIGFuZCBhbiBPTi1CRUhBTEYgaG9wIChhIHVzZXJcbiAqICAgdG9rZW4gZm9yd2FyZGVkIGJ5IGFzay9waXBlbGluZSB3aXRoIGEgc2lnbmVkIG1hcmtlciDigJQgdGhlIG9yaWdpbmF0aW5nXG4gKiAgIHJlcXVlc3Qgd2FzIGFscmVhZHkgZ2F0ZWQgYW5kIG1ldGVyZWQpO1xuICogLSBhbiBgYXBpQ2FsbHNgIHJlcXVlc3Qgb24gdGhlIGZsZWV0LXdpZGUgYFFVT1RBX0VYRU1QVF9ST1VURVNgIChjb25zb2xlXG4gKiAgIHBvbGxpbmcg4oCUIGFuIGV4aGF1c3RlZCBvcmcgbXVzdCBzdGlsbCByZW5kZXIgdGhlIGJhbm5lciBzYXlpbmcgc28pO1xuICogLSBhbiBpZGVtcG90ZW50IFJFVFJZIHdob3NlIG9yaWdpbmFsIGFscmVhZHkgY29tcGxldGVkICh0aGUgcmVwbGF5IGlzIHNlcnZlZFxuICogICBmcm9tIHRoZSBpZGVtcG90ZW5jeSBjYWNoZSwgZG9lcyBubyB3b3JrIGFuZCBpcyBuZXZlciBtZXRlcmVkOyByZWZ1c2luZyBpdFxuICogICB3b3VsZCA0MjkgYSBjbGllbnQgZm9yIGFza2luZyB3aGF0IGl0cyBvd24gZWFybGllciByZXF1ZXN0IHJldHVybmVkKS5cbiAqXG4gKiBSZWFkaW5ncyBhcmUgY2FjaGVkIHBlciBwb2QgKHNlZSB0aGUgY2FjaGUgbm90ZXMgYWJvdmUpLCBhbmQgdGhlIHJlYWQgaXRzZWxmXG4gKiBoYXMgYSBzaG9ydCBidWRnZXQgKGFwaS1jb3JlIGBRVU9UQV9DSEVDS19USU1FT1VUX01TYCwgbm8gcmV0cnkpIOKAlCBhIHNsb3dcbiAqIHF1b3RhIHNlcnZpY2UgZmFpbHMgb3BlbiBxdWlja2x5IGluc3RlYWQgb2Ygc3RhbGxpbmcgZXZlcnkgcmVxdWVzdC5cbiAqXG4gKiBNb3VudGFibGUgYXQgdGhlIGFwcC1yb3V0ZXMgbGV2ZWwgKGBhcHAudXNlKHByZWZpeCwg4oCmYXV0aCwgY2hlY2tRdW90YSguLi4pKWApOlxuICogdGhlIG9yZyBjb21lcyBmcm9tIHRoZSByZXF1ZXN0IGNvbnRleHQgd2hlbiBvbmUgaXMgYXR0YWNoZWQsIGVsc2UgZnJvbSB0aGVcbiAqIHZlcmlmaWVkIGByZXEudXNlci5vcmdhbml6YXRpb25JZGAg4oCUIHRoZSBzYW1lIGZhbGxiYWNrIHRoZSBtZXRlciB1c2VzLlxuICpcbiAqIEBwYXJhbSBxdW90YVNlcnZpY2UgLSBRdW90YSBzZXJ2aWNlIGNsaWVudFxuICogQHBhcmFtIHF1b3RhVHlwZSAtIFdoaWNoIHF1b3RhIHRvIGNoZWNrIChlLmcuICdhcGlDYWxscycsICdwaXBlbGluZXMnLCAncGx1Z2lucycpXG4gKiBAcmV0dXJucyBFeHByZXNzIG1pZGRsZXdhcmVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNoZWNrUXVvdGEoXG4gIHF1b3RhU2VydmljZTogUXVvdGFTZXJ2aWNlLFxuICBiYXNlUXVvdGFUeXBlOiBRdW90YVR5cGUsXG4pIHtcbiAgcmV0dXJuIHRhZ1JvdXRlR2F0ZShhc3luYyAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAvLyBUaGUgY2hlY2sgaXMgc2VudCB3aXRoIFRISVMgc2VydmljZSdzIGNyZWRlbnRpYWxzLCBzbyBpdCBtdXN0IG9ubHkgcnVuIGZvclxuICAgIC8vIGEgVkVSSUZJRUQgY2FsbGVyIOKAlCBvdGhlcndpc2UgdGhlIG9yZyB3b3VsZCBjb21lIGZyb20gdGhlIHNwb29mYWJsZVxuICAgIC8vIGhlYWRlci1kZXJpdmVkIGlkZW50aXR5LiBNb3VudGVkIGFmdGVyIHJlcXVpcmVBdXRoIGV2ZXJ5d2hlcmU7IGFuXG4gICAgLy8gdW5hdXRoZW50aWNhdGVkIHJlcXVlc3QgaGVyZSBpcyBhIHdpcmluZyBidWcsIHNvIHNraXAgKGZhaWwgb3BlbikgbG91ZGx5LlxuICAgIGlmICghcmVxLnVzZXIpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdRdW90YSBjaGVjayBza2lwcGVkOiByZXF1ZXN0IGlzIG5vdCBhdXRoZW50aWNhdGVkJyk7XG4gICAgICByZXR1cm4gbmV4dCgpO1xuICAgIH1cblxuICAgIGNvbnN0IGV4ZW1wdGlvbiA9IGNhbGxRdW90YUV4ZW1wdGlvbihyZXEpO1xuICAgIGlmIChleGVtcHRpb24gPT09ICdzZXJ2aWNlJyB8fCAoZXhlbXB0aW9uICYmIGJhc2VRdW90YVR5cGUgPT09ICdhcGlDYWxscycpKSByZXR1cm4gbmV4dCgpO1xuICAgIGlmIChiYXNlUXVvdGFUeXBlID09PSAnYXBpQ2FsbHMnICYmIGlzUXVvdGFFeGVtcHRSb3V0ZShyZXEubWV0aG9kLCByZXF1ZXN0UGF0aChyZXEpKSkgcmV0dXJuIG5leHQoKTtcblxuICAgIGNvbnN0IHF1b3RhVHlwZSA9IG1ldGVyZWRRdW90YVR5cGVGb3IocmVxLCBiYXNlUXVvdGFUeXBlKTtcbiAgICBsZXQgb3JnSWQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAgIHRyeSB7XG4gICAgICBvcmdJZCA9IGdldENvbnRleHQocmVxKS5pZGVudGl0eS5vcmdJZDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIE5vIHJlcXVlc3QgY29udGV4dCAocGxhdGZvcm0gYXR0YWNoZXMgbm9uZSk6IHRoZSB2ZXJpZmllZCBhdXRoIG9yZyDigJQgdGhlXG4gICAgICAvLyBzYW1lIGZhbGxiYWNrIGBtZXRlclF1b3RhT25TdWNjZXNzYCB1c2VzLCBzbyBib3RoIHJlYWQgb25lIGNvdW50ZXIuXG4gICAgICBvcmdJZCA9IHJlcS51c2VyLm9yZ2FuaXphdGlvbklkO1xuICAgIH1cblxuICAgIC8vIE5vIG9yZywgbm90aGluZyB0byBiaWxsICh0aGUgbWV0ZXIgc2tpcHMgaXQgdG9vKS4gTm90IGEgNDAwOiBydW4gZGVmZXJyZWRcbiAgICAvLyBmcm9tIGByZXF1aXJlQXV0aGAgdGhpcyBwcmVjZWRlcyB0aGUgcm91dGUncyBvd24gYHJlcXVpcmVPcmdJZGAsIGFuZCBhXG4gICAgLy8gcm91dGUgdGhhdCBuZWVkcyBubyBvcmcgKGEgcGVyc29uIGJldHdlZW4gb3JncyBzd2l0Y2hpbmcgaW50byBvbmUpIG11c3RcbiAgICAvLyBub3QgYmUgcmVmdXNlZCBieSBhIHF1b3RhIGdhdGUuXG4gICAgaWYgKCFvcmdJZCkgcmV0dXJuIG5leHQoKTtcblxuICAgIC8vIEEgcmV0cnkgb2YgYSBjb21wbGV0ZWQgaWRlbXBvdGVudCBtdXRhdGlvbiBpcyBhbnN3ZXJlZCBmcm9tIHRoZSByZXBsYXlcbiAgICAvLyBjYWNoZSB3aXRob3V0IHJ1bm5pbmcgdGhlIGhhbmRsZXIg4oCUIG5vdGhpbmcgdG8gZ2F0ZSAob25lIHN0b3JlIHJlYWQsIG9ubHlcbiAgICAvLyBmb3IgcmVxdWVzdHMgdGhhdCBjYXJyeSBhbiBJZGVtcG90ZW5jeS1LZXkpLlxuICAgIGlmIChhd2FpdCBoYXNDb21wbGV0ZWRJZGVtcG90ZW50UmVzcG9uc2UocmVxKSkgcmV0dXJuIG5leHQoKTtcblxuICAgIGxldCBxdW90YVN0YXR1czogUXVvdGFDaGVja1Jlc3VsdDtcbiAgICB0cnkge1xuICAgICAgcXVvdGFTdGF0dXMgPSBhd2FpdCByZWFkQ2hlY2socXVvdGFTZXJ2aWNlLCBvcmdJZCwgcXVvdGFUeXBlKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gRmFpbCBvcGVuIOKAlCBhbGxvdyB0aGUgcmVxdWVzdCBpZiBxdW90YSBzZXJ2aWNlIGlzIHVuYXZhaWxhYmxlXG4gICAgICBsb2dnZXIud2FybignUVVPVEFfRkFJTF9PUEVOOiBRdW90YSBjaGVjayBleGNlcHRpb24sIGFsbG93aW5nIHJlcXVlc3QnLCB7XG4gICAgICAgIG9yZ0lkLFxuICAgICAgICBxdW90YVR5cGUsXG4gICAgICAgIGVycm9yOiBlcnJvck1lc3NhZ2UoZXJyb3IpLFxuICAgICAgfSk7XG4gICAgICAvLyBTYW1lIGNvdW50ZXIgdGhlIGFwaS1jb3JlIHF1b3RhIGNsaWVudCBlbWl0cywgc28gYSBxdW90YS1zZXJ2aWNlIG91dGFnZVxuICAgICAgLy8gaXMgQUxFUlRBQkxFIGZyb20gb25lIHNlcmllcyByZWdhcmRsZXNzIG9mIHdoaWNoIGxheWVyIGZlbGwgb3Blbi4gT25seVxuICAgICAgLy8gYSBsb2cgbGluZSBoZXJlIG1lYW50IGV2ZXJ5IG9yZyB3YXMgc2lsZW50bHkgdW4tYmlsbGVkIGZvciB0aGUgZHVyYXRpb24uXG4gICAgICBlbWl0Q291bnRlcigncXVvdGFfZmFpbF9vcGVuX3RvdGFsJywgeyBvcGVyYXRpb246ICdjaGVjaycsIHJlYXNvbjogJ2V4Y2VwdGlvbicsIHF1b3RhVHlwZSB9KTtcbiAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgfVxuXG4gICAgaWYgKCFxdW90YVN0YXR1cy5hbGxvd2VkKSB7XG4gICAgICBsb2dnZXIud2FybihgJHtxdW90YVR5cGV9IHF1b3RhIGV4Y2VlZGVkYCwge1xuICAgICAgICBvcmdJZCxcbiAgICAgICAgbGltaXQ6IHF1b3RhU3RhdHVzLmxpbWl0LFxuICAgICAgICB1c2VkOiBxdW90YVN0YXR1cy51c2VkLFxuICAgICAgfSk7XG5cbiAgICAgIC8vIFJvdXRlIHRocm91Z2ggdGhlIHNoYXJlZCBoZWxwZXIgc28gdGhlIGNoZWNrLWdhdGUgNDI5IGVtaXRzIHRoZSBzYW1lXG4gICAgICAvLyBSZXRyeS1BZnRlciArIFgtUXVvdGEtKiBoZWFkZXJzIGFuZCB0aGUgc2FtZSBib2R5ICh3aGljaCBxdW90YSwgdGhlXG4gICAgICAvLyByZXNldCBkYXRlLCB0aGUgdXBncmFkZSBwYWdlKSBhcyB0aGUgcmVzZXJ2ZS1wYXRoIDQyOS5cbiAgICAgIHNlbmRRdW90YUV4Y2VlZGVkKFxuICAgICAgICByZXMsXG4gICAgICAgIHF1b3RhVHlwZSxcbiAgICAgICAgeyB0eXBlOiBxdW90YVR5cGUsIGxpbWl0OiBxdW90YVN0YXR1cy5saW1pdCwgdXNlZDogcXVvdGFTdGF0dXMudXNlZCwgcmVtYWluaW5nOiBxdW90YVN0YXR1cy5yZW1haW5pbmcgfSxcbiAgICAgICAgcXVvdGFTdGF0dXMucmVzZXRBdCxcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gT3V0c2lkZSB0aGUgdHJ5OiBhIHN5bmNocm9ub3VzIHRocm93IGRvd25zdHJlYW0gbXVzdCByZWFjaCBFeHByZXNzJ3MgZXJyb3JcbiAgICAvLyBoYW5kbGluZywgbm90IGJlIG1pc3Rha2VuIGZvciBhIHF1b3RhLXNlcnZpY2UgZmFpbHVyZSBhbmQgcnVuIG5leHQoKSB0d2ljZS5cbiAgICBuZXh0KCk7XG4gIH0sIHsga2luZDogJ3F1b3RhJywgbW9kZTogJ2NoZWNrJywgcXVvdGFUeXBlOiBiYXNlUXVvdGFUeXBlIH0pO1xufVxuXG4vKiogVGhlIHNlcnZpY2UtbG9jYWwgcmVxdWVzdCBwYXRoLCB3aXRob3V0IHRoZSBxdWVyeSBzdHJpbmcuICovXG5leHBvcnQgZnVuY3Rpb24gcmVxdWVzdFBhdGgocmVxOiBSZXF1ZXN0KTogc3RyaW5nIHtcbiAgcmV0dXJuIChyZXEub3JpZ2luYWxVcmwgPz8gcmVxLnVybCA/PyAnJykuc3BsaXQoJz8nKVswXSA/PyAnJztcbn1cblxuLyoqXG4gKiBUaGUgREVGRVJSRUQgZm9ybSBvZiB7QGxpbmsgY2hlY2tRdW90YX06IGFybXMgdGhlIGNoZWNrIGF0IG1vdW50IHRpbWUgYW5kIGxldHNcbiAqIGByZXF1aXJlQXV0aGAgcnVuIGl0IG9uY2UgdGhlIGNhbGxlciBpcyB2ZXJpZmllZCAoYXBpLWNvcmVcbiAqIGBhcm1EZWZlcnJlZFF1b3RhQ2hlY2tgKS4gRm9yIGEgcHJlZml4IG1vdW50IGFoZWFkIG9mIHJvdXRlcnMgdGhhdFxuICogYXV0aGVudGljYXRlIHBlciByb3V0ZSDigJQgdXNlIHtAbGluayBhcGlDYWxsc1F1b3RhfSwgd2hpY2ggcGFpcnMgaXQgd2l0aCB0aGVcbiAqIG1ldGVyLiBUYWdnZWQgYXMgdGhlIGNoZWNrIGdhdGUsIHNvIHRoZSByb3V0ZSB0YWJsZSByZWNvcmRzIGl0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVmZXJyZWRDaGVja1F1b3RhKHF1b3RhU2VydmljZTogUXVvdGFTZXJ2aWNlLCBxdW90YVR5cGU6IFF1b3RhVHlwZSk6IFJlcXVlc3RIYW5kbGVyIHtcbiAgY29uc3QgY2hlY2sgPSBjaGVja1F1b3RhKHF1b3RhU2VydmljZSwgcXVvdGFUeXBlKSBhcyBSZXF1ZXN0SGFuZGxlcjtcbiAgcmV0dXJuIHRhZ1JvdXRlR2F0ZSgoX3JlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKTogdm9pZCA9PiB7XG4gICAgYXJtRGVmZXJyZWRRdW90YUNoZWNrKHJlcywgY2hlY2spO1xuICAgIG5leHQoKTtcbiAgfSwgeyBraW5kOiAncXVvdGEnLCBtb2RlOiAnY2hlY2snLCBxdW90YVR5cGUgfSk7XG59XG4iXX0=
|
|
@@ -60,6 +60,28 @@ export declare function createEnvRedisIdempotencyStore(): IdempotencyStore | nul
|
|
|
60
60
|
export declare function setIdempotencyStore(store: IdempotencyStore): void;
|
|
61
61
|
/** The current process-wide default store (in-memory unless overridden). */
|
|
62
62
|
export declare function getIdempotencyStore(): IdempotencyStore;
|
|
63
|
+
/**
|
|
64
|
+
* `res.locals` key set to `true` when the response was REPLAYED from the
|
|
65
|
+
* idempotency cache instead of running the handler. A replay did no work, so
|
|
66
|
+
* anything that bills per successful response (`meterQuotaOnSuccess`) must skip
|
|
67
|
+
* it — the original request already paid. Use {@link isIdempotentReplay}.
|
|
68
|
+
*/
|
|
69
|
+
export declare const IDEMPOTENT_REPLAY_LOCAL = "idempotentReplay";
|
|
70
|
+
/** True when `res` is a replay of a cached idempotent response. */
|
|
71
|
+
export declare function isIdempotentReplay(res: Response): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* True when this request is a RETRY of an idempotent mutation whose original
|
|
74
|
+
* has COMPLETED (a cached 2xx on record) — i.e. the idempotency middleware will
|
|
75
|
+
* replay it without running the handler. The apiCalls check uses it to skip
|
|
76
|
+
* the gate for such a retry: the replay does no work and is never metered, so
|
|
77
|
+
* refusing it at the quota gate (which runs in requireAuth, BEFORE replay
|
|
78
|
+
* detection in most chains) would 429 a client for re-reading its own result.
|
|
79
|
+
*
|
|
80
|
+
* One store read, and only for a mutation carrying `Idempotency-Key`; every
|
|
81
|
+
* failure answers `false` (the gate then runs as usual). Uses the process-wide
|
|
82
|
+
* store — the same one the route chains' middleware resolves.
|
|
83
|
+
*/
|
|
84
|
+
export declare function hasCompletedIdempotentResponse(req: Request): Promise<boolean>;
|
|
63
85
|
export interface IdempotencyMiddlewareOptions {
|
|
64
86
|
/** Custom store backend. Defaults to the process-wide store set via
|
|
65
87
|
* {@link setIdempotencyStore} (in-memory until `createApp` wires Redis). */
|