@pipeline-builder/api-server 4.1.14 → 4.1.16
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/app-factory.d.ts +2 -4
- package/lib/api/app-factory.js +198 -172
- package/lib/api/check-quota.js +2 -1
- package/lib/api/idempotency-middleware.js +27 -31
- package/lib/api/index.d.ts +1 -1
- package/lib/api/index.js +2 -2
- package/lib/api/meter-quota.d.ts +6 -7
- package/lib/api/meter-quota.js +7 -8
- package/lib/api/metrics.js +6 -6
- package/lib/api/mongo-connect.js +5 -5
- package/lib/api/quota-reservation.d.ts +52 -0
- package/lib/api/quota-reservation.js +56 -0
- package/lib/api/rate-limit-by-org.d.ts +10 -0
- package/lib/api/rate-limit-by-org.js +8 -3
- package/lib/api/require-org-id.d.ts +1 -1
- package/lib/api/require-org-id.js +4 -4
- package/lib/api/route-wrapper.js +5 -5
- package/lib/api/server.js +22 -10
- package/lib/api/tenant-context.d.ts +5 -6
- package/lib/api/tenant-context.js +4 -4
- package/lib/http/sse-connection-manager.d.ts +2 -2
- package/lib/http/sse-connection-manager.js +13 -12
- package/lib/markdown.d.ts +13 -0
- package/lib/markdown.js +111 -0
- package/lib/testing/exports.json +39 -0
- package/package.json +27 -9
- package/lib/api/quota-helpers.d.ts +0 -22
- package/lib/api/quota-helpers.js +0 -23
package/lib/api/mongo-connect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright 2026 Pipeline Builder Contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { createLogger } from '@pipeline-builder/api-core';
|
|
3
|
+
import { createLogger, envInt } from '@pipeline-builder/api-core';
|
|
4
4
|
const logger = createLogger('mongo-connect');
|
|
5
5
|
/**
|
|
6
6
|
* Connect to MongoDB with sensible production-ready pool defaults and
|
|
@@ -32,10 +32,10 @@ export async function connectMongo(mongoose, uri) {
|
|
|
32
32
|
logger.info('MongoDB reconnected');
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
const maxPoolSize =
|
|
36
|
-
const minPoolSize =
|
|
37
|
-
const serverSelectionTimeoutMS =
|
|
35
|
+
const maxPoolSize = envInt('MONGO_MAX_POOL', 20, { min: 1 });
|
|
36
|
+
const minPoolSize = envInt('MONGO_MIN_POOL', 2, { min: 0 });
|
|
37
|
+
const serverSelectionTimeoutMS = envInt('MONGO_SERVER_SELECTION_MS', 5000, { min: 1 });
|
|
38
38
|
await mongoose.connect(uri, { maxPoolSize, minPoolSize, serverSelectionTimeoutMS });
|
|
39
39
|
logger.info('MongoDB connection established', { maxPoolSize, minPoolSize, serverSelectionTimeoutMS });
|
|
40
40
|
}
|
|
41
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ28tY29ubmVjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvbW9uZ28tY29ubmVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFbEUsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBb0I3Qzs7Ozs7Ozs7Ozs7R0FXRztBQUNILElBQUksYUFBYSxHQUFHLEtBQUssQ0FBQztBQUUxQixNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FBQyxRQUFzQixFQUFFLEdBQVc7SUFDcEUsUUFBUSxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFFbEMsMEVBQTBFO0lBQzFFLHdFQUF3RTtJQUN4RSx3REFBd0Q7SUFDeEQsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25CLGFBQWEsR0FBRyxJQUFJLENBQUM7UUFFckIsUUFBUSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDdEMsTUFBTSxDQUFDLEtBQUssQ0FBQywwQkFBMEIsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNwRSxDQUFDLENBQUMsQ0FBQztRQUVILFFBQVEsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDLGNBQWMsRUFBRSxHQUFHLEVBQUU7WUFDMUMsTUFBTSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQ3RDLENBQUMsQ0FBQyxDQUFDO1FBRUgsUUFBUSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsYUFBYSxFQUFFLEdBQUcsRUFBRTtZQUN6QyxNQUFNLENBQUMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLENBQUM7UUFDckMsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixFQUFFLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzdELE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM1RCxNQUFNLHdCQUF3QixHQUFHLE1BQU0sQ0FBQywyQkFBMkIsRUFBRSxJQUFJLEVBQUUsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUV2RixNQUFNLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSx3QkFBd0IsRUFBRSxDQUFDLENBQUM7SUFDcEYsTUFBTSxDQUFDLElBQUksQ0FBQyxnQ0FBZ0MsRUFBRSxFQUFFLFdBQVcsRUFBRSxXQUFXLEVBQUUsd0JBQXdCLEVBQUUsQ0FBQyxDQUFDO0FBQ3hHLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyLCBlbnZJbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignbW9uZ28tY29ubmVjdCcpO1xuXG4vKipcbiAqIE1pbmltYWwgc2hhcGUgb2YgbW9uZ29vc2UgYWNjZXB0ZWQgYnkgYGNvbm5lY3RNb25nb2AuIFBhc3NpbmcgdGhlIHJlYWxcbiAqIG1vbmdvb3NlIG9iamVjdCBzYXRpc2ZpZXMgaXQ7IGFwaS1zZXJ2ZXIgdGFrZXMgbW9uZ29vc2UgYXMgYSBwYXJhbWV0ZXJcbiAqIChkZXBlbmRlbmN5IGluamVjdGlvbikgc28gaXQgZG9lc24ndCBoYXZlIHRvIGRlY2xhcmUgbW9uZ29vc2UgYXMgYVxuICogZGVwZW5kZW5jeSBpdHNlbGYuXG4gKi9cbmludGVyZmFjZSBNb25nb29zZUxpa2Uge1xuICBzZXQob3B0aW9uOiBzdHJpbmcsIHZhbHVlOiB1bmtub3duKTogdW5rbm93bjtcbiAgY29ubmVjdGlvbjoge1xuICAgIG9uKGV2ZW50OiBzdHJpbmcsIGxpc3RlbmVyOiAoZXJyPzogRXJyb3IpID0+IHZvaWQpOiB1bmtub3duO1xuICB9O1xuICBjb25uZWN0KHVyaTogc3RyaW5nLCBvcHRpb25zOiB7XG4gICAgbWF4UG9vbFNpemU6IG51bWJlcjtcbiAgICBtaW5Qb29sU2l6ZTogbnVtYmVyO1xuICAgIHNlcnZlclNlbGVjdGlvblRpbWVvdXRNUzogbnVtYmVyO1xuICB9KTogUHJvbWlzZTx1bmtub3duPjtcbn1cblxuLyoqXG4gKiBDb25uZWN0IHRvIE1vbmdvREIgd2l0aCBzZW5zaWJsZSBwcm9kdWN0aW9uLXJlYWR5IHBvb2wgZGVmYXVsdHMgYW5kXG4gKiBzdGFuZGFyZCBldmVudC1oYW5kbGVyIHdpcmluZyAoZXJyb3IgLyBkaXNjb25uZWN0ZWQgLyByZWNvbm5lY3RlZCkuXG4gKlxuICogUG9vbCBzaXppbmcgaXMgcmVhZCBmcm9tIGVudiAoYE1PTkdPX01BWF9QT09MYCwgYE1PTkdPX01JTl9QT09MYCxcbiAqIGBNT05HT19TRVJWRVJfU0VMRUNUSU9OX01TYCkuIFdpdGhvdXQgdGhlc2UgdGhlIGRyaXZlciBkZWZhdWx0IG9mXG4gKiBgbWF4UG9vbFNpemU9MTAwYCBsZXRzIGEgYnVyc3QgZXhoYXVzdCBNb25nbydzIGNvbm5lY3Rpb24gY2VpbGluZ1xuICogYWNyb3NzIHJlcGxpY2FzLiBEZWZhdWx0czogMjAgbWF4IC8gMiBtaW4gLyA1cyBzZXJ2ZXIgc2VsZWN0aW9uLlxuICpcbiAqIFBhc3MgdGhlIHNlcnZpY2UncyBvd24gbW9uZ29vc2UgaW5zdGFuY2Ug4oCUIGFwaS1zZXJ2ZXIgZG9lc24ndCBidW5kbGVcbiAqIG1vbmdvb3NlIGl0c2VsZi5cbiAqL1xubGV0IGhhbmRsZXJzV2lyZWQgPSBmYWxzZTtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNvbm5lY3RNb25nbyhtb25nb29zZTogTW9uZ29vc2VMaWtlLCB1cmk6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBtb25nb29zZS5zZXQoJ3N0cmljdFF1ZXJ5JywgdHJ1ZSk7XG5cbiAgLy8gV2lyZSB0aGUgY29ubmVjdGlvbiBldmVudCBoYW5kbGVycyBleGFjdGx5IG9uY2UuIFRoZSBzdGFydHVwIHN1cGVydmlzb3JcbiAgLy8gbWF5IGNhbGwgY29ubmVjdE1vbmdvIHJlcGVhdGVkbHkgKHJldHJ5aW5nIHVudGlsIE1vbmdvIGlzIHJlYWNoYWJsZSk7XG4gIC8vIHJlLXJlZ2lzdGVyaW5nIG9uIGV2ZXJ5IGF0dGVtcHQgd291bGQgbGVhayBsaXN0ZW5lcnMuXG4gIGlmICghaGFuZGxlcnNXaXJlZCkge1xuICAgIGhhbmRsZXJzV2lyZWQgPSB0cnVlO1xuXG4gICAgbW9uZ29vc2UuY29ubmVjdGlvbi5vbignZXJyb3InLCAoZXJyKSA9PiB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ01vbmdvREIgY29ubmVjdGlvbiBlcnJvcicsIHsgZXJyb3I6IGVycj8ubWVzc2FnZSB9KTtcbiAgICB9KTtcblxuICAgIG1vbmdvb3NlLmNvbm5lY3Rpb24ub24oJ2Rpc2Nvbm5lY3RlZCcsICgpID0+IHtcbiAgICAgIGxvZ2dlci53YXJuKCdNb25nb0RCIGRpc2Nvbm5lY3RlZCcpO1xuICAgIH0pO1xuXG4gICAgbW9uZ29vc2UuY29ubmVjdGlvbi5vbigncmVjb25uZWN0ZWQnLCAoKSA9PiB7XG4gICAgICBsb2dnZXIuaW5mbygnTW9uZ29EQiByZWNvbm5lY3RlZCcpO1xuICAgIH0pO1xuICB9XG5cbiAgY29uc3QgbWF4UG9vbFNpemUgPSBlbnZJbnQoJ01PTkdPX01BWF9QT09MJywgMjAsIHsgbWluOiAxIH0pO1xuICBjb25zdCBtaW5Qb29sU2l6ZSA9IGVudkludCgnTU9OR09fTUlOX1BPT0wnLCAyLCB7IG1pbjogMCB9KTtcbiAgY29uc3Qgc2VydmVyU2VsZWN0aW9uVGltZW91dE1TID0gZW52SW50KCdNT05HT19TRVJWRVJfU0VMRUNUSU9OX01TJywgNTAwMCwgeyBtaW46IDEgfSk7XG5cbiAgYXdhaXQgbW9uZ29vc2UuY29ubmVjdCh1cmksIHsgbWF4UG9vbFNpemUsIG1pblBvb2xTaXplLCBzZXJ2ZXJTZWxlY3Rpb25UaW1lb3V0TVMgfSk7XG4gIGxvZ2dlci5pbmZvKCdNb25nb0RCIGNvbm5lY3Rpb24gZXN0YWJsaXNoZWQnLCB7IG1heFBvb2xTaXplLCBtaW5Qb29sU2l6ZSwgc2VydmVyU2VsZWN0aW9uVGltZW91dE1TIH0pO1xufVxuIl19
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type QuotaReserveResult, type QuotaService, type QuotaType } from '@pipeline-builder/api-core';
|
|
2
|
+
import type { Response } from 'express';
|
|
3
|
+
/** The reserved quota slot, handed to the guarded body. */
|
|
4
|
+
export interface QuotaSlot {
|
|
5
|
+
/** Minted service token the reservation used — reuse it for the body's own S2S calls. */
|
|
6
|
+
readonly serviceAuth: string;
|
|
7
|
+
readonly reservation: QuotaReserveResult;
|
|
8
|
+
/**
|
|
9
|
+
* The reserved resource has been consumed (e.g. the AI provider started
|
|
10
|
+
* responding). From here on a failure KEEPS the slot — the cost was incurred.
|
|
11
|
+
*/
|
|
12
|
+
markConsumed(): void;
|
|
13
|
+
/** Give the slot back now. Idempotent: refunds at most once. */
|
|
14
|
+
refund(): void;
|
|
15
|
+
}
|
|
16
|
+
export interface QuotaReservationOptions {
|
|
17
|
+
quotaService: QuotaService;
|
|
18
|
+
orgId: string;
|
|
19
|
+
type: QuotaType;
|
|
20
|
+
/** Signer of the quota service token. Defaults to this process (`SERVICE_NAME`). */
|
|
21
|
+
serviceName?: string;
|
|
22
|
+
logWarn: (message: string, data?: unknown) => void;
|
|
23
|
+
/** When set, a denied reservation is answered on it (429, or 503 when unconfirmable). */
|
|
24
|
+
res?: Response;
|
|
25
|
+
}
|
|
26
|
+
export type QuotaReservationOutcome<T> = {
|
|
27
|
+
status: 'denied';
|
|
28
|
+
reservation: QuotaReserveResult;
|
|
29
|
+
} | {
|
|
30
|
+
status: 'done';
|
|
31
|
+
value: T;
|
|
32
|
+
}
|
|
33
|
+
/** Only with an `onError` handler: the body threw and the handler answered. */
|
|
34
|
+
| {
|
|
35
|
+
status: 'failed';
|
|
36
|
+
error: unknown;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Reserve one quota slot, run `body`, and settle the slot by ONE rule:
|
|
40
|
+
* keep it once the resource was consumed; refund it if the body fails first.
|
|
41
|
+
*
|
|
42
|
+
* - Denied → `{ status: 'denied' }` (answered on `res` when given); `body` never runs.
|
|
43
|
+
* - `body` resolves → the slot is kept (unless `body` called `slot.refund()`).
|
|
44
|
+
* - `body` throws → refunded unless consumed — marked via `slot.markConsumed()`
|
|
45
|
+
* or signalled by the error itself carrying `providerContacted === true`
|
|
46
|
+
* (an AI round-trip that completed but produced nothing usable). The error is
|
|
47
|
+
* then passed to `onError`, or rethrown when there is none.
|
|
48
|
+
*
|
|
49
|
+
* The reserve and refund authenticate with a minted service token, never the
|
|
50
|
+
* end-user bearer: the quota increment endpoint rejects user principals.
|
|
51
|
+
*/
|
|
52
|
+
export declare function withQuotaReservation<T>(opts: QuotaReservationOptions, body: (slot: QuotaSlot) => Promise<T>, onError?: (error: unknown) => void | Promise<void>): Promise<QuotaReservationOutcome<T>>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { decrementQuota, getServiceAuthHeader, reserveQuota, sendQuotaReserveDenied, serviceIdentity, } from '@pipeline-builder/api-core';
|
|
4
|
+
/**
|
|
5
|
+
* Reserve one quota slot, run `body`, and settle the slot by ONE rule:
|
|
6
|
+
* keep it once the resource was consumed; refund it if the body fails first.
|
|
7
|
+
*
|
|
8
|
+
* - Denied → `{ status: 'denied' }` (answered on `res` when given); `body` never runs.
|
|
9
|
+
* - `body` resolves → the slot is kept (unless `body` called `slot.refund()`).
|
|
10
|
+
* - `body` throws → refunded unless consumed — marked via `slot.markConsumed()`
|
|
11
|
+
* or signalled by the error itself carrying `providerContacted === true`
|
|
12
|
+
* (an AI round-trip that completed but produced nothing usable). The error is
|
|
13
|
+
* then passed to `onError`, or rethrown when there is none.
|
|
14
|
+
*
|
|
15
|
+
* The reserve and refund authenticate with a minted service token, never the
|
|
16
|
+
* end-user bearer: the quota increment endpoint rejects user principals.
|
|
17
|
+
*/
|
|
18
|
+
export async function withQuotaReservation(opts, body, onError) {
|
|
19
|
+
const { quotaService, orgId, type, logWarn } = opts;
|
|
20
|
+
const serviceAuth = getServiceAuthHeader({ serviceName: opts.serviceName ?? serviceIdentity(), orgId, role: 'member' });
|
|
21
|
+
// Reserve atomically BEFORE any work: two concurrent requests at the limit
|
|
22
|
+
// can't both pass.
|
|
23
|
+
const reservation = await reserveQuota(quotaService, orgId, type, serviceAuth);
|
|
24
|
+
if (reservation.exceeded) {
|
|
25
|
+
if (opts.res)
|
|
26
|
+
sendQuotaReserveDenied(opts.res, type, reservation);
|
|
27
|
+
return { status: 'denied', reservation };
|
|
28
|
+
}
|
|
29
|
+
let consumed = false;
|
|
30
|
+
let refunded = false;
|
|
31
|
+
const slot = {
|
|
32
|
+
serviceAuth,
|
|
33
|
+
reservation,
|
|
34
|
+
markConsumed: () => { consumed = true; },
|
|
35
|
+
refund: () => {
|
|
36
|
+
if (refunded)
|
|
37
|
+
return;
|
|
38
|
+
refunded = true;
|
|
39
|
+
// The reserved resetAt keeps a period rollover from being double-charged.
|
|
40
|
+
decrementQuota(quotaService, orgId, type, serviceAuth, logWarn, 1, reservation.quota.resetAt);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
return { status: 'done', value: await body(slot) };
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const errorSaysConsumed = error?.providerContacted === true;
|
|
48
|
+
if (!consumed && !errorSaysConsumed)
|
|
49
|
+
slot.refund();
|
|
50
|
+
if (!onError)
|
|
51
|
+
throw error;
|
|
52
|
+
await onError(error);
|
|
53
|
+
return { status: 'failed', error };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVvdGEtcmVzZXJ2YXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL3F1b3RhLXJlc2VydmF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUNMLGNBQWMsRUFDZCxvQkFBb0IsRUFDcEIsWUFBWSxFQUNaLHNCQUFzQixFQUN0QixlQUFlLEdBSWhCLE1BQU0sNEJBQTRCLENBQUM7QUFrQ3BDOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLG9CQUFvQixDQUN4QyxJQUE2QixFQUM3QixJQUFxQyxFQUNyQyxPQUFrRDtJQUVsRCxNQUFNLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSSxDQUFDO0lBQ3BELE1BQU0sV0FBVyxHQUFHLG9CQUFvQixDQUFDLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXLElBQUksZUFBZSxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBRXhILDJFQUEyRTtJQUMzRSxtQkFBbUI7SUFDbkIsTUFBTSxXQUFXLEdBQUcsTUFBTSxZQUFZLENBQUMsWUFBWSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDL0UsSUFBSSxXQUFXLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDekIsSUFBSSxJQUFJLENBQUMsR0FBRztZQUFFLHNCQUFzQixDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQ2xFLE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxDQUFDO0lBQzNDLENBQUM7SUFFRCxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDckIsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLE1BQU0sSUFBSSxHQUFjO1FBQ3RCLFdBQVc7UUFDWCxXQUFXO1FBQ1gsWUFBWSxFQUFFLEdBQUcsRUFBRSxHQUFHLFFBQVEsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3hDLE1BQU0sRUFBRSxHQUFHLEVBQUU7WUFDWCxJQUFJLFFBQVE7Z0JBQUUsT0FBTztZQUNyQixRQUFRLEdBQUcsSUFBSSxDQUFDO1lBQ2hCLDBFQUEwRTtZQUMxRSxjQUFjLENBQUMsWUFBWSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxDQUFDLEVBQUUsV0FBVyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNoRyxDQUFDO0tBQ0YsQ0FBQztJQUVGLElBQUksQ0FBQztRQUNILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO0lBQ3JELENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxpQkFBaUIsR0FBSSxLQUFnRCxFQUFFLGlCQUFpQixLQUFLLElBQUksQ0FBQztRQUN4RyxJQUFJLENBQUMsUUFBUSxJQUFJLENBQUMsaUJBQWlCO1lBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ25ELElBQUksQ0FBQyxPQUFPO1lBQUUsTUFBTSxLQUFLLENBQUM7UUFDMUIsTUFBTSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDckIsT0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDckMsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHtcbiAgZGVjcmVtZW50UXVvdGEsXG4gIGdldFNlcnZpY2VBdXRoSGVhZGVyLFxuICByZXNlcnZlUXVvdGEsXG4gIHNlbmRRdW90YVJlc2VydmVEZW5pZWQsXG4gIHNlcnZpY2VJZGVudGl0eSxcbiAgdHlwZSBRdW90YVJlc2VydmVSZXN1bHQsXG4gIHR5cGUgUXVvdGFTZXJ2aWNlLFxuICB0eXBlIFF1b3RhVHlwZSxcbn0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHR5cGUgeyBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuXG4vKiogVGhlIHJlc2VydmVkIHF1b3RhIHNsb3QsIGhhbmRlZCB0byB0aGUgZ3VhcmRlZCBib2R5LiAqL1xuZXhwb3J0IGludGVyZmFjZSBRdW90YVNsb3Qge1xuICAvKiogTWludGVkIHNlcnZpY2UgdG9rZW4gdGhlIHJlc2VydmF0aW9uIHVzZWQg4oCUIHJldXNlIGl0IGZvciB0aGUgYm9keSdzIG93biBTMlMgY2FsbHMuICovXG4gIHJlYWRvbmx5IHNlcnZpY2VBdXRoOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHJlc2VydmF0aW9uOiBRdW90YVJlc2VydmVSZXN1bHQ7XG4gIC8qKlxuICAgKiBUaGUgcmVzZXJ2ZWQgcmVzb3VyY2UgaGFzIGJlZW4gY29uc3VtZWQgKGUuZy4gdGhlIEFJIHByb3ZpZGVyIHN0YXJ0ZWRcbiAgICogcmVzcG9uZGluZykuIEZyb20gaGVyZSBvbiBhIGZhaWx1cmUgS0VFUFMgdGhlIHNsb3Qg4oCUIHRoZSBjb3N0IHdhcyBpbmN1cnJlZC5cbiAgICovXG4gIG1hcmtDb25zdW1lZCgpOiB2b2lkO1xuICAvKiogR2l2ZSB0aGUgc2xvdCBiYWNrIG5vdy4gSWRlbXBvdGVudDogcmVmdW5kcyBhdCBtb3N0IG9uY2UuICovXG4gIHJlZnVuZCgpOiB2b2lkO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFF1b3RhUmVzZXJ2YXRpb25PcHRpb25zIHtcbiAgcXVvdGFTZXJ2aWNlOiBRdW90YVNlcnZpY2U7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIHR5cGU6IFF1b3RhVHlwZTtcbiAgLyoqIFNpZ25lciBvZiB0aGUgcXVvdGEgc2VydmljZSB0b2tlbi4gRGVmYXVsdHMgdG8gdGhpcyBwcm9jZXNzIChgU0VSVklDRV9OQU1FYCkuICovXG4gIHNlcnZpY2VOYW1lPzogc3RyaW5nO1xuICBsb2dXYXJuOiAobWVzc2FnZTogc3RyaW5nLCBkYXRhPzogdW5rbm93bikgPT4gdm9pZDtcbiAgLyoqIFdoZW4gc2V0LCBhIGRlbmllZCByZXNlcnZhdGlvbiBpcyBhbnN3ZXJlZCBvbiBpdCAoNDI5LCBvciA1MDMgd2hlbiB1bmNvbmZpcm1hYmxlKS4gKi9cbiAgcmVzPzogUmVzcG9uc2U7XG59XG5cbmV4cG9ydCB0eXBlIFF1b3RhUmVzZXJ2YXRpb25PdXRjb21lPFQ+ID1cbiAgfCB7IHN0YXR1czogJ2RlbmllZCc7IHJlc2VydmF0aW9uOiBRdW90YVJlc2VydmVSZXN1bHQgfVxuICB8IHsgc3RhdHVzOiAnZG9uZSc7IHZhbHVlOiBUIH1cbiAgLyoqIE9ubHkgd2l0aCBhbiBgb25FcnJvcmAgaGFuZGxlcjogdGhlIGJvZHkgdGhyZXcgYW5kIHRoZSBoYW5kbGVyIGFuc3dlcmVkLiAqL1xuICB8IHsgc3RhdHVzOiAnZmFpbGVkJzsgZXJyb3I6IHVua25vd24gfTtcblxuLyoqXG4gKiBSZXNlcnZlIG9uZSBxdW90YSBzbG90LCBydW4gYGJvZHlgLCBhbmQgc2V0dGxlIHRoZSBzbG90IGJ5IE9ORSBydWxlOlxuICoga2VlcCBpdCBvbmNlIHRoZSByZXNvdXJjZSB3YXMgY29uc3VtZWQ7IHJlZnVuZCBpdCBpZiB0aGUgYm9keSBmYWlscyBmaXJzdC5cbiAqXG4gKiAtIERlbmllZCDihpIgYHsgc3RhdHVzOiAnZGVuaWVkJyB9YCAoYW5zd2VyZWQgb24gYHJlc2Agd2hlbiBnaXZlbik7IGBib2R5YCBuZXZlciBydW5zLlxuICogLSBgYm9keWAgcmVzb2x2ZXMg4oaSIHRoZSBzbG90IGlzIGtlcHQgKHVubGVzcyBgYm9keWAgY2FsbGVkIGBzbG90LnJlZnVuZCgpYCkuXG4gKiAtIGBib2R5YCB0aHJvd3Mg4oaSIHJlZnVuZGVkIHVubGVzcyBjb25zdW1lZCDigJQgbWFya2VkIHZpYSBgc2xvdC5tYXJrQ29uc3VtZWQoKWBcbiAqICAgb3Igc2lnbmFsbGVkIGJ5IHRoZSBlcnJvciBpdHNlbGYgY2FycnlpbmcgYHByb3ZpZGVyQ29udGFjdGVkID09PSB0cnVlYFxuICogICAoYW4gQUkgcm91bmQtdHJpcCB0aGF0IGNvbXBsZXRlZCBidXQgcHJvZHVjZWQgbm90aGluZyB1c2FibGUpLiBUaGUgZXJyb3IgaXNcbiAqICAgdGhlbiBwYXNzZWQgdG8gYG9uRXJyb3JgLCBvciByZXRocm93biB3aGVuIHRoZXJlIGlzIG5vbmUuXG4gKlxuICogVGhlIHJlc2VydmUgYW5kIHJlZnVuZCBhdXRoZW50aWNhdGUgd2l0aCBhIG1pbnRlZCBzZXJ2aWNlIHRva2VuLCBuZXZlciB0aGVcbiAqIGVuZC11c2VyIGJlYXJlcjogdGhlIHF1b3RhIGluY3JlbWVudCBlbmRwb2ludCByZWplY3RzIHVzZXIgcHJpbmNpcGFscy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdpdGhRdW90YVJlc2VydmF0aW9uPFQ+KFxuICBvcHRzOiBRdW90YVJlc2VydmF0aW9uT3B0aW9ucyxcbiAgYm9keTogKHNsb3Q6IFF1b3RhU2xvdCkgPT4gUHJvbWlzZTxUPixcbiAgb25FcnJvcj86IChlcnJvcjogdW5rbm93bikgPT4gdm9pZCB8IFByb21pc2U8dm9pZD4sXG4pOiBQcm9taXNlPFF1b3RhUmVzZXJ2YXRpb25PdXRjb21lPFQ+PiB7XG4gIGNvbnN0IHsgcXVvdGFTZXJ2aWNlLCBvcmdJZCwgdHlwZSwgbG9nV2FybiB9ID0gb3B0cztcbiAgY29uc3Qgc2VydmljZUF1dGggPSBnZXRTZXJ2aWNlQXV0aEhlYWRlcih7IHNlcnZpY2VOYW1lOiBvcHRzLnNlcnZpY2VOYW1lID8/IHNlcnZpY2VJZGVudGl0eSgpLCBvcmdJZCwgcm9sZTogJ21lbWJlcicgfSk7XG5cbiAgLy8gUmVzZXJ2ZSBhdG9taWNhbGx5IEJFRk9SRSBhbnkgd29yazogdHdvIGNvbmN1cnJlbnQgcmVxdWVzdHMgYXQgdGhlIGxpbWl0XG4gIC8vIGNhbid0IGJvdGggcGFzcy5cbiAgY29uc3QgcmVzZXJ2YXRpb24gPSBhd2FpdCByZXNlcnZlUXVvdGEocXVvdGFTZXJ2aWNlLCBvcmdJZCwgdHlwZSwgc2VydmljZUF1dGgpO1xuICBpZiAocmVzZXJ2YXRpb24uZXhjZWVkZWQpIHtcbiAgICBpZiAob3B0cy5yZXMpIHNlbmRRdW90YVJlc2VydmVEZW5pZWQob3B0cy5yZXMsIHR5cGUsIHJlc2VydmF0aW9uKTtcbiAgICByZXR1cm4geyBzdGF0dXM6ICdkZW5pZWQnLCByZXNlcnZhdGlvbiB9O1xuICB9XG5cbiAgbGV0IGNvbnN1bWVkID0gZmFsc2U7XG4gIGxldCByZWZ1bmRlZCA9IGZhbHNlO1xuICBjb25zdCBzbG90OiBRdW90YVNsb3QgPSB7XG4gICAgc2VydmljZUF1dGgsXG4gICAgcmVzZXJ2YXRpb24sXG4gICAgbWFya0NvbnN1bWVkOiAoKSA9PiB7IGNvbnN1bWVkID0gdHJ1ZTsgfSxcbiAgICByZWZ1bmQ6ICgpID0+IHtcbiAgICAgIGlmIChyZWZ1bmRlZCkgcmV0dXJuO1xuICAgICAgcmVmdW5kZWQgPSB0cnVlO1xuICAgICAgLy8gVGhlIHJlc2VydmVkIHJlc2V0QXQga2VlcHMgYSBwZXJpb2Qgcm9sbG92ZXIgZnJvbSBiZWluZyBkb3VibGUtY2hhcmdlZC5cbiAgICAgIGRlY3JlbWVudFF1b3RhKHF1b3RhU2VydmljZSwgb3JnSWQsIHR5cGUsIHNlcnZpY2VBdXRoLCBsb2dXYXJuLCAxLCByZXNlcnZhdGlvbi5xdW90YS5yZXNldEF0KTtcbiAgICB9LFxuICB9O1xuXG4gIHRyeSB7XG4gICAgcmV0dXJuIHsgc3RhdHVzOiAnZG9uZScsIHZhbHVlOiBhd2FpdCBib2R5KHNsb3QpIH07XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXJyb3JTYXlzQ29uc3VtZWQgPSAoZXJyb3IgYXMgeyBwcm92aWRlckNvbnRhY3RlZD86IHVua25vd24gfSB8IG51bGwpPy5wcm92aWRlckNvbnRhY3RlZCA9PT0gdHJ1ZTtcbiAgICBpZiAoIWNvbnN1bWVkICYmICFlcnJvclNheXNDb25zdW1lZCkgc2xvdC5yZWZ1bmQoKTtcbiAgICBpZiAoIW9uRXJyb3IpIHRocm93IGVycm9yO1xuICAgIGF3YWl0IG9uRXJyb3IoZXJyb3IpO1xuICAgIHJldHVybiB7IHN0YXR1czogJ2ZhaWxlZCcsIGVycm9yIH07XG4gIH1cbn1cbiJdfQ==
|
|
@@ -8,6 +8,16 @@ export interface OrgRateLimitOptions {
|
|
|
8
8
|
windowMs: number;
|
|
9
9
|
/** Optional 429 message override. */
|
|
10
10
|
message?: string;
|
|
11
|
+
/**
|
|
12
|
+
* What one bucket is (default `org`):
|
|
13
|
+
* - `org` — the verified org (an org service account gets its own bucket);
|
|
14
|
+
* - `user` — the verified caller (`sub`), for per-person caps;
|
|
15
|
+
* - `ip` — the TRUSTED client IP (`req.ip`, resolved by the app's
|
|
16
|
+
* `trust proxy` setting — never a raw forwarding header), for
|
|
17
|
+
* per-address caps that a user can't reset by switching orgs.
|
|
18
|
+
* Every mode falls back to the client-IP bucket for an unauthenticated caller.
|
|
19
|
+
*/
|
|
20
|
+
keyBy?: 'org' | 'user' | 'ip';
|
|
11
21
|
}
|
|
12
22
|
/**
|
|
13
23
|
* Per-ORGANIZATION rate limiter for a specific hot/expensive route, keyed on the
|
|
@@ -21,7 +21,7 @@ import { createSharedRateLimitStore } from './rate-limit-store.js';
|
|
|
21
21
|
* withRoute(handler));
|
|
22
22
|
*/
|
|
23
23
|
export function rateLimitByOrg(opts) {
|
|
24
|
-
const { name, max, windowMs, message } = opts;
|
|
24
|
+
const { name, max, windowMs, message, keyBy = 'org' } = opts;
|
|
25
25
|
const options = {
|
|
26
26
|
max,
|
|
27
27
|
windowMs,
|
|
@@ -39,10 +39,15 @@ export function rateLimitByOrg(opts) {
|
|
|
39
39
|
// bucket when unauthenticated so pre-auth traffic is still bounded.
|
|
40
40
|
// Namespaced (`sa:`/`org:`/`ip:`) so no two id spaces can collide.
|
|
41
41
|
keyGenerator: (req) => {
|
|
42
|
+
const ip = `ip:${ipKeyGenerator(req.ip || 'anon', 64)}`;
|
|
43
|
+
if (keyBy === 'ip')
|
|
44
|
+
return ip;
|
|
45
|
+
if (keyBy === 'user')
|
|
46
|
+
return req.user?.sub ? `user:${req.user.sub}` : ip;
|
|
42
47
|
if (req.user?.principalType === 'service_account' && req.user.sub)
|
|
43
48
|
return `sa:${req.user.sub}`;
|
|
44
49
|
const orgId = req.user?.organizationId;
|
|
45
|
-
return orgId ? `org:${orgId.toLowerCase()}` :
|
|
50
|
+
return orgId ? `org:${orgId.toLowerCase()}` : ip;
|
|
46
51
|
},
|
|
47
52
|
handler: (_req, res) => {
|
|
48
53
|
sendError(res, 429, message ?? `Too many ${name} requests, please slow down.`, ErrorCode.RATE_LIMIT_EXCEEDED);
|
|
@@ -55,4 +60,4 @@ export function rateLimitByOrg(opts) {
|
|
|
55
60
|
options.store = createSharedRateLimitStore(`${process.env.SERVICE_NAME || 'api'}:${name}`);
|
|
56
61
|
return rateLimit(options);
|
|
57
62
|
}
|
|
58
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZS1saW1pdC1ieS1vcmcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL3JhdGUtbGltaXQtYnktb3JnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUUxRixPQUFPLFNBQVMsRUFBRSxFQUFFLGNBQWMsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQy9ELE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBd0JuRTs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQUMsSUFBeUI7SUFDdEQsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxLQUFLLEdBQUcsS0FBSyxFQUFFLEdBQUcsSUFBSSxDQUFDO0lBRTdELE1BQU0sT0FBTyxHQUFvQztRQUMvQyxHQUFHO1FBQ0gsUUFBUTtRQUNSLGVBQWUsRUFBRSxJQUFJO1FBQ3JCLGFBQWEsRUFBRSxLQUFLO1FBQ3BCLDJFQUEyRTtRQUMzRSxJQUFJLEVBQUUsQ0FBQyxHQUFZLEVBQUUsRUFBRSxDQUFDLHNCQUFzQixDQUFDLEdBQUcsQ0FBQztRQUNuRCwyRUFBMkU7UUFDM0UsZ0JBQWdCLEVBQUUsSUFBSTtRQUN0QixzRUFBc0U7UUFDdEUsNEVBQTRFO1FBQzVFLDBFQUEwRTtRQUMxRSx5RUFBeUU7UUFDekUsNEVBQTRFO1FBQzVFLG9FQUFvRTtRQUNwRSxtRUFBbUU7UUFDbkUsWUFBWSxFQUFFLENBQUMsR0FBWSxFQUFVLEVBQUU7WUFDckMsTUFBTSxFQUFFLEdBQUcsTUFBTSxjQUFjLENBQUMsR0FBRyxDQUFDLEVBQUUsSUFBSSxNQUFNLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUN4RCxJQUFJLEtBQUssS0FBSyxJQUFJO2dCQUFFLE9BQU8sRUFBRSxDQUFDO1lBQzlCLElBQUksS0FBSyxLQUFLLE1BQU07Z0JBQUUsT0FBTyxHQUFHLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUMsUUFBUSxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDekUsSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLGFBQWEsS0FBSyxpQkFBaUIsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLEdBQUc7Z0JBQUUsT0FBTyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDL0YsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxjQUFjLENBQUM7WUFDdkMsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNuRCxDQUFDO1FBQ0QsT0FBTyxFQUFFLENBQUMsSUFBYSxFQUFFLEdBQWEsRUFBUSxFQUFFO1lBQzlDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLE9BQU8sSUFBSSxZQUFZLElBQUksOEJBQThCLEVBQUUsU0FBUyxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDaEgsQ0FBQztLQUNGLENBQUM7SUFFRiwrRUFBK0U7SUFDL0UsaUZBQWlGO0lBQ2pGLDJFQUEyRTtJQUMzRSxtQkFBbUI7SUFDbkIsT0FBTyxDQUFDLEtBQUssR0FBRywwQkFBMEIsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxJQUFJLEtBQUssSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRTNGLE9BQU8sU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQzVCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgc2VuZEVycm9yLCBFcnJvckNvZGUsIHZlcmlmeVNlcnZpY2VQcmluY2lwYWwgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgcmF0ZUxpbWl0LCB7IGlwS2V5R2VuZXJhdG9yIH0gZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcbmltcG9ydCB7IGNyZWF0ZVNoYXJlZFJhdGVMaW1pdFN0b3JlIH0gZnJvbSAnLi9yYXRlLWxpbWl0LXN0b3JlLmpzJztcblxuLyoqIENvbmZpZyBmb3IgYSB7QGxpbmsgcmF0ZUxpbWl0QnlPcmd9IGxpbWl0ZXIuICovXG5leHBvcnQgaW50ZXJmYWNlIE9yZ1JhdGVMaW1pdE9wdGlvbnMge1xuICAvKiogVW5pcXVlIG5hbWUg4oCUIG5hbWVzcGFjZXMgdGhlIFJlZGlzIGJ1Y2tldCArIGxhYmVscyBsb2dzLiAqL1xuICBuYW1lOiBzdHJpbmc7XG4gIC8qKiBNYXggcmVxdWVzdHMgcGVyIHdpbmRvdyBwZXIgb3JnIChvciBwZXIgY2xpZW50IElQIHdoZW4gdW5hdXRoZW50aWNhdGVkKS4gKi9cbiAgbWF4OiBudW1iZXI7XG4gIC8qKiBXaW5kb3cgbGVuZ3RoIGluIG1zLiAqL1xuICB3aW5kb3dNczogbnVtYmVyO1xuICAvKiogT3B0aW9uYWwgNDI5IG1lc3NhZ2Ugb3ZlcnJpZGUuICovXG4gIG1lc3NhZ2U/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBXaGF0IG9uZSBidWNrZXQgaXMgKGRlZmF1bHQgYG9yZ2ApOlxuICAgKiAgLSBgb3JnYCAg4oCUIHRoZSB2ZXJpZmllZCBvcmcgKGFuIG9yZyBzZXJ2aWNlIGFjY291bnQgZ2V0cyBpdHMgb3duIGJ1Y2tldCk7XG4gICAqICAtIGB1c2VyYCDigJQgdGhlIHZlcmlmaWVkIGNhbGxlciAoYHN1YmApLCBmb3IgcGVyLXBlcnNvbiBjYXBzO1xuICAgKiAgLSBgaXBgICAg4oCUIHRoZSBUUlVTVEVEIGNsaWVudCBJUCAoYHJlcS5pcGAsIHJlc29sdmVkIGJ5IHRoZSBhcHAnc1xuICAgKiAgICAgICAgICAgICBgdHJ1c3QgcHJveHlgIHNldHRpbmcg4oCUIG5ldmVyIGEgcmF3IGZvcndhcmRpbmcgaGVhZGVyKSwgZm9yXG4gICAqICAgICAgICAgICAgIHBlci1hZGRyZXNzIGNhcHMgdGhhdCBhIHVzZXIgY2FuJ3QgcmVzZXQgYnkgc3dpdGNoaW5nIG9yZ3MuXG4gICAqIEV2ZXJ5IG1vZGUgZmFsbHMgYmFjayB0byB0aGUgY2xpZW50LUlQIGJ1Y2tldCBmb3IgYW4gdW5hdXRoZW50aWNhdGVkIGNhbGxlci5cbiAgICovXG4gIGtleUJ5PzogJ29yZycgfCAndXNlcicgfCAnaXAnO1xufVxuXG4vKipcbiAqIFBlci1PUkdBTklaQVRJT04gcmF0ZSBsaW1pdGVyIGZvciBhIHNwZWNpZmljIGhvdC9leHBlbnNpdmUgcm91dGUsIGtleWVkIG9uIHRoZVxuICogVkVSSUZJRUQgYXV0aCBvcmcg4oCUIHRoZSBjb21wbGVtZW50IHRvIHRoZSBjb2Fyc2UgZ2xvYmFsIHBlci1JUCBsaW1pdGVyIGluXG4gKiBgY3JlYXRlQXBwYCAod2hpY2ggYSB3aG9sZSBvcmcgYmVoaW5kIG9uZSBOQVQgc2hhcmVzLCBhbmQgYSBkaXN0cmlidXRlZFxuICogYXR0YWNrZXIgc3ByZWFkcyBhY3Jvc3MgSVAgYnVja2V0cyB0byBldmFkZSkuXG4gKlxuICogTVVTVCBiZSBtb3VudGVkIEFGVEVSIGByZXF1aXJlQXV0aGAgc28gYHJlcS51c2VyLm9yZ2FuaXphdGlvbklkYCBpcyBwb3B1bGF0ZWQ7XG4gKiBhbiB1bmF1dGhlbnRpY2F0ZWQgY2FsbGVyIGZhbGxzIGJhY2sgdG8gYSBjbGllbnQtSVAgYnVja2V0IChuZXZlciBhIHNwb29mYWJsZVxuICogaGVhZGVyKSBzbyBpdCBzdGF5cyBib3VuZGVkLiBWZXJpZmllZCBpbnRlcm5hbCBzZXJ2aWNlIHByaW5jaXBhbHMgYXJlIGV4ZW1wdFxuICogKHRoZXkgY2FycnkgYSBzaWduZWQgc2VydmljZSBKV1QpLiBVc2VzIHRoZSBzaGFyZWQgZW52IFJlZGlzIHN0b3JlXG4gKiAoY3Jvc3MtcmVwbGljYSwgbmFtZXNwYWNlZCBieSBgbmFtZWApIHdoZW4gY29uZmlndXJlZCwgZWxzZSBpbi1tZW1vcnkuXG4gKlxuICogQGV4YW1wbGVcbiAqICAgcm91dGVyLnBvc3QoJy8nLCAuLi5jcmVhdGVBdXRoZW50aWNhdGVkV2l0aE9yZ1JvdXRlKCksIHJlcXVpcmVQZXJtaXNzaW9uKCdtZXNzYWdlczp3cml0ZScpLFxuICogICAgIHJhdGVMaW1pdEJ5T3JnKHsgbmFtZTogJ21lc3NhZ2Utc2VuZCcsIG1heDogNjAsIHdpbmRvd01zOiA2MF8wMDAgfSksXG4gKiAgICAgd2l0aFJvdXRlKGhhbmRsZXIpKTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJhdGVMaW1pdEJ5T3JnKG9wdHM6IE9yZ1JhdGVMaW1pdE9wdGlvbnMpIHtcbiAgY29uc3QgeyBuYW1lLCBtYXgsIHdpbmRvd01zLCBtZXNzYWdlLCBrZXlCeSA9ICdvcmcnIH0gPSBvcHRzO1xuXG4gIGNvbnN0IG9wdGlvbnM6IFBhcmFtZXRlcnM8dHlwZW9mIHJhdGVMaW1pdD5bMF0gPSB7XG4gICAgbWF4LFxuICAgIHdpbmRvd01zLFxuICAgIHN0YW5kYXJkSGVhZGVyczogdHJ1ZSxcbiAgICBsZWdhY3lIZWFkZXJzOiBmYWxzZSxcbiAgICAvLyBFeGVtcHQgY3J5cHRvZ3JhcGhpY2FsbHktdmVyaWZpZWQgaW50ZXJuYWwgc2VydmljZSBjYWxsZXJzIChzaWduZWQgSldUKS5cbiAgICBza2lwOiAocmVxOiBSZXF1ZXN0KSA9PiB2ZXJpZnlTZXJ2aWNlUHJpbmNpcGFsKHJlcSksXG4gICAgLy8gUmVkaXMgc3RvcmUgZmFpbHVyZSBkZWdyYWRlcyB0byBcIm5vdCBsaW1pdGVkXCIsIG5ldmVyIGEgNTAwIG9uIHRoZSByb3V0ZS5cbiAgICBwYXNzT25TdG9yZUVycm9yOiB0cnVlLFxuICAgIC8vIEJ1Y2tldCBieSBWRVJJRklFRCBpZGVudGl0eSAoc2V0IGJ5IHJlcXVpcmVBdXRoKSwgTk9UIHRoZSBzcG9vZmFibGVcbiAgICAvLyBgeC1vcmctaWRgIGhlYWRlci4gQW4gT1JHIFNFUlZJQ0UgQUNDT1VOVCBnZXRzIGl0cyBPV04gYnVja2V0IHJhdGhlciB0aGFuXG4gICAgLy8gaXRzIG9yZydzOiBhdXRvbWF0aW9uIGlzIGV4YWN0bHkgdGhlIHRyYWZmaWMgc2hhcGUgdGhhdCB3b3VsZCBvdGhlcndpc2VcbiAgICAvLyBkcmFpbiB0aGUgd2luZG93IGl0cyBvcmcncyBwZW9wbGUgc2hhcmUsIGFuZCB0aGUgcGxhbiByZXF1aXJlcyBtYWNoaW5lXG4gICAgLy8gcmF0ZSBsaW1pdHMgdG8gYmUga2V5ZWQgcGVyIGFjY291bnQuIEZhbGxzIGJhY2sgdG8gYSBub3JtYWxpemVkIGNsaWVudC1JUFxuICAgIC8vIGJ1Y2tldCB3aGVuIHVuYXV0aGVudGljYXRlZCBzbyBwcmUtYXV0aCB0cmFmZmljIGlzIHN0aWxsIGJvdW5kZWQuXG4gICAgLy8gTmFtZXNwYWNlZCAoYHNhOmAvYG9yZzpgL2BpcDpgKSBzbyBubyB0d28gaWQgc3BhY2VzIGNhbiBjb2xsaWRlLlxuICAgIGtleUdlbmVyYXRvcjogKHJlcTogUmVxdWVzdCk6IHN0cmluZyA9PiB7XG4gICAgICBjb25zdCBpcCA9IGBpcDoke2lwS2V5R2VuZXJhdG9yKHJlcS5pcCB8fCAnYW5vbicsIDY0KX1gO1xuICAgICAgaWYgKGtleUJ5ID09PSAnaXAnKSByZXR1cm4gaXA7XG4gICAgICBpZiAoa2V5QnkgPT09ICd1c2VyJykgcmV0dXJuIHJlcS51c2VyPy5zdWIgPyBgdXNlcjoke3JlcS51c2VyLnN1Yn1gIDogaXA7XG4gICAgICBpZiAocmVxLnVzZXI/LnByaW5jaXBhbFR5cGUgPT09ICdzZXJ2aWNlX2FjY291bnQnICYmIHJlcS51c2VyLnN1YikgcmV0dXJuIGBzYToke3JlcS51c2VyLnN1Yn1gO1xuICAgICAgY29uc3Qgb3JnSWQgPSByZXEudXNlcj8ub3JnYW5pemF0aW9uSWQ7XG4gICAgICByZXR1cm4gb3JnSWQgPyBgb3JnOiR7b3JnSWQudG9Mb3dlckNhc2UoKX1gIDogaXA7XG4gICAgfSxcbiAgICBoYW5kbGVyOiAoX3JlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSk6IHZvaWQgPT4ge1xuICAgICAgc2VuZEVycm9yKHJlcywgNDI5LCBtZXNzYWdlID8/IGBUb28gbWFueSAke25hbWV9IHJlcXVlc3RzLCBwbGVhc2Ugc2xvdyBkb3duLmAsIEVycm9yQ29kZS5SQVRFX0xJTUlUX0VYQ0VFREVEKTtcbiAgICB9LFxuICB9O1xuXG4gIC8vIFNoYXJlZCBjcm9zcy1yZXBsaWNhIHN0b3JlIChvbmUgcHJvY2Vzcy13aWRlIFJlZGlzIGNvbm5lY3Rpb24pIHdoZW4gUmVkaXMgaXNcbiAgLy8gY29uZmlndXJlZCwgZWxzZSBwZXItcHJvY2VzcyBtZW1vcnkuIE5hbWVzcGFjZWQgYnkgc2VydmljZSBBTkQgbGltaXRlciBuYW1lIHNvXG4gIC8vIGRpc3RpbmN0IGxpbWl0ZXJzIOKAlCBvciBzYW1lLW5hbWVkIGxpbWl0ZXJzIGluIGRpZmZlcmVudCBzZXJ2aWNlcyDigJQgbmV2ZXJcbiAgLy8gc2hhcmUgYSBjb3VudGVyLlxuICBvcHRpb25zLnN0b3JlID0gY3JlYXRlU2hhcmVkUmF0ZUxpbWl0U3RvcmUoYCR7cHJvY2Vzcy5lbnYuU0VSVklDRV9OQU1FIHx8ICdhcGknfToke25hbWV9YCk7XG5cbiAgcmV0dXJuIHJhdGVMaW1pdChvcHRpb25zKTtcbn1cbiJdfQ==
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Request, Response, NextFunction } from 'express';
|
|
2
2
|
/**
|
|
3
|
-
* Create middleware that validates the request has
|
|
3
|
+
* Create middleware that validates the request has a resolved tenant org (`ctx.identity.orgId`).
|
|
4
4
|
*
|
|
5
5
|
* @returns Express middleware
|
|
6
6
|
*
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { ErrorCode, sendError } from '@pipeline-builder/api-core';
|
|
4
4
|
import { getContext } from './get-context.js';
|
|
5
5
|
/**
|
|
6
|
-
* Create middleware that validates the request has
|
|
6
|
+
* Create middleware that validates the request has a resolved tenant org (`ctx.identity.orgId`).
|
|
7
7
|
*
|
|
8
8
|
* @returns Express middleware
|
|
9
9
|
*
|
|
@@ -18,11 +18,11 @@ export function requireOrgId() {
|
|
|
18
18
|
return (req, res, next) => {
|
|
19
19
|
const ctx = getContext(req);
|
|
20
20
|
if (!ctx.identity.orgId) {
|
|
21
|
-
ctx.log('ERROR', 'Organization ID is missing from request
|
|
22
|
-
sendError(res, 400, 'Organization ID is required
|
|
21
|
+
ctx.log('ERROR', 'Organization ID is missing from the request identity');
|
|
22
|
+
sendError(res, 400, 'Organization ID is required: the caller has no active organization.', ErrorCode.VALIDATION_ERROR);
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
next();
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
28
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVxdWlyZS1vcmctaWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL3JlcXVpcmUtb3JnLWlkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVsRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFOUM7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUsWUFBWTtJQUMxQixPQUFPLENBQUMsR0FBWSxFQUFFLEdBQWEsRUFBRSxJQUFrQixFQUFRLEVBQUU7UUFDL0QsTUFBTSxHQUFHLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTVCLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3hCLEdBQUcsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHNEQUFzRCxDQUFDLENBQUM7WUFDekUsU0FBUyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUscUVBQXFFLEVBQUUsU0FBUyxDQUFDLGdCQUFnQixDQUFDLENBQUM7WUFDdkgsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLEVBQUUsQ0FBQztJQUNULENBQUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgRXJyb3JDb2RlLCBzZW5kRXJyb3IgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBOZXh0RnVuY3Rpb24gfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IGdldENvbnRleHQgfSBmcm9tICcuL2dldC1jb250ZXh0LmpzJztcblxuLyoqXG4gKiBDcmVhdGUgbWlkZGxld2FyZSB0aGF0IHZhbGlkYXRlcyB0aGUgcmVxdWVzdCBoYXMgYSByZXNvbHZlZCB0ZW5hbnQgb3JnIChgY3R4LmlkZW50aXR5Lm9yZ0lkYCkuXG4gKlxuICogQHJldHVybnMgRXhwcmVzcyBtaWRkbGV3YXJlXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHsgYXBwLCBzc2VNYW5hZ2VyIH0gPSBjcmVhdGVBcHAoKTtcbiAqXG4gKiBhcHAuZ2V0KCcvcGlwZWxpbmVzJywgcmVxdWlyZUF1dGgsIHJlcXVpcmVPcmdJZCgpLCBoYW5kbGVyKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVxdWlyZU9yZ0lkKCkge1xuICByZXR1cm4gKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKTogdm9pZCA9PiB7XG4gICAgY29uc3QgY3R4ID0gZ2V0Q29udGV4dChyZXEpO1xuXG4gICAgaWYgKCFjdHguaWRlbnRpdHkub3JnSWQpIHtcbiAgICAgIGN0eC5sb2coJ0VSUk9SJywgJ09yZ2FuaXphdGlvbiBJRCBpcyBtaXNzaW5nIGZyb20gdGhlIHJlcXVlc3QgaWRlbnRpdHknKTtcbiAgICAgIHNlbmRFcnJvcihyZXMsIDQwMCwgJ09yZ2FuaXphdGlvbiBJRCBpcyByZXF1aXJlZDogdGhlIGNhbGxlciBoYXMgbm8gYWN0aXZlIG9yZ2FuaXphdGlvbi4nLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbmV4dCgpO1xuICB9O1xufVxuIl19
|
package/lib/api/route-wrapper.js
CHANGED
|
@@ -41,9 +41,7 @@ export function withRoute(handler, options = {}) {
|
|
|
41
41
|
return async (req, res) => {
|
|
42
42
|
const ctx = getContext(req);
|
|
43
43
|
// `identity.orgId` is already normalized (trimmed + lowercased) once at
|
|
44
|
-
// resolution in api-core's `getIdentity`, so the
|
|
45
|
-
// `.toLowerCase()` here is redundant — kept normalization at the single
|
|
46
|
-
// source of truth instead. This value now matches the RLS GUC exactly.
|
|
44
|
+
// resolution in api-core's `getIdentity`, so it matches the RLS GUC exactly.
|
|
47
45
|
const orgId = ctx.identity.orgId || '';
|
|
48
46
|
const userId = ctx.identity.userId || '';
|
|
49
47
|
if (requireOrgId && !orgId) {
|
|
@@ -65,7 +63,9 @@ export function withRoute(handler, options = {}) {
|
|
|
65
63
|
const status = error.statusCode >= 400 && error.statusCode < 600
|
|
66
64
|
? error.statusCode
|
|
67
65
|
: 500;
|
|
68
|
-
return
|
|
66
|
+
return error.details === undefined
|
|
67
|
+
? sendError(res, status, error.message, error.code)
|
|
68
|
+
: sendError(res, status, error.message, error.code, error.details);
|
|
69
69
|
}
|
|
70
70
|
// Unhandled error → 500. NEVER echo the raw message to the client: DB driver
|
|
71
71
|
// errors carry the SQL + bound params, and CrudService deliberately lets them
|
|
@@ -81,4 +81,4 @@ export function withRoute(handler, options = {}) {
|
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
84
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtd3JhcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvcm91dGUtd3JhcHBlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFDTCxRQUFRLEVBQ1IsWUFBWSxFQUNaLGNBQWMsRUFDZCxTQUFTLEVBQ1QsY0FBYyxFQUNkLGlCQUFpQixFQUNqQixZQUFZLEdBQ2IsTUFBTSw0QkFBNEIsQ0FBQztBQUVwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFHOUMsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBRTdDOzs7OztHQUtHO0FBQ0gsU0FBUyxlQUFlLENBQUMsS0FBYztJQUNyQyxJQUFJLEtBQUssWUFBWSxLQUFLLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztRQUN4RSxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksSUFBSSxtQkFBbUIseUJBQXlCLENBQUM7SUFDdkUsQ0FBQztJQUNELE9BQU8sWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzdCLENBQUM7QUE4QkQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBTSxVQUFVLFNBQVMsQ0FDdkIsT0FBNEMsRUFDNUMsT0FBTyxHQUFxQixFQUFFO0lBRTlCLE1BQU0sRUFBRSxZQUFZLEdBQUcsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDO0lBRXhDLE9BQU8sS0FBSyxFQUFFLEdBQVksRUFBRSxHQUFhLEVBQUUsRUFBRTtRQUMzQyxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDNUIsd0VBQXdFO1FBQ3hFLDZFQUE2RTtRQUM3RSxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUM7UUFDdkMsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxNQUFNLElBQUksRUFBRSxDQUFDO1FBRXpDLElBQUksWUFBWSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDM0IsT0FBTyxjQUFjLENBQUMsR0FBRyxFQUFFLDZCQUE2QixDQUFDLENBQUM7UUFDNUQsQ0FBQztRQUVELElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxDQUFDLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDbEQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixzREFBc0Q7WUFDdEQsSUFBSSxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7Z0JBQ3BCLE1BQU0sQ0FBQyxLQUFLLENBQUMseUNBQXlDLEVBQUU7b0JBQ3RELFNBQVMsRUFBRSxHQUFHLENBQUMsU0FBUztvQkFDeEIsS0FBSyxFQUFFLGVBQWUsQ0FBQyxLQUFLLENBQUM7aUJBQzlCLENBQUMsQ0FBQztnQkFDSCxPQUFPO1lBQ1QsQ0FBQztZQUVELElBQUksS0FBSyxZQUFZLFFBQVEsRUFBRSxDQUFDO2dCQUM5QixNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsVUFBVSxJQUFJLEdBQUcsSUFBSSxLQUFLLENBQUMsVUFBVSxHQUFHLEdBQUc7b0JBQzlELENBQUMsQ0FBQyxLQUFLLENBQUMsVUFBVTtvQkFDbEIsQ0FBQyxDQUFDLEdBQUcsQ0FBQztnQkFDUixPQUFPLEtBQUssQ0FBQyxPQUFPLEtBQUssU0FBUztvQkFDaEMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQztvQkFDbkQsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsTUFBTSxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDdkUsQ0FBQztZQUVELDZFQUE2RTtZQUM3RSw4RUFBOEU7WUFDOUUsK0VBQStFO1lBQy9FLDhFQUE4RTtZQUM5RSxNQUFNLEVBQUUsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDakMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBQ2pDLFNBQVMsRUFBRSxHQUFHLENBQUMsU0FBUztnQkFDeEIsS0FBSyxFQUFFLGVBQWUsQ0FBQyxLQUFLLENBQUM7Z0JBQzdCLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQzFDLENBQUMsQ0FBQztZQUNILE9BQU8saUJBQWlCLENBQUMsR0FBRyxFQUFFLHVCQUF1QixFQUFFLEVBQUUsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZGLENBQUM7SUFDSCxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7XG4gIEFwcEVycm9yLFxuICBlcnJvck1lc3NhZ2UsXG4gIGV4dHJhY3REYkVycm9yLFxuICBzZW5kRXJyb3IsXG4gIHNlbmRCYWRSZXF1ZXN0LFxuICBzZW5kSW50ZXJuYWxFcnJvcixcbiAgY3JlYXRlTG9nZ2VyLFxufSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBSZXF1ZXN0SGFuZGxlciB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgZ2V0Q29udGV4dCB9IGZyb20gJy4vZ2V0LWNvbnRleHQuanMnO1xuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0Q29udGV4dCB9IGZyb20gJy4vcmVxdWVzdC10eXBlcy5qcyc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcigncm91dGUtd3JhcHBlcicpO1xuXG4vKipcbiAqIEEgbG9nLXNhZmUgcmVuZGVyaW5nIG9mIGFuIHVua25vd24gZXJyb3IuIGRyaXp6bGUtb3JtJ3MgYERyaXp6bGVRdWVyeUVycm9yYFxuICogZW1iZWRzIHRoZSBmYWlsZWQgU1FMIHN0YXRlbWVudCBBTkQgdGhlIGJvdW5kIHBhcmFtZXRlciB2YWx1ZXMgKHJlYWwgZGF0YSkgaW5cbiAqIGl0cyBgLm1lc3NhZ2VgIChgRmFpbGVkIHF1ZXJ5OiA8c3FsPlxcbnBhcmFtczogPHZhbHVlcz5gKS4gTmV2ZXIgcHV0IHRoYXQgaW4gYVxuICogcmVzcG9uc2Ugb3IgYSBsb2cgbGluZSDigJQgY29sbGFwc2UgaXQgdG8gdGhlIGVycm9yIG5hbWUuIE90aGVyIGVycm9ycyBhcmUgc2FmZS5cbiAqL1xuZnVuY3Rpb24gc2FmZUVycm9yRm9yTG9nKGVycm9yOiB1bmtub3duKTogc3RyaW5nIHtcbiAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IgJiYgZXJyb3IubWVzc2FnZS5zdGFydHNXaXRoKCdGYWlsZWQgcXVlcnk6JykpIHtcbiAgICByZXR1cm4gYCR7ZXJyb3IubmFtZSB8fCAnRHJpenpsZVF1ZXJ5RXJyb3InfTogZGF0YWJhc2UgcXVlcnkgZmFpbGVkYDtcbiAgfVxuICByZXR1cm4gZXJyb3JNZXNzYWdlKGVycm9yKTtcbn1cblxuLyoqXG4gKiBDb250ZXh0IG9iamVjdCBwYXNzZWQgdG8gZXZlcnkgcm91dGUgaGFuZGxlciB3cmFwcGVkIHdpdGggYHdpdGhSb3V0ZSgpYC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSb3V0ZUNvbnRleHQge1xuICAvKiogT3JpZ2luYWwgRXhwcmVzcyByZXF1ZXN0ICovXG4gIHJlcTogUmVxdWVzdDtcbiAgLyoqIE9yaWdpbmFsIEV4cHJlc3MgcmVzcG9uc2UgKi9cbiAgcmVzOiBSZXNwb25zZTtcbiAgLyoqIFJlcXVlc3QgY29udGV4dCB3aXRoIGlkZW50aXR5IGFuZCBsb2dnaW5nICovXG4gIGN0eDogUmVxdWVzdENvbnRleHQ7XG4gIC8qKiBMb3dlcmNhc2VkIG9yZ2FuaXphdGlvbiBJRCAoZ3VhcmFudGVlZCBub24tZW1wdHkgd2hlbiByZXF1aXJlT3JnSWQgaXMgdHJ1ZSkgKi9cbiAgb3JnSWQ6IHN0cmluZztcbiAgLyoqIFVzZXIgSUQgZnJvbSBKV1QgKGRlZmF1bHRzIHRvIGVtcHR5IHN0cmluZykgKi9cbiAgdXNlcklkOiBzdHJpbmc7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgYHdpdGhSb3V0ZSgpYC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXaXRoUm91dGVPcHRpb25zIHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gcmVxdWlyZSBvcmdJZCBvbiB0aGUgcmVxdWVzdC5cbiAgICogV2hlbiB0cnVlIChkZWZhdWx0KSwgcmV0dXJucyA0MDAgaWYgb3JnSWQgaXMgbWlzc2luZy5cbiAgICogU2V0IHRvIGZhbHNlIGZvciByb3V0ZXMgdGhhdCBkb24ndCBuZWVkIG9yZyBjb250ZXh0LlxuICAgKi9cbiAgcmVxdWlyZU9yZ0lkPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBXcmFwIGFuIGFzeW5jIHJvdXRlIGhhbmRsZXIgd2l0aCBzdGFuZGFyZCBib2lsZXJwbGF0ZS5cbiAqXG4gKiBFeHRyYWN0cyBjb250ZXh0LCBvcmdJZCwgdXNlcklkIGZyb20gdGhlIHJlcXVlc3QsIHZhbGlkYXRlcyBvcmdJZCxcbiAqIGFuZCBjYXRjaGVzIGVycm9ycy4gVHlwZWQgYEFwcEVycm9yYCBzdWJjbGFzc2VzIGFyZSBhdXRvbWF0aWNhbGx5XG4gKiBtYXBwZWQgdG8gdGhlIGNvcnJlY3QgSFRUUCByZXNwb25zZS5cbiAqXG4gKiBAcGFyYW0gaGFuZGxlciAtIEFzeW5jIGZ1bmN0aW9uIHJlY2VpdmluZyBhIFJvdXRlQ29udGV4dFxuICogQHBhcmFtIG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIEV4cHJlc3MgUmVxdWVzdEhhbmRsZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogcm91dGVyLmdldCgnLzppZCcsIHdpdGhSb3V0ZShhc3luYyAoeyByZXEsIHJlcywgY3R4LCBvcmdJZCB9KSA9PiB7XG4gKiAgIGNvbnN0IGlkID0gZ2V0UGFyYW0ocmVxLnBhcmFtcywgJ2lkJyk7XG4gKiAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBpcGVsaW5lU2VydmljZS5maW5kQnlJZChpZCwgb3JnSWQpO1xuICogICBpZiAoIXJlc3VsdCkgdGhyb3cgbmV3IE5vdEZvdW5kRXJyb3IoJ1BpcGVsaW5lIG5vdCBmb3VuZCcpO1xuICogICByZXR1cm4gc2VuZFN1Y2Nlc3MocmVzLCAyMDAsIHsgcGlwZWxpbmU6IHJlc3VsdCB9KTtcbiAqIH0pKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJvdXRlKFxuICBoYW5kbGVyOiAocmM6IFJvdXRlQ29udGV4dCkgPT4gUHJvbWlzZTx2b2lkPixcbiAgb3B0aW9uczogV2l0aFJvdXRlT3B0aW9ucyA9IHt9LFxuKTogUmVxdWVzdEhhbmRsZXIge1xuICBjb25zdCB7IHJlcXVpcmVPcmdJZCA9IHRydWUgfSA9IG9wdGlvbnM7XG5cbiAgcmV0dXJuIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgICBjb25zdCBjdHggPSBnZXRDb250ZXh0KHJlcSk7XG4gICAgLy8gYGlkZW50aXR5Lm9yZ0lkYCBpcyBhbHJlYWR5IG5vcm1hbGl6ZWQgKHRyaW1tZWQgKyBsb3dlcmNhc2VkKSBvbmNlIGF0XG4gICAgLy8gcmVzb2x1dGlvbiBpbiBhcGktY29yZSdzIGBnZXRJZGVudGl0eWAsIHNvIGl0IG1hdGNoZXMgdGhlIFJMUyBHVUMgZXhhY3RseS5cbiAgICBjb25zdCBvcmdJZCA9IGN0eC5pZGVudGl0eS5vcmdJZCB8fCAnJztcbiAgICBjb25zdCB1c2VySWQgPSBjdHguaWRlbnRpdHkudXNlcklkIHx8ICcnO1xuXG4gICAgaWYgKHJlcXVpcmVPcmdJZCAmJiAhb3JnSWQpIHtcbiAgICAgIHJldHVybiBzZW5kQmFkUmVxdWVzdChyZXMsICdPcmdhbml6YXRpb24gSUQgaXMgcmVxdWlyZWQnKTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgaGFuZGxlcih7IHJlcSwgcmVzLCBjdHgsIG9yZ0lkLCB1c2VySWQgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIERvbid0IHNlbmQgYW5vdGhlciByZXNwb25zZSBpZiBvbmUgd2FzIGFscmVhZHkgc2VudFxuICAgICAgaWYgKHJlcy5oZWFkZXJzU2VudCkge1xuICAgICAgICBsb2dnZXIuZXJyb3IoJ1JvdXRlIGhhbmRsZXIgZXJyb3IgYWZ0ZXIgcmVzcG9uc2Ugc2VudCcsIHtcbiAgICAgICAgICByZXF1ZXN0SWQ6IGN0eC5yZXF1ZXN0SWQsXG4gICAgICAgICAgZXJyb3I6IHNhZmVFcnJvckZvckxvZyhlcnJvciksXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEFwcEVycm9yKSB7XG4gICAgICAgIGNvbnN0IHN0YXR1cyA9IGVycm9yLnN0YXR1c0NvZGUgPj0gNDAwICYmIGVycm9yLnN0YXR1c0NvZGUgPCA2MDBcbiAgICAgICAgICA/IGVycm9yLnN0YXR1c0NvZGVcbiAgICAgICAgICA6IDUwMDtcbiAgICAgICAgcmV0dXJuIGVycm9yLmRldGFpbHMgPT09IHVuZGVmaW5lZFxuICAgICAgICAgID8gc2VuZEVycm9yKHJlcywgc3RhdHVzLCBlcnJvci5tZXNzYWdlLCBlcnJvci5jb2RlKVxuICAgICAgICAgIDogc2VuZEVycm9yKHJlcywgc3RhdHVzLCBlcnJvci5tZXNzYWdlLCBlcnJvci5jb2RlLCBlcnJvci5kZXRhaWxzKTtcbiAgICAgIH1cblxuICAgICAgLy8gVW5oYW5kbGVkIGVycm9yIOKGkiA1MDAuIE5FVkVSIGVjaG8gdGhlIHJhdyBtZXNzYWdlIHRvIHRoZSBjbGllbnQ6IERCIGRyaXZlclxuICAgICAgLy8gZXJyb3JzIGNhcnJ5IHRoZSBTUUwgKyBib3VuZCBwYXJhbXMsIGFuZCBDcnVkU2VydmljZSBkZWxpYmVyYXRlbHkgbGV0cyB0aGVtXG4gICAgICAvLyBwcm9wYWdhdGUgaGVyZS4gU2VuZCBhIGdlbmVyaWMgbWVzc2FnZSArIHJlcXVlc3RJZCAodG8gY29ycmVsYXRlIHdpdGggbG9ncyksXG4gICAgICAvLyBhbmQgbG9nIG9ubHkgc2FuaXRpemVkIERCIG1ldGFkYXRhIChjb2RlL2NvbnN0cmFpbnQvdGFibGUg4oCUIG5vIFNRTC9wYXJhbXMpLlxuICAgICAgY29uc3QgZGIgPSBleHRyYWN0RGJFcnJvcihlcnJvcik7XG4gICAgICBjdHgubG9nKCdFUlJPUicsICdSZXF1ZXN0IGZhaWxlZCcsIHtcbiAgICAgICAgcmVxdWVzdElkOiBjdHgucmVxdWVzdElkLFxuICAgICAgICBlcnJvcjogc2FmZUVycm9yRm9yTG9nKGVycm9yKSxcbiAgICAgICAgLi4uKE9iamVjdC5rZXlzKGRiKS5sZW5ndGggPyB7IGRiIH0gOiB7fSksXG4gICAgICB9KTtcbiAgICAgIHJldHVybiBzZW5kSW50ZXJuYWxFcnJvcihyZXMsICdJbnRlcm5hbCBzZXJ2ZXIgZXJyb3InLCB7IHJlcXVlc3RJZDogY3R4LnJlcXVlc3RJZCB9KTtcbiAgICB9XG4gIH07XG59XG4iXX0=
|
package/lib/api/server.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
// Copyright 2026 Pipeline Builder Contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Server } from 'http';
|
|
4
|
-
import { buildRouteTable, createLogger, errorMessage, installCrashHandlers, nextBackoffMs, resolveRedisConnection, retryForever, summarizeRouteTable } from '@pipeline-builder/api-core';
|
|
4
|
+
import { buildRouteTable, createLogger, errorMessage, installCrashHandlers, nextBackoffMs, resolveRedisConnection, retryForever, summarizeRouteTable, sleep, envInt } from '@pipeline-builder/api-core';
|
|
5
5
|
import { Config } from '@pipeline-builder/pipeline-core';
|
|
6
6
|
import { getConnection, closeConnection } from '@pipeline-builder/pipeline-data';
|
|
7
7
|
import { setReady, isReady } from './readiness.js';
|
|
8
8
|
import { shutdownTracing } from './tracing.js';
|
|
9
9
|
import { SSEManager } from '../http/sse-connection-manager.js';
|
|
10
10
|
const logger = createLogger('server');
|
|
11
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
11
|
// Dependency-supervisor cadence. Initial connect retries with capped backoff;
|
|
13
12
|
// once connected, re-checks at a steady interval so a later outage flips the
|
|
14
13
|
// service NotReady (draining traffic) and a recovery flips it back — without
|
|
15
14
|
// ever exiting / restarting the process.
|
|
16
15
|
const READY_RETRY_BASE_MS = 1000;
|
|
17
16
|
const READY_RETRY_MAX_MS = 10000;
|
|
18
|
-
const READY_MONITOR_INTERVAL_MS =
|
|
17
|
+
const READY_MONITOR_INTERVAL_MS = envInt('READINESS_MONITOR_INTERVAL_MS', 15_000, { min: 1 });
|
|
19
18
|
/**
|
|
20
19
|
* Establish and continuously monitor the service's datastore dependency,
|
|
21
20
|
* flipping readiness state instead of crash-looping.
|
|
@@ -41,6 +40,8 @@ async function superviseDependencies(name, onBeforeStart, testDatabase, aborted)
|
|
|
41
40
|
}
|
|
42
41
|
// No datastore to gate on → ready as soon as we are listening.
|
|
43
42
|
if (testDatabase === false) {
|
|
43
|
+
if (aborted())
|
|
44
|
+
return; // shutdown began while we were initializing
|
|
44
45
|
setReady(true);
|
|
45
46
|
logger.info(`${name} ready (no datastore dependency)`);
|
|
46
47
|
return;
|
|
@@ -55,6 +56,10 @@ async function superviseDependencies(name, onBeforeStart, testDatabase, aborted)
|
|
|
55
56
|
catch {
|
|
56
57
|
ok = false;
|
|
57
58
|
}
|
|
59
|
+
// A probe that was in flight when shutdown began must not flip the
|
|
60
|
+
// draining instance back to ready.
|
|
61
|
+
if (aborted())
|
|
62
|
+
break;
|
|
58
63
|
if (ok && !isReady()) {
|
|
59
64
|
setReady(true);
|
|
60
65
|
waitDelay = READY_RETRY_BASE_MS;
|
|
@@ -108,7 +113,7 @@ export async function startServer(app, options = {}) {
|
|
|
108
113
|
// Validate auth configuration at server startup (not during CDK synthesis)
|
|
109
114
|
Config.validateAuth();
|
|
110
115
|
// Fail fast if this service has no internal signing key of its own, or no
|
|
111
|
-
// public bundle to verify its peers with
|
|
116
|
+
// public bundle to verify its peers with. Without them it would mint
|
|
112
117
|
// tokens on an EPHEMERAL in-process key that no peer accepts, and reject every
|
|
113
118
|
// peer's token — i.e. silently lose all service-to-service traffic. Checked
|
|
114
119
|
// HERE rather than in `createApp` because it is a property of the running
|
|
@@ -155,9 +160,21 @@ export async function startServer(app, options = {}) {
|
|
|
155
160
|
if (shuttingDown)
|
|
156
161
|
return;
|
|
157
162
|
shuttingDown = true;
|
|
163
|
+
// Drain FIRST: report NotReady before anything else, so the load balancer /
|
|
164
|
+
// readiness probe stops sending new work while the rest of shutdown runs.
|
|
165
|
+
// (Previously readiness stayed green through onShutdown, which could take
|
|
166
|
+
// seconds — new requests kept arriving at a process tearing itself down.)
|
|
167
|
+
setReady(false);
|
|
158
168
|
if (signal) {
|
|
159
169
|
logger.info(`${signal} received, shutting down gracefully...`);
|
|
160
170
|
}
|
|
171
|
+
// Arm the force-exit BEFORE awaiting anything: a hung onShutdown (a stuck
|
|
172
|
+
// queue close, a Redis quit that never answers) would otherwise block forever
|
|
173
|
+
// with the timer never started, so the pod would ignore SIGTERM until SIGKILL.
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
logger.error('Forced shutdown after timeout');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}, shutdownTimeoutMs).unref();
|
|
161
178
|
// Custom shutdown callback
|
|
162
179
|
if (onShutdown) {
|
|
163
180
|
try {
|
|
@@ -194,11 +211,6 @@ export async function startServer(app, options = {}) {
|
|
|
194
211
|
}
|
|
195
212
|
process.exit(0);
|
|
196
213
|
});
|
|
197
|
-
// Force shutdown after timeout
|
|
198
|
-
setTimeout(() => {
|
|
199
|
-
logger.error('Forced shutdown after timeout');
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}, shutdownTimeoutMs).unref();
|
|
202
214
|
};
|
|
203
215
|
// Register signal handlers
|
|
204
216
|
process.once('SIGINT', () => void shutdown('SIGINT'));
|
|
@@ -246,4 +258,4 @@ export function runServer(app, options = {}) {
|
|
|
246
258
|
process.exit(1);
|
|
247
259
|
});
|
|
248
260
|
}
|
|
249
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwaS9zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzlCLE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQUUsc0JBQXNCLEVBQUUsWUFBWSxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDekwsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3pELE9BQU8sRUFBRSxhQUFhLEVBQUUsZUFBZSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFakYsT0FBTyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUUvRCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsUUFBUSxDQUFDLENBQUM7QUFFdEMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxFQUFVLEVBQWlCLEVBQUUsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRS9GLDhFQUE4RTtBQUM5RSw2RUFBNkU7QUFDN0UsNkVBQTZFO0FBQzdFLHlDQUF5QztBQUN6QyxNQUFNLG1CQUFtQixHQUFHLElBQUksQ0FBQztBQUNqQyxNQUFNLGtCQUFrQixHQUFHLEtBQUssQ0FBQztBQUNqQyxNQUFNLHlCQUF5QixHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLDZCQUE2QixJQUFJLE9BQU8sRUFBRSxFQUFFLENB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QUFRLEVBQUUsR0FBRyxFQUFFLENBQUMsUUFBUSxFQUFFO0tBQzNCLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQUMsR0FBWSxFQUFFLE9BQU8sR0FBdUIsRUFBRTtJQUN0RSw4RUFBOEU7SUFDOUUsOEVBQThFO0lBQzlFLHdFQUF3RTtJQUN4RSwyQkFBMkI7SUFDM0IsTUFBTSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRTVFLHlFQUF5RTtJQUN6RSx5RUFBeUU7SUFDekUsNEVBQTRFO0lBQzVFLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzdCLFdBQVcsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7UUFDeEMsNEVBQTRFO1FBQzVFLDJFQUEyRTtRQUMzRSw0RUFBNEU7UUFDNUUscUVBQXFFO1FBQ3JFLDZFQUE2RTtRQUM3RSxNQUFNLENBQUMsS0FBSyxDQUFDLHdCQUF3QixFQUFFO1lBQ3JDLEtBQUssRUFBRSxZQUFZLENBQUMsS0FBSyxDQUFDO1lBQzFCLEtBQUssRUFBRSxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTO1NBQ3hELENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IFNlcnZlciB9IGZyb20gJ2h0dHAnO1xuaW1wb3J0IHsgYnVpbGRSb3V0ZVRhYmxlLCBjcmVhdGVMb2dnZXIsIGVycm9yTWVzc2FnZSwgaW5zdGFsbENyYXNoSGFuZGxlcnMsIG5leHRCYWNrb2ZmTXMsIHJlc29sdmVSZWRpc0Nvbm5lY3Rpb24sIHJldHJ5Rm9yZXZlciwgc3VtbWFyaXplUm91dGVUYWJsZSB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IENvbmZpZyB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLWNvcmUnO1xuaW1wb3J0IHsgZ2V0Q29ubmVjdGlvbiwgY2xvc2VDb25uZWN0aW9uIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtZGF0YSc7XG5pbXBvcnQgdHlwZSB7IEV4cHJlc3MgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IHNldFJlYWR5LCBpc1JlYWR5IH0gZnJvbSAnLi9yZWFkaW5lc3MuanMnO1xuaW1wb3J0IHsgc2h1dGRvd25UcmFjaW5nIH0gZnJvbSAnLi90cmFjaW5nLmpzJztcbmltcG9ydCB7IFNTRU1hbmFnZXIgfSBmcm9tICcuLi9odHRwL3NzZS1jb25uZWN0aW9uLW1hbmFnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3NlcnZlcicpO1xuXG5jb25zdCBzbGVlcCA9IChtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiA9PiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBtcykpO1xuXG4vLyBEZXBlbmRlbmN5LXN1cGVydmlzb3IgY2FkZW5jZS4gSW5pdGlhbCBjb25uZWN0IHJldHJpZXMgd2l0aCBjYXBwZWQgYmFja29mZjtcbi8vIG9uY2UgY29ubmVjdGVkLCByZS1jaGVja3MgYXQgYSBzdGVhZHkgaW50ZXJ2YWwgc28gYSBsYXRlciBvdXRhZ2UgZmxpcHMgdGhlXG4vLyBzZXJ2aWNlIE5vdFJlYWR5IChkcmFpbmluZyB0cmFmZmljKSBhbmQgYSByZWNvdmVyeSBmbGlwcyBpdCBiYWNrIOKAlCB3aXRob3V0XG4vLyBldmVyIGV4aXRpbmcgLyByZXN0YXJ0aW5nIHRoZSBwcm9jZXNzLlxuY29uc3QgUkVBRFlfUkVUUllfQkFTRV9NUyA9IDEwMDA7XG5jb25zdCBSRUFEWV9SRVRSWV9NQVhfTVMgPSAxMDAwMDtcbmNvbnN0IFJFQURZX01PTklUT1JfSU5URVJWQUxfTVMgPSBwYXJzZUludChwcm9jZXNzLmVudi5SRUFESU5FU1NfTU9OSVRPUl9JTlRFUlZBTF9NUyB8fCAnMTUwMDAnLCAxMCk7XG5cbi8qKlxuICogRXN0YWJsaXNoIGFuZCBjb250aW51b3VzbHkgbW9uaXRvciB0aGUgc2VydmljZSdzIGRhdGFzdG9yZSBkZXBlbmRlbmN5LFxuICogZmxpcHBpbmcgcmVhZGluZXNzIHN0YXRlIGluc3RlYWQgb2YgY3Jhc2gtbG9vcGluZy5cbiAqXG4gKiAtIEVzdGFibGlzaCBwaGFzZTogcmV0cnkgYG9uQmVmb3JlU3RhcnRgIChlLmcuIGNvbm5lY3RNb25nbykgd2l0aCBjYXBwZWRcbiAqICAgYmFja29mZiB1bnRpbCBpdCBzdWNjZWVkcyDigJQgYSBjb2xkIGRhdGFzdG9yZSBtdXN0IG5ldmVyIGV4aXQgdGhlIHByb2Nlc3MuXG4gKiAtIE1vbml0b3IgcGhhc2U6IHBvbGwgYHRlc3REYXRhYmFzZWAgZm9yIHRoZSBsaWZlIG9mIHRoZSBwcm9jZXNzOyBmbGlwXG4gKiAgIHJlYWR5L05vdFJlYWR5IG9uIHRyYW5zaXRpb25zLiBXaGlsZSBOb3RSZWFkeSB0aGUgcmVhZGluZXNzIGd1YXJkIDUwM3NcbiAqICAgYnVzaW5lc3MgdHJhZmZpYyBhbmQgb3JjaGVzdHJhdG9yIHJlYWRpbmVzcyBwcm9iZXMgZHJhaW4gdGhlIGluc3RhbmNlLlxuICpcbiAqIFJ1bnMgaW4gdGhlIGJhY2tncm91bmQgKHRoZSBIVFRQIHNlcnZlciBpcyBhbHJlYWR5IGxpc3RlbmluZykuIFN0b3BzIHdoZW5cbiAqIGBhYm9ydGVkKClgIHJldHVybnMgdHJ1ZSAoZ3JhY2VmdWwgc2h1dGRvd24pLCBzbyBpdCBuZXZlciBrZWVwcyBhIGNsb3NpbmdcbiAqIHByb2Nlc3MgYWxpdmUuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHN1cGVydmlzZURlcGVuZGVuY2llcyhcbiAgbmFtZTogc3RyaW5nLFxuICBvbkJlZm9yZVN0YXJ0OiAoKCkgPT4gUHJvbWlzZTx2b2lkPikgfCB1bmRlZmluZWQsXG4gIHRlc3REYXRhYmFzZTogKCgpID0+IFByb21pc2U8Ym9vbGVhbj4pIHwgZmFsc2UgfCB1bmRlZmluZWQsXG4gIGFib3J0ZWQ6ICgpID0+IGJvb2xlYW4sXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKG9uQmVmb3JlU3RhcnQpIHtcbiAgICBhd2FpdCByZXRyeUZvcmV2ZXIob25CZWZvcmVTdGFydCwge1xuICAgICAgYmFzZU1zOiBSRUFEWV9SRVRSWV9CQVNFX01TLFxuICAgICAgbWF4TXM6IFJFQURZX1JFVFJZX01BWF9NUyxcbiAgICAgIG9uQXR0ZW1wdEZhaWxlZDogKGVycm9yLCBkZWxheU1zKSA9PiBsb2dnZXIud2FybihcbiAgICAgICAgYCR7bmFtZX06IGRlcGVuZGVuY3kgaW5pdCBmYWlsZWQsIHJldHJ5aW5nIGluICR7ZGVsYXlNc31tc2AsXG4gICAgICAgIHsgZXJyb3I6IGVycm9yTWVzc2FnZShlcnJvcikgfSxcbiAgICAgICksXG4gICAgICBzaG91bGRDb250aW51ZTogKCkgPT4gIWFib3J0ZWQoKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8vIE5vIGRhdGFzdG9yZSB0byBnYXRlIG9uIOKGkiByZWFkeSBhcyBzb29uIGFzIHdlIGFyZSBsaXN0ZW5pbmcuXG4gIGlmICh0ZXN0RGF0YWJhc2UgPT09IGZhbHNlKSB7XG4gICAgc2V0UmVhZHkodHJ1ZSk7XG4gICAgbG9nZ2VyLmluZm8oYCR7bmFtZX0gcmVhZHkgKG5vIGRhdGFzdG9yZSBkZXBlbmRlbmN5KWApO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHByb2JlID0gdGVzdERhdGFiYXNlID8/ICgoKSA9PiBnZXRDb25uZWN0aW9uKCkudGVzdENvbm5lY3Rpb24oKSk7XG4gIGxldCB3YWl0RGVsYXkgPSBSRUFEWV9SRVRSWV9CQVNFX01TO1xuICB3aGlsZSAoIWFib3J0ZWQoKSkge1xuICAgIGxldCBvayA9IGZhbHNlO1xuICAgIHRyeSB7XG4gICAgICBvayA9IGF3YWl0IHByb2JlKCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICBvayA9IGZhbHNlO1xuICAgIH1cblxuICAgIGlmIChvayAmJiAhaXNSZWFkeSgpKSB7XG4gICAgICBzZXRSZWFkeSh0cnVlKTtcbiAgICAgIHdhaXREZWxheSA9IFJFQURZX1JFVFJZX0JBU0VfTVM7XG4gICAgICBsb2dnZXIuaW5mbyhgJHtuYW1lfSByZWFkeSDigJQgZGVwZW5kZW5jaWVzIGNvbm5lY3RlZGApO1xuICAgIH0gZWxzZSBpZiAoIW9rICYmIGlzUmVhZHkoKSkge1xuICAgICAgc2V0UmVhZHkoZmFsc2UpO1xuICAgICAgbG9nZ2VyLndhcm4oYCR7bmFtZX0gZGVncmFkZWQg4oCUIGRlcGVuZGVuY3kgY2hlY2sgZmFpbGluZyAobm93IE5vdFJlYWR5KWApO1xuICAgIH1cblxuICAgIC8vIFN0ZWFkeSBjYWRlbmNlIG9uY2UgcmVhZHk7IGZhc3RlciBiYWNrb2ZmIHdoaWxlIHdhaXRpbmcgc28gd2UgZmxpcCB0b1xuICAgIC8vIHJlYWR5IHByb21wdGx5IHdoZW4gdGhlIGRhdGFzdG9yZSBjb21lcyB1cC5cbiAgICBpZiAoaXNSZWFkeSgpKSB7XG4gICAgICBhd2FpdCBzbGVlcChSRUFEWV9NT05JVE9SX0lOVEVSVkFMX01TKTtcbiAgICB9IGVsc2Uge1xuICAgICAgYXdhaXQgc2xlZXAod2FpdERlbGF5KTtcbiAgICAgIHdhaXREZWxheSA9IG5leHRCYWNrb2ZmTXMod2FpdERlbGF5LCBSRUFEWV9SRVRSWV9NQVhfTVMpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHN0YXJ0aW5nIGEgc2VydmVyXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RhcnRTZXJ2ZXJPcHRpb25zIHtcbiAgLyoqIFNlcnZpY2UgbmFtZSBmb3IgbG9nZ2luZyAqL1xuICBuYW1lPzogc3RyaW5nO1xuICAvKiogUG9ydCB0byBsaXN0ZW4gb24gKGRlZmF1bHQ6IGZyb20gY29uZmlnKSAqL1xuICBwb3J0PzogbnVtYmVyO1xuICAvKiogU1NFIG1hbmFnZXIgZm9yIGdyYWNlZnVsIHNodXRkb3duICovXG4gIHNzZU1hbmFnZXI/OiBTU0VNYW5hZ2VyO1xuICAvKiogU2h1dGRvd24gdGltZW91dCBpbiBtaWxsaXNlY29uZHMgKGRlZmF1bHQ6IDE1MDAwKSAqL1xuICBzaHV0ZG93blRpbWVvdXRNcz86IG51bWJlcjtcbiAgLyoqIENhbGxiYWNrIGFmdGVyIHNlcnZlciBzdGFydHMgKi9cbiAgb25TdGFydD86IChwb3J0OiBudW1iZXIpID0+IHZvaWQ7XG4gIC8qKiBDYWxsYmFjayBiZWZvcmUgc2h1dGRvd24gKi9cbiAgb25TaHV0ZG93bj86ICgpID0+IFByb21pc2U8dm9pZD47XG4gIC8qKiBSdW5zIGJlZm9yZSBkYXRhYmFzZSBjaGVjayAoZS5nLiwgaW5pdGlhbGl6ZSBleHRlcm5hbCBjb25uZWN0aW9ucykgKi9cbiAgb25CZWZvcmVTdGFydD86ICgpID0+IFByb21pc2U8dm9pZD47XG4gIC8qKiBDdXN0b20gZGF0YWJhc2UgaGVhbHRoIGNoZWNrLCBvciBmYWxzZSB0byBza2lwLiBEZWZhdWx0OiBQb3N0Z3JlU1FMIHRlc3RDb25uZWN0aW9uICovXG4gIHRlc3REYXRhYmFzZT86ICgoKSA9PiBQcm9taXNlPGJvb2xlYW4+KSB8IGZhbHNlO1xuICAvKiogQ3VzdG9tIGRhdGFiYXNlIGNsb3NlLCBvciBmYWxzZSB0byBza2lwLiBEZWZhdWx0OiBQb3N0Z3JlU1FMIGNsb3NlQ29ubmVjdGlvbiAqL1xuICBjbG9zZURhdGFiYXNlPzogKCgpID0+IFByb21pc2U8dm9pZD4pIHwgZmFsc2U7XG59XG5cbi8qKlxuICogUmVzdWx0IG9mIHN0YXJ0aW5nIGEgc2VydmVyXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RhcnRTZXJ2ZXJSZXN1bHQge1xuICAvKiogSFRUUCBzZXJ2ZXIgaW5zdGFuY2UgKi9cbiAgc2VydmVyOiBTZXJ2ZXI7XG4gIC8qKiBQb3J0IHRoZSBzZXJ2ZXIgaXMgbGlzdGVuaW5nIG9uICovXG4gIHBvcnQ6IG51bWJlcjtcbiAgLyoqIEZ1bmN0aW9uIHRvIG1hbnVhbGx5IHRyaWdnZXIgc2h1dGRvd24gKi9cbiAgc2h1dGRvd246ICgpID0+IFByb21pc2U8dm9pZD47XG59XG5cbi8qKlxuICogU3RhcnQgYW4gRXhwcmVzcyBzZXJ2ZXIgd2l0aCBncmFjZWZ1bCBzaHV0ZG93biBoYW5kbGluZ1xuICpcbiAqIEZlYXR1cmVzOlxuICogLSBBdXRvbWF0aWMgZGF0YWJhc2UgY29ubmVjdGlvbiB0ZXN0aW5nXG4gKiAtIEdyYWNlZnVsIHNodXRkb3duIG9uIFNJR0lOVC9TSUdURVJNXG4gKiAtIFNTRSBjb25uZWN0aW9uIGNsZWFudXBcbiAqIC0gQ29uZmlndXJhYmxlIHNodXRkb3duIHRpbWVvdXRcbiAqXG4gKiBAcGFyYW0gYXBwIC0gRXhwcmVzcyBhcHBsaWNhdGlvblxuICogQHBhcmFtIG9wdGlvbnMgLSBTZXJ2ZXIgb3B0aW9uc1xuICogQHJldHVybnMgU2VydmVyIGluc3RhbmNlIGFuZCBjb250cm9sIGZ1bmN0aW9uc1xuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBjb25zdCB7IGFwcCwgc3NlTWFuYWdlciB9ID0gY3JlYXRlQXBwKCk7XG4gKlxuICogYXBwLnBvc3QoJy9hcGkvcmVzb3VyY2UnLCByZXF1aXJlQXV0aCwgaGFuZGxlcik7XG4gKlxuICogYXdhaXQgc3RhcnRTZXJ2ZXIoYXBwLCB7XG4gKiAgIG5hbWU6ICdNeSBNaWNyb3NlcnZpY2UnLFxuICogICBzc2VNYW5hZ2VyLFxuICogICBvblN0YXJ0OiAocG9ydCkgPT4gY29uc29sZS5sb2coYExpc3RlbmluZyBvbiAke3BvcnR9YCksXG4gKiB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRTZXJ2ZXIoXG4gIGFwcDogRXhwcmVzcyxcbiAgb3B0aW9uczogU3RhcnRTZXJ2ZXJPcHRpb25zID0ge30sXG4pOiBQcm9taXNlPFN0YXJ0U2VydmVyUmVzdWx0PiB7XG4gIGNvbnN0IHNlcnZlckNvbmZpZyA9IENvbmZpZy5nZXQoJ3NlcnZlcicpO1xuICBjb25zdCB7XG4gICAgbmFtZSA9ICdNaWNyb3NlcnZpY2UnLFxuICAgIHNzZU1hbmFnZXIsXG4gICAgc2h1dGRvd25UaW1lb3V0TXMgPSAxNTAwMCxcbiAgICBvblN0YXJ0LFxuICAgIG9uU2h1dGRvd24sXG4gICAgb25CZWZvcmVTdGFydCxcbiAgICB0ZXN0RGF0YWJhc2UsXG4gICAgY2xvc2VEYXRhYmFzZSxcbiAgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IHBvcnQgPSBvcHRpb25zLnBvcnQgPz8gc2VydmVyQ29uZmlnLnBvcnQ7XG5cbiAgLy8gVmFsaWRhdGUgYXV0aCBjb25maWd1cmF0aW9uIGF0IHNlcnZlciBzdGFydHVwIChub3QgZHVyaW5nIENESyBzeW50aGVzaXMpXG4gIENvbmZpZy52YWxpZGF0ZUF1dGgoKTtcbiAgLy8gRmFpbCBmYXN0IGlmIHRoaXMgc2VydmljZSBoYXMgbm8gaW50ZXJuYWwgc2lnbmluZyBrZXkgb2YgaXRzIG93biwgb3Igbm9cbiAgLy8gcHVibGljIGJ1bmRsZSB0byB2ZXJpZnkgaXRzIHBlZXJzIHdpdGggKCMxNCkuIFdpdGhvdXQgdGhlbSBpdCB3b3VsZCBtaW50XG4gIC8vIHRva2VucyBvbiBhbiBFUEhFTUVSQUwgaW4tcHJvY2VzcyBrZXkgdGhhdCBubyBwZWVyIGFjY2VwdHMsIGFuZCByZWplY3QgZXZlcnlcbiAgLy8gcGVlcidzIHRva2VuIOKAlCBpLmUuIHNpbGVudGx5IGxvc2UgYWxsIHNlcnZpY2UtdG8tc2VydmljZSB0cmFmZmljLiBDaGVja2VkXG4gIC8vIEhFUkUgcmF0aGVyIHRoYW4gaW4gYGNyZWF0ZUFwcGAgYmVjYXVzZSBpdCBpcyBhIHByb3BlcnR5IG9mIHRoZSBydW5uaW5nXG4gIC8vIGRlcGxveW1lbnQsIG5vdCBvZiB0aGUgYXBwIG9iamVjdDogcm91dGUtdGFibGUgYnVpbGRlcnMgYW5kIHRlc3Qgc3VpdGVzXG4gIC8vIGFzc2VtYmxlIGFuIGFwcCB3aXRob3V0IGV2ZXIgc2lnbmluZyBhbnl0aGluZy5cbiAgZm9yIChjb25zdCBlbnZWYXIgb2YgWydTRVJWSUNFX1NJR05JTkdfS0VZX0ZJTEUnLCAnU0VSVklDRV9LRVlfQlVORExFX0ZJTEUnXSBhcyBjb25zdCkge1xuICAgIGlmICghcHJvY2Vzcy5lbnZbZW52VmFyXSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke2VudlZhcn0gZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgcmVxdWlyZWQgKGdlbmVyYXRlIHdpdGggZGVwbG95L2Jpbi9zZXJ2aWNlLXNpZ25pbmcta2V5cy5zaCkuIFNldCBpdCBiZWZvcmUgc3RhcnRpbmcgdGhlIHNlcnZlci5gKTtcbiAgICB9XG4gIH1cbiAgLy8gRmFpbCBmYXN0IG9uIGFuIHVudXNhYmxlIFJlZGlzIGNvbmZpZ3VyYXRpb24gKHRocm93cyBSZWRpc0NvbmZpZ0Vycm9yKSDigJQgYVxuICAvLyBSZWRpcyBjbGllbnQgaXMgY3JlYXRlZCBsYXppbHkgb24gZmlyc3QgdXNlLCBzbyB3aXRob3V0IHRoaXMgYSBiYWQgY29uZmlnXG4gIC8vIHdvdWxkIHN1cmZhY2UgYXMgcmVxdWVzdC10aW1lIGVycm9ycyBpbnN0ZWFkIG9mIGEgZmFpbGVkIHN0YXJ0LlxuICByZXNvbHZlUmVkaXNDb25uZWN0aW9uKCk7XG5cbiAgbG9nZ2VyLmluZm8oYFN0YXJ0aW5nICR7bmFtZX0uLi5gKTtcblxuICAvLyBNYXJrIE5vdFJlYWR5IHN5bmNocm9ub3VzbHksIEJFRk9SRSB0aGUgcG9ydCBvcGVucywgc28gbm8gcmVxdWVzdCBzbGlwc1xuICAvLyB0aHJvdWdoIHRoZSByZWFkaW5lc3MgZ3VhcmQgYmVmb3JlIHRoZSBzdXBlcnZpc29yIGhhcyBydW4uIFRoZSBzdXBlcnZpc29yXG4gIC8vIGZsaXBzIGl0IHRydWUgb25jZSBkZXBlbmRlbmNpZXMgY29ubmVjdC5cbiAgc2V0UmVhZHkoZmFsc2UpO1xuXG4gIC8vIExpc3RlbiBGSVJTVC4gUHJldmlvdXNseSB0aGUgcHJvY2VzcyB0ZXN0ZWQgdGhlIERCIGFuZCBgcHJvY2Vzcy5leGl0KDEpYCdkXG4gIC8vIG9uIGZhaWx1cmUgKHRvIGJlIHJlc3RhcnRlZCksIHNvIGR1cmluZyBhIGNvbGQtREIgc3RhbXBlZGUgc2VydmljZXNcbiAgLy8gY3Jhc2gtbG9vcGVkIGFuZCBuZXZlciBldmVuIG9wZW5lZCB0aGVpciBwb3J0LiBOb3cgd2Ugb3BlbiB0aGUgcG9ydFxuICAvLyBpbW1lZGlhdGVseSDigJQgL2hlYWx0aCAobGl2ZW5lc3MpLCAvcmVhZHkgKHJlYWRpbmVzcykgYW5kIHRoZSByZWFkaW5lc3NcbiAgLy8gZ3VhcmQgYWxsIHJlc3BvbmQgYXQgb25jZSDigJQgYW5kIGVzdGFibGlzaCArIG1vbml0b3IgdGhlIGRhdGFzdG9yZSBpbiB0aGVcbiAgLy8gYmFja2dyb3VuZCwgcmVwb3J0aW5nIE5vdFJlYWR5IHVudGlsIGl0IGNvbm5lY3RzIGluc3RlYWQgb2YgZXhpdGluZy5cbiAgY29uc3Qgc2VydmVyID0gYXBwLmxpc3Rlbihwb3J0LCAoKSA9PiB7XG4gICAgbG9nZ2VyLmluZm8oYCR7bmFtZX0gbGlzdGVuaW5nIG9uIHBvcnQ6ICR7cG9ydH1gKTtcbiAgICBsb2dnZXIuaW5mbyhgUGxhdGZvcm0gVVJMOiAke3NlcnZlckNvbmZpZy5wbGF0Zm9ybVVybH1gKTtcbiAgICBvblN0YXJ0Py4ocG9ydCk7XG4gIH0pO1xuXG4gIC8vIFNodXRkb3duIGhhbmRsZXIgKGd1YXJkZWQgYWdhaW5zdCBjb25jdXJyZW50IHNpZ25hbHMpXG4gIGxldCBzaHV0dGluZ0Rvd24gPSBmYWxzZTtcblxuICAvLyBCYWNrZ3JvdW5kIGRlcGVuZGVuY3kgc3VwZXJ2aXNvciDigJQgZXN0YWJsaXNoZWQgKyBtb25pdG9yZWQgYWZ0ZXIgbGlzdGVuLFxuICAvLyBzdG9wcGVkIG9uIHNodXRkb3duIHNvIGl0IG5ldmVyIGtlZXBzIGEgY2xvc2luZyBwcm9jZXNzIGFsaXZlLiBHdWFyZCB0aGVcbiAgLy8gZmxvYXRpbmcgcHJvbWlzZTogZXZlcnkgZXhwZWN0ZWQgZmFpbHVyZSBpcyBoYW5kbGVkIGluc2lkZSwgc28gYSByZWplY3Rpb25cbiAgLy8gaGVyZSBtZWFucyBhIHByb2dyYW1taW5nIGVycm9yIOKAlCBsb2cgaXQgYW5kIGxlYXZlIHRoZSBzZXJ2aWNlIE5vdFJlYWR5XG4gIC8vIHJhdGhlciB0aGFuIGxldHRpbmcgaXQgYmVjb21lIGFuIHVuaGFuZGxlZFJlamVjdGlvbiB0aGF0IGNyYXNoLWV4aXRzIHRoZVxuICAvLyBwcm9jZXNzICh3aGljaCB3b3VsZCBkZWZlYXQgdGhlIHdob2xlIFwibmV2ZXIgZXhpdCBvbiBkZXBlbmRlbmN5IHRyb3VibGVcIlxuICAvLyBnb2FsKS5cbiAgc3VwZXJ2aXNlRGVwZW5kZW5jaWVzKG5hbWUsIG9uQmVmb3JlU3RhcnQsIHRlc3REYXRhYmFzZSwgKCkgPT4gc2h1dHRpbmdEb3duKS5jYXRjaCgoZXJyb3IpID0+IHtcbiAgICBsb2dnZXIuZXJyb3IoJ0RlcGVuZGVuY3kgc3VwZXJ2aXNvciBjcmFzaGVkOyBzZXJ2aWNlIHdpbGwgcmVtYWluIE5vdFJlYWR5JywgeyBlcnJvcjogZXJyb3JNZXNzYWdlKGVycm9yKSB9KTtcbiAgfSk7XG5cbiAgY29uc3Qgc2h1dGRvd24gPSBhc3luYyAoc2lnbmFsPzogc3RyaW5nKSA9PiB7XG4gICAgaWYgKHNodXR0aW5nRG93bikgcmV0dXJuO1xuICAgIHNodXR0aW5nRG93biA9IHRydWU7XG5cbiAgICBpZiAoc2lnbmFsKSB7XG4gICAgICBsb2dnZXIuaW5mbyhgJHtzaWduYWx9IHJlY2VpdmVkLCBzaHV0dGluZyBkb3duIGdyYWNlZnVsbHkuLi5gKTtcbiAgICB9XG5cbiAgICAvLyBDdXN0b20gc2h1dGRvd24gY2FsbGJhY2tcbiAgICBpZiAob25TaHV0ZG93bikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgb25TaHV0ZG93bigpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdFcnJvciBpbiBvblNodXRkb3duIGNhbGxiYWNrJywgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBDbG9zZSBTU0UgY29ubmVjdGlvbnNcbiAgICBpZiAoc3NlTWFuYWdlcikge1xuICAgICAgc3NlTWFuYWdlci5zaHV0ZG93bigpO1xuICAgICAgbG9nZ2VyLmluZm8oJ1NTRSBjb25uZWN0aW9ucyBjbG9zZWQnKTtcbiAgICB9XG5cbiAgICAvLyBDbG9zZSBIVFRQIHNlcnZlclxuICAgIHNlcnZlci5jbG9zZShhc3luYyAoKSA9PiB7XG4gICAgICBsb2dnZXIuaW5mbygnSFRUUCBzZXJ2ZXIgY2xvc2VkJyk7XG5cbiAgICAgIC8vIFNodXRkb3duIE9wZW5UZWxlbWV0cnkgdHJhY2luZ1xuICAgICAgYXdhaXQgc2h1dGRvd25UcmFjaW5nKCkuY2F0Y2goZXJyID0+IGxvZ2dlci5lcnJvcignRXJyb3Igc2h1dHRpbmcgZG93biB0cmFjaW5nJywgeyBlcnJvcjogZXJyIH0pKTtcblxuICAgICAgLy8gQ2xvc2UgZGF0YWJhc2UgY29ubmVjdGlvblxuICAgICAgaWYgKGNsb3NlRGF0YWJhc2UgIT09IGZhbHNlKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgaWYgKGNsb3NlRGF0YWJhc2UpIHtcbiAgICAgICAgICAgIGF3YWl0IGNsb3NlRGF0YWJhc2UoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYXdhaXQgY2xvc2VDb25uZWN0aW9uKCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxvZ2dlci5pbmZvKCdEYXRhYmFzZSBjb25uZWN0aW9uIGNsb3NlZCcpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgY2xvc2luZyBkYXRhYmFzZScsIHsgZXJyb3IgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcHJvY2Vzcy5leGl0KDApO1xuICAgIH0pO1xuXG4gICAgLy8gRm9yY2Ugc2h1dGRvd24gYWZ0ZXIgdGltZW91dFxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGb3JjZWQgc2h1dGRvd24gYWZ0ZXIgdGltZW91dCcpO1xuICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgIH0sIHNodXRkb3duVGltZW91dE1zKS51bnJlZigpO1xuICB9O1xuXG4gIC8vIFJlZ2lzdGVyIHNpZ25hbCBoYW5kbGVyc1xuICBwcm9jZXNzLm9uY2UoJ1NJR0lOVCcsICgpID0+IHZvaWQgc2h1dGRvd24oJ1NJR0lOVCcpKTtcbiAgcHJvY2Vzcy5vbmNlKCdTSUdURVJNJywgKCkgPT4gdm9pZCBzaHV0ZG93bignU0lHVEVSTScpKTtcblxuICByZXR1cm4ge1xuICAgIHNlcnZlcixcbiAgICBwb3J0LFxuICAgIHNodXRkb3duOiAoKSA9PiBzaHV0ZG93bigpLFxuICB9O1xufVxuXG4vKipcbiAqIFNpbXBsZSBzZXJ2ZXIgc3RhcnQgd3JhcHBlciB3aXRoIGVycm9yIGhhbmRsaW5nXG4gKlxuICogQHBhcmFtIGFwcCAtIEV4cHJlc3MgYXBwbGljYXRpb25cbiAqIEBwYXJhbSBvcHRpb25zIC0gU2VydmVyIG9wdGlvbnNcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgeyBhcHAsIHNzZU1hbmFnZXIgfSA9IGNyZWF0ZUFwcCgpO1xuICogYXBwLnBvc3QoJy9hcGkvcmVzb3VyY2UnLCBoYW5kbGVyKTtcbiAqXG4gKiB2b2lkIHJ1blNlcnZlcihhcHAsIHsgbmFtZTogJ015IFNlcnZpY2UnLCBzc2VNYW5hZ2VyIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBydW5TZXJ2ZXIoYXBwOiBFeHByZXNzLCBvcHRpb25zOiBTdGFydFNlcnZlck9wdGlvbnMgPSB7fSk6IHZvaWQge1xuICAvLyBSb3V0ZSB0YWJsZSBvZiB0aGUgZnVsbHkgYXNzZW1ibGVkIGFwcCAoc2VlIGFwaS1jb3JlJ3Mgcm91dGUtdGFibGUudHMpOiB0aGVcbiAgLy8gc2FtZSBpbnRyb3NwZWN0aW9uIHRoZSBwZXItc2VydmljZSBjb3ZlcmFnZSB0ZXN0IGFzc2VydHMgb24sIGxvZ2dlZCBvbmNlIGF0XG4gIC8vIGJvb3Qgc28gYW4gdW5nYXRlZCBvciB1bmF1ZGl0ZWQgd3JpdGUgcm91dGUgaXMgdmlzaWJsZSBpbiB0aGUgcnVubmluZ1xuICAvLyBzZXJ2aWNlLCBub3Qgb25seSBpbiBDSS5cbiAgbG9nZ2VyLmluZm8oJ1JvdXRlIHRhYmxlIGJ1aWx0Jywgc3VtbWFyaXplUm91dGVUYWJsZShidWlsZFJvdXRlVGFibGUoYXBwKSkpO1xuXG4gIC8vIExhc3QtcmVzb3J0IGZhdWx0IGhhbmRsZXJzIGZvciB0aGUgcHJvZHVjdGlvbiBlbnRyeXBvaW50OiBhbiB1bmhhbmRsZWRcbiAgLy8gcmVqZWN0aW9uIC8gdW5jYXVnaHQgZXhjZXB0aW9uIGlzIGxvZ2dlZCAobm90IHNpbGVudGx5IGZhdGFsKSB0aGVuIHRoZVxuICAvLyBwcm9jZXNzIGV4aXRzIGZvciB0aGUgb3JjaGVzdHJhdG9yIHRvIHJlc3RhcnQuIE5vLW9wIHVuZGVyIE5PREVfRU5WPXRlc3QuXG4gIGluc3RhbGxDcmFzaEhhbmRsZXJzKGxvZ2dlcik7XG4gIHN0YXJ0U2VydmVyKGFwcCwgb3B0aW9ucykuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgLy8gc3RhcnRTZXJ2ZXIgbm93IGxpc3RlbnMtZmlyc3QgYW5kIHN1cGVydmlzZXMgdGhlIERCIGluIHRoZSBiYWNrZ3JvdW5kLCBzb1xuICAgIC8vIGl0IG9ubHkgcmVqZWN0cyBvbiBhIGdlbnVpbmVseSBmYXRhbCBTRVRVUCBlcnJvciAoaW52YWxpZCBhdXRoIGNvbmZpZywgYVxuICAgIC8vIGZhaWxlZCBvblN0YXJ0LCB0aGUgcG9ydCBhbHJlYWR5IGluIHVzZSkuIExvZyBtZXNzYWdlICsgc3RhY2sgZXhwbGljaXRseTpcbiAgICAvLyBhbiBFcnJvciBpbiB3aW5zdG9uIG1ldGFkYXRhIHNlcmlhbGl6ZXMgdG8gYHt9YCAobWVzc2FnZS9zdGFjayBhcmVcbiAgICAvLyBub24tZW51bWVyYWJsZSksIHdoaWNoIHdvdWxkIG90aGVyd2lzZSBoaWRlIHRoZSBjYXVzZSBiZWhpbmQgYHtlcnJvcjp7fX1gLlxuICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIHN0YXJ0IHNlcnZlcicsIHtcbiAgICAgIGVycm9yOiBlcnJvck1lc3NhZ2UoZXJyb3IpLFxuICAgICAgc3RhY2s6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5zdGFjayA6IHVuZGVmaW5lZCxcbiAgICB9KTtcbiAgICBwcm9jZXNzLmV4aXQoMSk7XG4gIH0pO1xufVxuIl19
|
|
261
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwaS9zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzlCLE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxhQUFhLEVBQUUsc0JBQXNCLEVBQUUsWUFBWSxFQUFFLG1CQUFtQixFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUN4TSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDekQsT0FBTyxFQUFFLGFBQWEsRUFBRSxlQUFlLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDL0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBRS9ELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUd0Qyw4RUFBOEU7QUFDOUUsNkVBQTZFO0FBQzdFLDZFQUE2RTtBQUM3RSx5Q0FBeUM7QUFDekMsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLENBQUM7QUFDakMsTUFBTSxrQkFBa0IsR0FBRyxLQUFLLENBQUM7QUFDakMsTUFBTSx5QkFBeUIsR0FBRyxNQUFNLENBQUMsK0JBQStCLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7QUFFOUY7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILEtBQUssVUFBVSxxQkFBcUIsQ0FDbEMsSUFBWSxFQUNaLGFBQWdELEVBQ2hELFlBQTBELEVBQzFELE9BQXNCO0lBRXRCLElB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Q0FBQyxTQUFTLEVBQUUsR0FBRyxFQUFFLENBQUMsS0FBSyxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztJQUV4RCxPQUFPO1FBQ0wsTUFBTTtRQUNOLElBQUk7UUFDSixRQUFRLEVBQUUsR0FBRyxFQUFFLENBQUMsUUFBUSxFQUFFO0tBQzNCLENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sVUFBVSxTQUFTLENBQUMsR0FBWSxFQUFFLE9BQU8sR0FBdUIsRUFBRTtJQUN0RSw4RUFBOEU7SUFDOUUsOEVBQThFO0lBQzlFLHdFQUF3RTtJQUN4RSwyQkFBMkI7SUFDM0IsTUFBTSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBRTVFLHlFQUF5RTtJQUN6RSx5RUFBeUU7SUFDekUsNEVBQTRFO0lBQzVFLG9CQUFvQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzdCLFdBQVcsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7UUFDeEMsNEVBQTRFO1FBQzVFLDJFQUEyRTtRQUMzRSw0RUFBNEU7UUFDNUUscUVBQXFFO1FBQ3JFLDZFQUE2RTtRQUM3RSxNQUFNLENBQUMsS0FBSyxDQUFDLHdCQUF3QixFQUFFO1lBQ3JDLEtBQUssRUFBRSxZQUFZLENBQUMsS0FBSyxDQUFDO1lBQzFCLEtBQUssRUFBRSxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTO1NBQ3hELENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IFNlcnZlciB9IGZyb20gJ2h0dHAnO1xuaW1wb3J0IHsgYnVpbGRSb3V0ZVRhYmxlLCBjcmVhdGVMb2dnZXIsIGVycm9yTWVzc2FnZSwgaW5zdGFsbENyYXNoSGFuZGxlcnMsIG5leHRCYWNrb2ZmTXMsIHJlc29sdmVSZWRpc0Nvbm5lY3Rpb24sIHJldHJ5Rm9yZXZlciwgc3VtbWFyaXplUm91dGVUYWJsZSwgc2xlZXAsIGVudkludCB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IENvbmZpZyB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL3BpcGVsaW5lLWNvcmUnO1xuaW1wb3J0IHsgZ2V0Q29ubmVjdGlvbiwgY2xvc2VDb25uZWN0aW9uIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtZGF0YSc7XG5pbXBvcnQgdHlwZSB7IEV4cHJlc3MgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IHNldFJlYWR5LCBpc1JlYWR5IH0gZnJvbSAnLi9yZWFkaW5lc3MuanMnO1xuaW1wb3J0IHsgc2h1dGRvd25UcmFjaW5nIH0gZnJvbSAnLi90cmFjaW5nLmpzJztcbmltcG9ydCB7IFNTRU1hbmFnZXIgfSBmcm9tICcuLi9odHRwL3NzZS1jb25uZWN0aW9uLW1hbmFnZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3NlcnZlcicpO1xuXG5cbi8vIERlcGVuZGVuY3ktc3VwZXJ2aXNvciBjYWRlbmNlLiBJbml0aWFsIGNvbm5lY3QgcmV0cmllcyB3aXRoIGNhcHBlZCBiYWNrb2ZmO1xuLy8gb25jZSBjb25uZWN0ZWQsIHJlLWNoZWNrcyBhdCBhIHN0ZWFkeSBpbnRlcnZhbCBzbyBhIGxhdGVyIG91dGFnZSBmbGlwcyB0aGVcbi8vIHNlcnZpY2UgTm90UmVhZHkgKGRyYWluaW5nIHRyYWZmaWMpIGFuZCBhIHJlY292ZXJ5IGZsaXBzIGl0IGJhY2sg4oCUIHdpdGhvdXRcbi8vIGV2ZXIgZXhpdGluZyAvIHJlc3RhcnRpbmcgdGhlIHByb2Nlc3MuXG5jb25zdCBSRUFEWV9SRVRSWV9CQVNFX01TID0gMTAwMDtcbmNvbnN0IFJFQURZX1JFVFJZX01BWF9NUyA9IDEwMDAwO1xuY29uc3QgUkVBRFlfTU9OSVRPUl9JTlRFUlZBTF9NUyA9IGVudkludCgnUkVBRElORVNTX01PTklUT1JfSU5URVJWQUxfTVMnLCAxNV8wMDAsIHsgbWluOiAxIH0pO1xuXG4vKipcbiAqIEVzdGFibGlzaCBhbmQgY29udGludW91c2x5IG1vbml0b3IgdGhlIHNlcnZpY2UncyBkYXRhc3RvcmUgZGVwZW5kZW5jeSxcbiAqIGZsaXBwaW5nIHJlYWRpbmVzcyBzdGF0ZSBpbnN0ZWFkIG9mIGNyYXNoLWxvb3BpbmcuXG4gKlxuICogLSBFc3RhYmxpc2ggcGhhc2U6IHJldHJ5IGBvbkJlZm9yZVN0YXJ0YCAoZS5nLiBjb25uZWN0TW9uZ28pIHdpdGggY2FwcGVkXG4gKiAgIGJhY2tvZmYgdW50aWwgaXQgc3VjY2VlZHMg4oCUIGEgY29sZCBkYXRhc3RvcmUgbXVzdCBuZXZlciBleGl0IHRoZSBwcm9jZXNzLlxuICogLSBNb25pdG9yIHBoYXNlOiBwb2xsIGB0ZXN0RGF0YWJhc2VgIGZvciB0aGUgbGlmZSBvZiB0aGUgcHJvY2VzczsgZmxpcFxuICogICByZWFkeS9Ob3RSZWFkeSBvbiB0cmFuc2l0aW9ucy4gV2hpbGUgTm90UmVhZHkgdGhlIHJlYWRpbmVzcyBndWFyZCA1MDNzXG4gKiAgIGJ1c2luZXNzIHRyYWZmaWMgYW5kIG9yY2hlc3RyYXRvciByZWFkaW5lc3MgcHJvYmVzIGRyYWluIHRoZSBpbnN0YW5jZS5cbiAqXG4gKiBSdW5zIGluIHRoZSBiYWNrZ3JvdW5kICh0aGUgSFRUUCBzZXJ2ZXIgaXMgYWxyZWFkeSBsaXN0ZW5pbmcpLiBTdG9wcyB3aGVuXG4gKiBgYWJvcnRlZCgpYCByZXR1cm5zIHRydWUgKGdyYWNlZnVsIHNodXRkb3duKSwgc28gaXQgbmV2ZXIga2VlcHMgYSBjbG9zaW5nXG4gKiBwcm9jZXNzIGFsaXZlLlxuICovXG5hc3luYyBmdW5jdGlvbiBzdXBlcnZpc2VEZXBlbmRlbmNpZXMoXG4gIG5hbWU6IHN0cmluZyxcbiAgb25CZWZvcmVTdGFydDogKCgpID0+IFByb21pc2U8dm9pZD4pIHwgdW5kZWZpbmVkLFxuICB0ZXN0RGF0YWJhc2U6ICgoKSA9PiBQcm9taXNlPGJvb2xlYW4+KSB8IGZhbHNlIHwgdW5kZWZpbmVkLFxuICBhYm9ydGVkOiAoKSA9PiBib29sZWFuLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGlmIChvbkJlZm9yZVN0YXJ0KSB7XG4gICAgYXdhaXQgcmV0cnlGb3JldmVyKG9uQmVmb3JlU3RhcnQsIHtcbiAgICAgIGJhc2VNczogUkVBRFlfUkVUUllfQkFTRV9NUyxcbiAgICAgIG1heE1zOiBSRUFEWV9SRVRSWV9NQVhfTVMsXG4gICAgICBvbkF0dGVtcHRGYWlsZWQ6IChlcnJvciwgZGVsYXlNcykgPT4gbG9nZ2VyLndhcm4oXG4gICAgICAgIGAke25hbWV9OiBkZXBlbmRlbmN5IGluaXQgZmFpbGVkLCByZXRyeWluZyBpbiAke2RlbGF5TXN9bXNgLFxuICAgICAgICB7IGVycm9yOiBlcnJvck1lc3NhZ2UoZXJyb3IpIH0sXG4gICAgICApLFxuICAgICAgc2hvdWxkQ29udGludWU6ICgpID0+ICFhYm9ydGVkKCksXG4gICAgfSk7XG4gIH1cblxuICAvLyBObyBkYXRhc3RvcmUgdG8gZ2F0ZSBvbiDihpIgcmVhZHkgYXMgc29vbiBhcyB3ZSBhcmUgbGlzdGVuaW5nLlxuICBpZiAodGVzdERhdGFiYXNlID09PSBmYWxzZSkge1xuICAgIGlmIChhYm9ydGVkKCkpIHJldHVybjsgLy8gc2h1dGRvd24gYmVnYW4gd2hpbGUgd2Ugd2VyZSBpbml0aWFsaXppbmdcbiAgICBzZXRSZWFkeSh0cnVlKTtcbiAgICBsb2dnZXIuaW5mbyhgJHtuYW1lfSByZWFkeSAobm8gZGF0YXN0b3JlIGRlcGVuZGVuY3kpYCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgcHJvYmUgPSB0ZXN0RGF0YWJhc2UgPz8gKCgpID0+IGdldENvbm5lY3Rpb24oKS50ZXN0Q29ubmVjdGlvbigpKTtcbiAgbGV0IHdhaXREZWxheSA9IFJFQURZX1JFVFJZX0JBU0VfTVM7XG4gIHdoaWxlICghYWJvcnRlZCgpKSB7XG4gICAgbGV0IG9rID0gZmFsc2U7XG4gICAgdHJ5IHtcbiAgICAgIG9rID0gYXdhaXQgcHJvYmUoKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIG9rID0gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gQSBwcm9iZSB0aGF0IHdhcyBpbiBmbGlnaHQgd2hlbiBzaHV0ZG93biBiZWdhbiBtdXN0IG5vdCBmbGlwIHRoZVxuICAgIC8vIGRyYWluaW5nIGluc3RhbmNlIGJhY2sgdG8gcmVhZHkuXG4gICAgaWYgKGFib3J0ZWQoKSkgYnJlYWs7XG4gICAgaWYgKG9rICYmICFpc1JlYWR5KCkpIHtcbiAgICAgIHNldFJlYWR5KHRydWUpO1xuICAgICAgd2FpdERlbGF5ID0gUkVBRFlfUkVUUllfQkFTRV9NUztcbiAgICAgIGxvZ2dlci5pbmZvKGAke25hbWV9IHJlYWR5IOKAlCBkZXBlbmRlbmNpZXMgY29ubmVjdGVkYCk7XG4gICAgfSBlbHNlIGlmICghb2sgJiYgaXNSZWFkeSgpKSB7XG4gICAgICBzZXRSZWFkeShmYWxzZSk7XG4gICAgICBsb2dnZXIud2FybihgJHtuYW1lfSBkZWdyYWRlZCDigJQgZGVwZW5kZW5jeSBjaGVjayBmYWlsaW5nIChub3cgTm90UmVhZHkpYCk7XG4gICAgfVxuXG4gICAgLy8gU3RlYWR5IGNhZGVuY2Ugb25jZSByZWFkeTsgZmFzdGVyIGJhY2tvZmYgd2hpbGUgd2FpdGluZyBzbyB3ZSBmbGlwIHRvXG4gICAgLy8gcmVhZHkgcHJvbXB0bHkgd2hlbiB0aGUgZGF0YXN0b3JlIGNvbWVzIHVwLlxuICAgIGlmIChpc1JlYWR5KCkpIHtcbiAgICAgIGF3YWl0IHNsZWVwKFJFQURZX01PTklUT1JfSU5URVJWQUxfTVMpO1xuICAgIH0gZWxzZSB7XG4gICAgICBhd2FpdCBzbGVlcCh3YWl0RGVsYXkpO1xuICAgICAgd2FpdERlbGF5ID0gbmV4dEJhY2tvZmZNcyh3YWl0RGVsYXksIFJFQURZX1JFVFJZX01BWF9NUyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3Igc3RhcnRpbmcgYSBzZXJ2ZXJcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGFydFNlcnZlck9wdGlvbnMge1xuICAvKiogU2VydmljZSBuYW1lIGZvciBsb2dnaW5nICovXG4gIG5hbWU/OiBzdHJpbmc7XG4gIC8qKiBQb3J0IHRvIGxpc3RlbiBvbiAoZGVmYXVsdDogZnJvbSBjb25maWcpICovXG4gIHBvcnQ/OiBudW1iZXI7XG4gIC8qKiBTU0UgbWFuYWdlciBmb3IgZ3JhY2VmdWwgc2h1dGRvd24gKi9cbiAgc3NlTWFuYWdlcj86IFNTRU1hbmFnZXI7XG4gIC8qKiBTaHV0ZG93biB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcyAoZGVmYXVsdDogMTUwMDApICovXG4gIHNodXRkb3duVGltZW91dE1zPzogbnVtYmVyO1xuICAvKiogQ2FsbGJhY2sgYWZ0ZXIgc2VydmVyIHN0YXJ0cyAqL1xuICBvblN0YXJ0PzogKHBvcnQ6IG51bWJlcikgPT4gdm9pZDtcbiAgLyoqIENhbGxiYWNrIGJlZm9yZSBzaHV0ZG93biAqL1xuICBvblNodXRkb3duPzogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgLyoqIFJ1bnMgYmVmb3JlIGRhdGFiYXNlIGNoZWNrIChlLmcuLCBpbml0aWFsaXplIGV4dGVybmFsIGNvbm5lY3Rpb25zKSAqL1xuICBvbkJlZm9yZVN0YXJ0PzogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgLyoqIEN1c3RvbSBkYXRhYmFzZSBoZWFsdGggY2hlY2ssIG9yIGZhbHNlIHRvIHNraXAuIERlZmF1bHQ6IFBvc3RncmVTUUwgdGVzdENvbm5lY3Rpb24gKi9cbiAgdGVzdERhdGFiYXNlPzogKCgpID0+IFByb21pc2U8Ym9vbGVhbj4pIHwgZmFsc2U7XG4gIC8qKiBDdXN0b20gZGF0YWJhc2UgY2xvc2UsIG9yIGZhbHNlIHRvIHNraXAuIERlZmF1bHQ6IFBvc3RncmVTUUwgY2xvc2VDb25uZWN0aW9uICovXG4gIGNsb3NlRGF0YWJhc2U/OiAoKCkgPT4gUHJvbWlzZTx2b2lkPikgfCBmYWxzZTtcbn1cblxuLyoqXG4gKiBSZXN1bHQgb2Ygc3RhcnRpbmcgYSBzZXJ2ZXJcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGFydFNlcnZlclJlc3VsdCB7XG4gIC8qKiBIVFRQIHNlcnZlciBpbnN0YW5jZSAqL1xuICBzZXJ2ZXI6IFNlcnZlcjtcbiAgLyoqIFBvcnQgdGhlIHNlcnZlciBpcyBsaXN0ZW5pbmcgb24gKi9cbiAgcG9ydDogbnVtYmVyO1xuICAvKiogRnVuY3Rpb24gdG8gbWFudWFsbHkgdHJpZ2dlciBzaHV0ZG93biAqL1xuICBzaHV0ZG93bjogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbn1cblxuLyoqXG4gKiBTdGFydCBhbiBFeHByZXNzIHNlcnZlciB3aXRoIGdyYWNlZnVsIHNodXRkb3duIGhhbmRsaW5nXG4gKlxuICogRmVhdHVyZXM6XG4gKiAtIEF1dG9tYXRpYyBkYXRhYmFzZSBjb25uZWN0aW9uIHRlc3RpbmdcbiAqIC0gR3JhY2VmdWwgc2h1dGRvd24gb24gU0lHSU5UL1NJR1RFUk1cbiAqIC0gU1NFIGNvbm5lY3Rpb24gY2xlYW51cFxuICogLSBDb25maWd1cmFibGUgc2h1dGRvd24gdGltZW91dFxuICpcbiAqIEBwYXJhbSBhcHAgLSBFeHByZXNzIGFwcGxpY2F0aW9uXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFNlcnZlciBvcHRpb25zXG4gKiBAcmV0dXJucyBTZXJ2ZXIgaW5zdGFuY2UgYW5kIGNvbnRyb2wgZnVuY3Rpb25zXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHsgYXBwLCBzc2VNYW5hZ2VyIH0gPSBjcmVhdGVBcHAoKTtcbiAqXG4gKiBhcHAucG9zdCgnL2FwaS9yZXNvdXJjZScsIHJlcXVpcmVBdXRoLCBoYW5kbGVyKTtcbiAqXG4gKiBhd2FpdCBzdGFydFNlcnZlcihhcHAsIHtcbiAqICAgbmFtZTogJ015IE1pY3Jvc2VydmljZScsXG4gKiAgIHNzZU1hbmFnZXIsXG4gKiAgIG9uU3RhcnQ6IChwb3J0KSA9PiBjb25zb2xlLmxvZyhgTGlzdGVuaW5nIG9uICR7cG9ydH1gKSxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihcbiAgYXBwOiBFeHByZXNzLFxuICBvcHRpb25zOiBTdGFydFNlcnZlck9wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8U3RhcnRTZXJ2ZXJSZXN1bHQ+IHtcbiAgY29uc3Qgc2VydmVyQ29uZmlnID0gQ29uZmlnLmdldCgnc2VydmVyJyk7XG4gIGNvbnN0IHtcbiAgICBuYW1lID0gJ01pY3Jvc2VydmljZScsXG4gICAgc3NlTWFuYWdlcixcbiAgICBzaHV0ZG93blRpbWVvdXRNcyA9IDE1MDAwLFxuICAgIG9uU3RhcnQsXG4gICAgb25TaHV0ZG93bixcbiAgICBvbkJlZm9yZVN0YXJ0LFxuICAgIHRlc3REYXRhYmFzZSxcbiAgICBjbG9zZURhdGFiYXNlLFxuICB9ID0gb3B0aW9ucztcbiAgY29uc3QgcG9ydCA9IG9wdGlvbnMucG9ydCA/PyBzZXJ2ZXJDb25maWcucG9ydDtcblxuICAvLyBWYWxpZGF0ZSBhdXRoIGNvbmZpZ3VyYXRpb24gYXQgc2VydmVyIHN0YXJ0dXAgKG5vdCBkdXJpbmcgQ0RLIHN5bnRoZXNpcylcbiAgQ29uZmlnLnZhbGlkYXRlQXV0aCgpO1xuICAvLyBGYWlsIGZhc3QgaWYgdGhpcyBzZXJ2aWNlIGhhcyBubyBpbnRlcm5hbCBzaWduaW5nIGtleSBvZiBpdHMgb3duLCBvciBub1xuICAvLyBwdWJsaWMgYnVuZGxlIHRvIHZlcmlmeSBpdHMgcGVlcnMgd2l0aC4gV2l0aG91dCB0aGVtIGl0IHdvdWxkIG1pbnRcbiAgLy8gdG9rZW5zIG9uIGFuIEVQSEVNRVJBTCBpbi1wcm9jZXNzIGtleSB0aGF0IG5vIHBlZXIgYWNjZXB0cywgYW5kIHJlamVjdCBldmVyeVxuICAvLyBwZWVyJ3MgdG9rZW4g4oCUIGkuZS4gc2lsZW50bHkgbG9zZSBhbGwgc2VydmljZS10by1zZXJ2aWNlIHRyYWZmaWMuIENoZWNrZWRcbiAgLy8gSEVSRSByYXRoZXIgdGhhbiBpbiBgY3JlYXRlQXBwYCBiZWNhdXNlIGl0IGlzIGEgcHJvcGVydHkgb2YgdGhlIHJ1bm5pbmdcbiAgLy8gZGVwbG95bWVudCwgbm90IG9mIHRoZSBhcHAgb2JqZWN0OiByb3V0ZS10YWJsZSBidWlsZGVycyBhbmQgdGVzdCBzdWl0ZXNcbiAgLy8gYXNzZW1ibGUgYW4gYXBwIHdpdGhvdXQgZXZlciBzaWduaW5nIGFueXRoaW5nLlxuICBmb3IgKGNvbnN0IGVudlZhciBvZiBbJ1NFUlZJQ0VfU0lHTklOR19LRVlfRklMRScsICdTRVJWSUNFX0tFWV9CVU5ETEVfRklMRSddIGFzIGNvbnN0KSB7XG4gICAgaWYgKCFwcm9jZXNzLmVudltlbnZWYXJdKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYCR7ZW52VmFyfSBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyByZXF1aXJlZCAoZ2VuZXJhdGUgd2l0aCBkZXBsb3kvYmluL3NlcnZpY2Utc2lnbmluZy1rZXlzLnNoKS4gU2V0IGl0IGJlZm9yZSBzdGFydGluZyB0aGUgc2VydmVyLmApO1xuICAgIH1cbiAgfVxuICAvLyBGYWlsIGZhc3Qgb24gYW4gdW51c2FibGUgUmVkaXMgY29uZmlndXJhdGlvbiAodGhyb3dzIFJlZGlzQ29uZmlnRXJyb3IpIOKAlCBhXG4gIC8vIFJlZGlzIGNsaWVudCBpcyBjcmVhdGVkIGxhemlseSBvbiBmaXJzdCB1c2UsIHNvIHdpdGhvdXQgdGhpcyBhIGJhZCBjb25maWdcbiAgLy8gd291bGQgc3VyZmFjZSBhcyByZXF1ZXN0LXRpbWUgZXJyb3JzIGluc3RlYWQgb2YgYSBmYWlsZWQgc3RhcnQuXG4gIHJlc29sdmVSZWRpc0Nvbm5lY3Rpb24oKTtcblxuICBsb2dnZXIuaW5mbyhgU3RhcnRpbmcgJHtuYW1lfS4uLmApO1xuXG4gIC8vIE1hcmsgTm90UmVhZHkgc3luY2hyb25vdXNseSwgQkVGT1JFIHRoZSBwb3J0IG9wZW5zLCBzbyBubyByZXF1ZXN0IHNsaXBzXG4gIC8vIHRocm91Z2ggdGhlIHJlYWRpbmVzcyBndWFyZCBiZWZvcmUgdGhlIHN1cGVydmlzb3IgaGFzIHJ1bi4gVGhlIHN1cGVydmlzb3JcbiAgLy8gZmxpcHMgaXQgdHJ1ZSBvbmNlIGRlcGVuZGVuY2llcyBjb25uZWN0LlxuICBzZXRSZWFkeShmYWxzZSk7XG5cbiAgLy8gTGlzdGVuIEZJUlNULiBQcmV2aW91c2x5IHRoZSBwcm9jZXNzIHRlc3RlZCB0aGUgREIgYW5kIGBwcm9jZXNzLmV4aXQoMSlgJ2RcbiAgLy8gb24gZmFpbHVyZSAodG8gYmUgcmVzdGFydGVkKSwgc28gZHVyaW5nIGEgY29sZC1EQiBzdGFtcGVkZSBzZXJ2aWNlc1xuICAvLyBjcmFzaC1sb29wZWQgYW5kIG5ldmVyIGV2ZW4gb3BlbmVkIHRoZWlyIHBvcnQuIE5vdyB3ZSBvcGVuIHRoZSBwb3J0XG4gIC8vIGltbWVkaWF0ZWx5IOKAlCAvaGVhbHRoIChsaXZlbmVzcyksIC9yZWFkeSAocmVhZGluZXNzKSBhbmQgdGhlIHJlYWRpbmVzc1xuICAvLyBndWFyZCBhbGwgcmVzcG9uZCBhdCBvbmNlIOKAlCBhbmQgZXN0YWJsaXNoICsgbW9uaXRvciB0aGUgZGF0YXN0b3JlIGluIHRoZVxuICAvLyBiYWNrZ3JvdW5kLCByZXBvcnRpbmcgTm90UmVhZHkgdW50aWwgaXQgY29ubmVjdHMgaW5zdGVhZCBvZiBleGl0aW5nLlxuICBjb25zdCBzZXJ2ZXIgPSBhcHAubGlzdGVuKHBvcnQsICgpID0+IHtcbiAgICBsb2dnZXIuaW5mbyhgJHtuYW1lfSBsaXN0ZW5pbmcgb24gcG9ydDogJHtwb3J0fWApO1xuICAgIGxvZ2dlci5pbmZvKGBQbGF0Zm9ybSBVUkw6ICR7c2VydmVyQ29uZmlnLnBsYXRmb3JtVXJsfWApO1xuICAgIG9uU3RhcnQ/Lihwb3J0KTtcbiAgfSk7XG5cbiAgLy8gU2h1dGRvd24gaGFuZGxlciAoZ3VhcmRlZCBhZ2FpbnN0IGNvbmN1cnJlbnQgc2lnbmFscylcbiAgbGV0IHNodXR0aW5nRG93biA9IGZhbHNlO1xuXG4gIC8vIEJhY2tncm91bmQgZGVwZW5kZW5jeSBzdXBlcnZpc29yIOKAlCBlc3RhYmxpc2hlZCArIG1vbml0b3JlZCBhZnRlciBsaXN0ZW4sXG4gIC8vIHN0b3BwZWQgb24gc2h1dGRvd24gc28gaXQgbmV2ZXIga2VlcHMgYSBjbG9zaW5nIHByb2Nlc3MgYWxpdmUuIEd1YXJkIHRoZVxuICAvLyBmbG9hdGluZyBwcm9taXNlOiBldmVyeSBleHBlY3RlZCBmYWlsdXJlIGlzIGhhbmRsZWQgaW5zaWRlLCBzbyBhIHJlamVjdGlvblxuICAvLyBoZXJlIG1lYW5zIGEgcHJvZ3JhbW1pbmcgZXJyb3Ig4oCUIGxvZyBpdCBhbmQgbGVhdmUgdGhlIHNlcnZpY2UgTm90UmVhZHlcbiAgLy8gcmF0aGVyIHRoYW4gbGV0dGluZyBpdCBiZWNvbWUgYW4gdW5oYW5kbGVkUmVqZWN0aW9uIHRoYXQgY3Jhc2gtZXhpdHMgdGhlXG4gIC8vIHByb2Nlc3MgKHdoaWNoIHdvdWxkIGRlZmVhdCB0aGUgd2hvbGUgXCJuZXZlciBleGl0IG9uIGRlcGVuZGVuY3kgdHJvdWJsZVwiXG4gIC8vIGdvYWwpLlxuICBzdXBlcnZpc2VEZXBlbmRlbmNpZXMobmFtZSwgb25CZWZvcmVTdGFydCwgdGVzdERhdGFiYXNlLCAoKSA9PiBzaHV0dGluZ0Rvd24pLmNhdGNoKChlcnJvcikgPT4ge1xuICAgIGxvZ2dlci5lcnJvcignRGVwZW5kZW5jeSBzdXBlcnZpc29yIGNyYXNoZWQ7IHNlcnZpY2Ugd2lsbCByZW1haW4gTm90UmVhZHknLCB7IGVycm9yOiBlcnJvck1lc3NhZ2UoZXJyb3IpIH0pO1xuICB9KTtcblxuICBjb25zdCBzaHV0ZG93biA9IGFzeW5jIChzaWduYWw/OiBzdHJpbmcpID0+IHtcbiAgICBpZiAoc2h1dHRpbmdEb3duKSByZXR1cm47XG4gICAgc2h1dHRpbmdEb3duID0gdHJ1ZTtcblxuICAgIC8vIERyYWluIEZJUlNUOiByZXBvcnQgTm90UmVhZHkgYmVmb3JlIGFueXRoaW5nIGVsc2UsIHNvIHRoZSBsb2FkIGJhbGFuY2VyIC9cbiAgICAvLyByZWFkaW5lc3MgcHJvYmUgc3RvcHMgc2VuZGluZyBuZXcgd29yayB3aGlsZSB0aGUgcmVzdCBvZiBzaHV0ZG93biBydW5zLlxuICAgIC8vIChQcmV2aW91c2x5IHJlYWRpbmVzcyBzdGF5ZWQgZ3JlZW4gdGhyb3VnaCBvblNodXRkb3duLCB3aGljaCBjb3VsZCB0YWtlXG4gICAgLy8gc2Vjb25kcyDigJQgbmV3IHJlcXVlc3RzIGtlcHQgYXJyaXZpbmcgYXQgYSBwcm9jZXNzIHRlYXJpbmcgaXRzZWxmIGRvd24uKVxuICAgIHNldFJlYWR5KGZhbHNlKTtcblxuICAgIGlmIChzaWduYWwpIHtcbiAgICAgIGxvZ2dlci5pbmZvKGAke3NpZ25hbH0gcmVjZWl2ZWQsIHNodXR0aW5nIGRvd24gZ3JhY2VmdWxseS4uLmApO1xuICAgIH1cblxuICAgIC8vIEFybSB0aGUgZm9yY2UtZXhpdCBCRUZPUkUgYXdhaXRpbmcgYW55dGhpbmc6IGEgaHVuZyBvblNodXRkb3duIChhIHN0dWNrXG4gICAgLy8gcXVldWUgY2xvc2UsIGEgUmVkaXMgcXVpdCB0aGF0IG5ldmVyIGFuc3dlcnMpIHdvdWxkIG90aGVyd2lzZSBibG9jayBmb3JldmVyXG4gICAgLy8gd2l0aCB0aGUgdGltZXIgbmV2ZXIgc3RhcnRlZCwgc28gdGhlIHBvZCB3b3VsZCBpZ25vcmUgU0lHVEVSTSB1bnRpbCBTSUdLSUxMLlxuICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGb3JjZWQgc2h1dGRvd24gYWZ0ZXIgdGltZW91dCcpO1xuICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgIH0sIHNodXRkb3duVGltZW91dE1zKS51bnJlZigpO1xuXG4gICAgLy8gQ3VzdG9tIHNodXRkb3duIGNhbGxiYWNrXG4gICAgaWYgKG9uU2h1dGRvd24pIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IG9uU2h1dGRvd24oKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgaW4gb25TaHV0ZG93biBjYWxsYmFjaycsIHsgZXJyb3IgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQ2xvc2UgU1NFIGNvbm5lY3Rpb25zXG4gICAgaWYgKHNzZU1hbmFnZXIpIHtcbiAgICAgIHNzZU1hbmFnZXIuc2h1dGRvd24oKTtcbiAgICAgIGxvZ2dlci5pbmZvKCdTU0UgY29ubmVjdGlvbnMgY2xvc2VkJyk7XG4gICAgfVxuXG4gICAgLy8gQ2xvc2UgSFRUUCBzZXJ2ZXJcbiAgICBzZXJ2ZXIuY2xvc2UoYXN5bmMgKCkgPT4ge1xuICAgICAgbG9nZ2VyLmluZm8oJ0hUVFAgc2VydmVyIGNsb3NlZCcpO1xuXG4gICAgICAvLyBTaHV0ZG93biBPcGVuVGVsZW1ldHJ5IHRyYWNpbmdcbiAgICAgIGF3YWl0IHNodXRkb3duVHJhY2luZygpLmNhdGNoKGVyciA9PiBsb2dnZXIuZXJyb3IoJ0Vycm9yIHNodXR0aW5nIGRvd24gdHJhY2luZycsIHsgZXJyb3I6IGVyciB9KSk7XG5cbiAgICAgIC8vIENsb3NlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAgICAgIGlmIChjbG9zZURhdGFiYXNlICE9PSBmYWxzZSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmIChjbG9zZURhdGFiYXNlKSB7XG4gICAgICAgICAgICBhd2FpdCBjbG9zZURhdGFiYXNlKCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGF3YWl0IGNsb3NlQ29ubmVjdGlvbigpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgY29ubmVjdGlvbiBjbG9zZWQnKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIGNsb3NpbmcgZGF0YWJhc2UnLCB7IGVycm9yIH0pO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICB9KTtcbiAgfTtcblxuICAvLyBSZWdpc3RlciBzaWduYWwgaGFuZGxlcnNcbiAgcHJvY2Vzcy5vbmNlKCdTSUdJTlQnLCAoKSA9PiB2b2lkIHNodXRkb3duKCdTSUdJTlQnKSk7XG4gIHByb2Nlc3Mub25jZSgnU0lHVEVSTScsICgpID0+IHZvaWQgc2h1dGRvd24oJ1NJR1RFUk0nKSk7XG5cbiAgcmV0dXJuIHtcbiAgICBzZXJ2ZXIsXG4gICAgcG9ydCxcbiAgICBzaHV0ZG93bjogKCkgPT4gc2h1dGRvd24oKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBTaW1wbGUgc2VydmVyIHN0YXJ0IHdyYXBwZXIgd2l0aCBlcnJvciBoYW5kbGluZ1xuICpcbiAqIEBwYXJhbSBhcHAgLSBFeHByZXNzIGFwcGxpY2F0aW9uXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFNlcnZlciBvcHRpb25zXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHsgYXBwLCBzc2VNYW5hZ2VyIH0gPSBjcmVhdGVBcHAoKTtcbiAqIGFwcC5wb3N0KCcvYXBpL3Jlc291cmNlJywgaGFuZGxlcik7XG4gKlxuICogdm9pZCBydW5TZXJ2ZXIoYXBwLCB7IG5hbWU6ICdNeSBTZXJ2aWNlJywgc3NlTWFuYWdlciB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuU2VydmVyKGFwcDogRXhwcmVzcywgb3B0aW9uczogU3RhcnRTZXJ2ZXJPcHRpb25zID0ge30pOiB2b2lkIHtcbiAgLy8gUm91dGUgdGFibGUgb2YgdGhlIGZ1bGx5IGFzc2VtYmxlZCBhcHAgKHNlZSBhcGktY29yZSdzIHJvdXRlLXRhYmxlLnRzKTogdGhlXG4gIC8vIHNhbWUgaW50cm9zcGVjdGlvbiB0aGUgcGVyLXNlcnZpY2UgY292ZXJhZ2UgdGVzdCBhc3NlcnRzIG9uLCBsb2dnZWQgb25jZSBhdFxuICAvLyBib290IHNvIGFuIHVuZ2F0ZWQgb3IgdW5hdWRpdGVkIHdyaXRlIHJvdXRlIGlzIHZpc2libGUgaW4gdGhlIHJ1bm5pbmdcbiAgLy8gc2VydmljZSwgbm90IG9ubHkgaW4gQ0kuXG4gIGxvZ2dlci5pbmZvKCdSb3V0ZSB0YWJsZSBidWlsdCcsIHN1bW1hcml6ZVJvdXRlVGFibGUoYnVpbGRSb3V0ZVRhYmxlKGFwcCkpKTtcblxuICAvLyBMYXN0LXJlc29ydCBmYXVsdCBoYW5kbGVycyBmb3IgdGhlIHByb2R1Y3Rpb24gZW50cnlwb2ludDogYW4gdW5oYW5kbGVkXG4gIC8vIHJlamVjdGlvbiAvIHVuY2F1Z2h0IGV4Y2VwdGlvbiBpcyBsb2dnZWQgKG5vdCBzaWxlbnRseSBmYXRhbCkgdGhlbiB0aGVcbiAgLy8gcHJvY2VzcyBleGl0cyBmb3IgdGhlIG9yY2hlc3RyYXRvciB0byByZXN0YXJ0LiBOby1vcCB1bmRlciBOT0RFX0VOVj10ZXN0LlxuICBpbnN0YWxsQ3Jhc2hIYW5kbGVycyhsb2dnZXIpO1xuICBzdGFydFNlcnZlcihhcHAsIG9wdGlvbnMpLmNhdGNoKChlcnJvcikgPT4ge1xuICAgIC8vIHN0YXJ0U2VydmVyIG5vdyBsaXN0ZW5zLWZpcnN0IGFuZCBzdXBlcnZpc2VzIHRoZSBEQiBpbiB0aGUgYmFja2dyb3VuZCwgc29cbiAgICAvLyBpdCBvbmx5IHJlamVjdHMgb24gYSBnZW51aW5lbHkgZmF0YWwgU0VUVVAgZXJyb3IgKGludmFsaWQgYXV0aCBjb25maWcsIGFcbiAgICAvLyBmYWlsZWQgb25TdGFydCwgdGhlIHBvcnQgYWxyZWFkeSBpbiB1c2UpLiBMb2cgbWVzc2FnZSArIHN0YWNrIGV4cGxpY2l0bHk6XG4gICAgLy8gYW4gRXJyb3IgaW4gd2luc3RvbiBtZXRhZGF0YSBzZXJpYWxpemVzIHRvIGB7fWAgKG1lc3NhZ2Uvc3RhY2sgYXJlXG4gICAgLy8gbm9uLWVudW1lcmFibGUpLCB3aGljaCB3b3VsZCBvdGhlcndpc2UgaGlkZSB0aGUgY2F1c2UgYmVoaW5kIGB7ZXJyb3I6e319YC5cbiAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBzdGFydCBzZXJ2ZXInLCB7XG4gICAgICBlcnJvcjogZXJyb3JNZXNzYWdlKGVycm9yKSxcbiAgICAgIHN0YWNrOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3Iuc3RhY2sgOiB1bmRlZmluZWQsXG4gICAgfSk7XG4gICAgcHJvY2Vzcy5leGl0KDEpO1xuICB9KTtcbn1cbiJdfQ==
|
|
@@ -2,9 +2,8 @@ import { type TenantContext } from '@pipeline-builder/pipeline-data';
|
|
|
2
2
|
import type { Request, Response, NextFunction } from 'express';
|
|
3
3
|
/**
|
|
4
4
|
* Resolves the RLS tenant scope for a request. The default reads the CALLER's
|
|
5
|
-
* authenticated identity org (`ctx.identity.orgId`, validated by requireOrgId) —
|
|
6
|
-
*
|
|
7
|
-
* RLS. Services with a different request boundary supply their own resolver (e.g. platform
|
|
5
|
+
* authenticated identity org (`ctx.identity.orgId`, validated by requireOrgId) — never a
|
|
6
|
+
* route param, which names the QUERIED scope, the wrong tenant for RLS. Services with a different request boundary supply their own resolver (e.g. platform
|
|
8
7
|
* sets the scope pre-auth from an unverified JWT peek so unauthenticated endpoints still
|
|
9
8
|
* get a sane default).
|
|
10
9
|
*/
|
|
@@ -21,8 +20,8 @@ export type TenantScopeResolver = (req: Request) => TenantContext;
|
|
|
21
20
|
* Default placement is AFTER `requireAuth` + `requireOrgId` (the route factories do this);
|
|
22
21
|
* pass a custom `resolve` for a different boundary. A thrown resolver means tenant identity
|
|
23
22
|
* could NOT be established (a wiring bug — this runs after auth) — we hard-FAIL the request
|
|
24
|
-
* (500 via next(err)) rather than proceed. Proceeding would run queries with empty RLS GUCs
|
|
25
|
-
* under FORCE RLS
|
|
26
|
-
*
|
|
23
|
+
* (500 via next(err)) rather than proceed. Proceeding would run queries with empty RLS GUCs,
|
|
24
|
+
* which under FORCE RLS hide every row and refuse writes — a wiring bug surfacing as a
|
|
25
|
+
* silently empty result instead of an error.
|
|
27
26
|
*/
|
|
28
27
|
export declare function withTenantContext(resolve?: TenantScopeResolver): (req: Request, _res: Response, next: NextFunction) => void;
|