@pipeline-builder/pipeline-data 4.1.46 → 4.1.47
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/reporting/analytics-queries.js +43 -15
- package/lib/database/index.d.ts +2 -0
- package/lib/database/index.js +5 -1
- package/lib/database/postgres-connection.js +8 -1
- package/lib/database/read-connection.d.ts +19 -0
- package/lib/database/read-connection.js +122 -0
- package/lib/database/read-lag.d.ts +31 -0
- package/lib/database/read-lag.js +202 -0
- package/lib/database/tenancy.d.ts +102 -1
- package/lib/database/tenancy.js +101 -3
- package/lib/testing/exports.json +10 -0
- package/package.json +2 -2
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
35
35
|
import { db } from './postgres-connection.js';
|
|
36
|
+
import { type LagVerdict } from './read-lag.js';
|
|
36
37
|
export interface TenantContext {
|
|
37
38
|
/** Caller's org. Undefined for un-authenticated / system jobs. */
|
|
38
39
|
orgId?: string;
|
|
@@ -51,6 +52,25 @@ export interface TenantContext {
|
|
|
51
52
|
* (e.g. entity-event compliance eval) can honor parent `propagateToChildren`
|
|
52
53
|
* rules without a request in scope. */
|
|
53
54
|
parentOrgId?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Set once this request has opened a PRIMARY transaction, i.e. may have
|
|
57
|
+
* written. Subsequent `withTenantTxRead` calls in the same request then go to
|
|
58
|
+
* the primary too, so a handler cannot read its own write off a replica that
|
|
59
|
+
* has not replayed it yet.
|
|
60
|
+
*
|
|
61
|
+
* Deliberately set by `withTenantTx` rather than by write call sites: that
|
|
62
|
+
* path is the one that can write, it needs no per-call-site discipline, and
|
|
63
|
+
* being wrong in this direction only costs a replica hit. Mutated in place on
|
|
64
|
+
* the AsyncLocalStorage store — the store is per-request, so this cannot leak
|
|
65
|
+
* between requests.
|
|
66
|
+
*
|
|
67
|
+
* SCOPE: this covers reads AFTER a write IN THE SAME REQUEST. It does NOT
|
|
68
|
+
* cover the next request (POST then a page reload issuing GET). That needs
|
|
69
|
+
* shared state — see the cross-request section of
|
|
70
|
+
* docs/plans/postgres-read-write-split.md — and must be settled before call
|
|
71
|
+
* sites are migrated in bulk.
|
|
72
|
+
*/
|
|
73
|
+
mayHaveWritten?: boolean;
|
|
54
74
|
}
|
|
55
75
|
/**
|
|
56
76
|
* Per-request tenant context. Populated by Express middleware once the JWT
|
|
@@ -92,4 +112,85 @@ export declare function getTenantContext(): TenantContext | undefined;
|
|
|
92
112
|
* - Returns whatever `fn` returns; throws whatever `fn` throws (Drizzle
|
|
93
113
|
* handles the COMMIT/ROLLBACK for us).
|
|
94
114
|
*/
|
|
95
|
-
export declare function withTenantTx<T>(fn: (tx: Parameters<Parameters<typeof db.transaction>[0]>[0]) => Promise<T
|
|
115
|
+
export declare function withTenantTx<T>(fn: (tx: Parameters<Parameters<typeof db.transaction>[0]>[0]) => Promise<T>,
|
|
116
|
+
/**
|
|
117
|
+
* INTERNAL. Whether this transaction should mark the request as possibly
|
|
118
|
+
* having written. Always true for a real caller; only withTenantTxRead's
|
|
119
|
+
* primary-fallback path passes false, because a read that lands on the
|
|
120
|
+
* primary has not written. See the comment at the assignment below.
|
|
121
|
+
*/
|
|
122
|
+
markWritten?: boolean): Promise<T>;
|
|
123
|
+
/**
|
|
124
|
+
* Why a read was or was not sent to the replica. Returned by
|
|
125
|
+
* {@link chooseReadTarget} so the decision is inspectable — and testable
|
|
126
|
+
* without a replica, which matters because the test substrate (PGlite) has no
|
|
127
|
+
* replication at all.
|
|
128
|
+
*/
|
|
129
|
+
export type ReadTargetReason =
|
|
130
|
+
/** No replica configured, or DB_READ_ENABLED=false. */
|
|
131
|
+
'no-replica'
|
|
132
|
+
/** This request already opened a primary transaction and may have written. */
|
|
133
|
+
| 'may-have-written'
|
|
134
|
+
/**
|
|
135
|
+
* No tenant context. A caller outside `runWithTenantContext` has no org to
|
|
136
|
+
* scope RLS by and no watermark to compare, so it goes to the primary — where
|
|
137
|
+
* `withTenantTx`'s RLS_CONTEXT_MODE guard logs or throws. Routed to a replica
|
|
138
|
+
* instead, the same call would set `app.org_id = ''` with no diagnostic at all.
|
|
139
|
+
*/
|
|
140
|
+
| 'no-context'
|
|
141
|
+
/**
|
|
142
|
+
* A replica exists but its replay lag is beyond `DB_READ_MAX_LAG_SECONDS`,
|
|
143
|
+
* or is UNKNOWN (no sample yet, the sample failed, or the cached sample has
|
|
144
|
+
* gone stale). Unknown and lagging are the same decision on purpose: the
|
|
145
|
+
* circuit fails closed.
|
|
146
|
+
*/
|
|
147
|
+
| 'replica-lagging'
|
|
148
|
+
/** Routed to the replica. */
|
|
149
|
+
| 'replica';
|
|
150
|
+
export interface ReadTarget {
|
|
151
|
+
useReplica: boolean;
|
|
152
|
+
reason: ReadTargetReason;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Decide where a read-only transaction should run.
|
|
156
|
+
*
|
|
157
|
+
* Pure and side-effect free on purpose: this is the whole of the routing
|
|
158
|
+
* policy, and it is the part worth testing. Replication itself cannot be
|
|
159
|
+
* reproduced locally, so the testable unit is the DECISION, not the lag.
|
|
160
|
+
*
|
|
161
|
+
* The bias is deliberate: anything uncertain resolves to the primary. A read
|
|
162
|
+
* served by the primary is merely less efficient; a read served by a stale
|
|
163
|
+
* replica is a user-visible bug nobody can reproduce.
|
|
164
|
+
*/
|
|
165
|
+
/** Everything about the replica that is not request-specific. */
|
|
166
|
+
export interface ReadReplicaHealth {
|
|
167
|
+
/** A replica pool exists AND `DB_READ_ENABLED` is not false. */
|
|
168
|
+
available: boolean;
|
|
169
|
+
/** The lag circuit's verdict. `unknown` is treated exactly like `lagging`. */
|
|
170
|
+
lag: LagVerdict;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Where a read goes, and why.
|
|
174
|
+
*
|
|
175
|
+
* SYSTEM gates are checked before REQUEST gates, which matters only for the
|
|
176
|
+
* explanation — but the explanation is what an operator reads when asking why
|
|
177
|
+
* the replica is idle, and "my request wrote" is a far less useful answer than
|
|
178
|
+
* "the standby is 40 seconds behind".
|
|
179
|
+
*/
|
|
180
|
+
export declare function chooseReadTarget(ctx: TenantContext | undefined, health: ReadReplicaHealth): ReadTarget;
|
|
181
|
+
/**
|
|
182
|
+
* Open a READ-ONLY transaction with the same RLS GUCs `withTenantTx` sets,
|
|
183
|
+
* routed to the replica when {@link chooseReadTarget} allows it.
|
|
184
|
+
*
|
|
185
|
+
* `SET TRANSACTION READ ONLY` is the guardrail, not decoration. Several queries
|
|
186
|
+
* in this codebase look like reads at the call site and are not —
|
|
187
|
+
* `SELECT … FOR UPDATE` in crud-service's setDefault, `pg_advisory_xact_lock`
|
|
188
|
+
* in pipeline-service and compliance's subscription-service. Routed to a
|
|
189
|
+
* standby those fail anyway; declaring the transaction read-only makes them
|
|
190
|
+
* fail HERE, with a clear message, including on the primary where they would
|
|
191
|
+
* otherwise succeed and quietly acquire a lock the caller did not intend.
|
|
192
|
+
*
|
|
193
|
+
* Falls back to the primary transparently, so a caller never has to ask
|
|
194
|
+
* whether a replica exists.
|
|
195
|
+
*/
|
|
196
|
+
export declare function withTenantTxRead<T>(fn: (tx: Parameters<Parameters<typeof db.transaction>[0]>[0]) => Promise<T>): Promise<T>;
|
package/lib/database/tenancy.js
CHANGED
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
* also set `app.is_sysadmin = 'true'` or they'll lose access to it.
|
|
35
35
|
*/
|
|
36
36
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
37
|
-
import { createLogger, envInt } from '@pipeline-builder/api-core';
|
|
37
|
+
import { createLogger, emitCounter, envInt } from '@pipeline-builder/api-core';
|
|
38
38
|
import { sql } from 'drizzle-orm';
|
|
39
39
|
import { db } from './postgres-connection.js';
|
|
40
|
+
import { getReadDb, getReadPool } from './read-connection.js';
|
|
41
|
+
import { lagVerdict, maybeSampleLag } from './read-lag.js';
|
|
40
42
|
const logger = createLogger('tenant-context');
|
|
41
43
|
function getContextMode() {
|
|
42
44
|
const raw = process.env.RLS_CONTEXT_MODE?.toLowerCase();
|
|
@@ -102,7 +104,14 @@ export function getTenantContext() {
|
|
|
102
104
|
* - Returns whatever `fn` returns; throws whatever `fn` throws (Drizzle
|
|
103
105
|
* handles the COMMIT/ROLLBACK for us).
|
|
104
106
|
*/
|
|
105
|
-
export async function withTenantTx(fn
|
|
107
|
+
export async function withTenantTx(fn,
|
|
108
|
+
/**
|
|
109
|
+
* INTERNAL. Whether this transaction should mark the request as possibly
|
|
110
|
+
* having written. Always true for a real caller; only withTenantTxRead's
|
|
111
|
+
* primary-fallback path passes false, because a read that lands on the
|
|
112
|
+
* primary has not written. See the comment at the assignment below.
|
|
113
|
+
*/
|
|
114
|
+
markWritten = true) {
|
|
106
115
|
const ctx = tenantContext.getStore();
|
|
107
116
|
if (!ctx) {
|
|
108
117
|
// Surface the bug instead of silently SET'ing empty GUCs. The default
|
|
@@ -127,6 +136,22 @@ export async function withTenantTx(fn) {
|
|
|
127
136
|
const orgId = ctx?.orgId ?? '';
|
|
128
137
|
const isSuperAdmin = ctx?.isSuperAdmin ? 'true' : 'false';
|
|
129
138
|
const statementTimeoutMs = getStatementTimeoutMs();
|
|
139
|
+
// This transaction runs on the PRIMARY and may write, so any later read in
|
|
140
|
+
// this request must stay on the primary too. Set before the transaction opens
|
|
141
|
+
// rather than after it commits: a read issued concurrently from the same
|
|
142
|
+
// handler would otherwise race the flag.
|
|
143
|
+
//
|
|
144
|
+
// `markWritten: false` is passed ONLY by withTenantTxRead's fallback path,
|
|
145
|
+
// and it is not an optimisation — without it the feature defeats itself. That
|
|
146
|
+
// path runs on the primary, so it would set this flag, which api-server's
|
|
147
|
+
// middleware reads after the response to set the Redis write pin. A purely
|
|
148
|
+
// READ-ONLY request would therefore pin its whole org to the primary for the
|
|
149
|
+
// pin TTL, every time, and since reads fall back whenever no replica is
|
|
150
|
+
// configured (the shipping default) every org would be permanently pinned.
|
|
151
|
+
// The flag means "this request may have WRITTEN", not "this request touched
|
|
152
|
+
// the primary", and the difference is the whole read-offload benefit.
|
|
153
|
+
if (ctx && markWritten)
|
|
154
|
+
ctx.mayHaveWritten = true;
|
|
130
155
|
return db.transaction(async (tx) => {
|
|
131
156
|
// Set every transaction-scoped GUC in a SINGLE round-trip. `set_config()`
|
|
132
157
|
// returns its value, so multiple calls compose in one SELECT rather than a
|
|
@@ -149,4 +174,77 @@ export async function withTenantTx(fn) {
|
|
|
149
174
|
return fn(tx);
|
|
150
175
|
});
|
|
151
176
|
}
|
|
152
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW5jeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhYmFzZS90ZW5hbmN5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0NHO0FBRUgsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNsRSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUU5QyxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQWU5QyxTQUFTLGNBQWM7SUFDckIsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxXQUFXLEVBQUUsQ0FBQztJQUN4RCxJQUFJLEdBQUcsS0FBSyxRQUFRLElBQUksR0FBRyxLQUFLLE1BQU0sSUFBSSxHQUFHLEtBQUssUUFBUTtRQUFFLE9BQU8sR0FBRyxDQUFDO0lBQ3ZFLHVFQUF1RTtJQUN2RSxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxLQUFLLFlBQVksQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7QUFDbkUsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsU0FBUyxxQkFBcUI7SUFDNUIsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDLHlCQUF5QixFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3JELE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUM7QUFDL0IsQ0FBQztBQXNCRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLElBQUksaUJBQWlCLEVBQWlCLENBQUM7QUFFcEU7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQUksR0FBa0IsRUFBRSxFQUFXO0lBQ3JFLE9BQU8sYUFBYSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDcEMsQ0FBQztBQUVEOzBFQUMwRTtBQUMxRSxNQUFNLFVBQVUsZ0JBQWdCO0lBQzlCLE9BQU8sYUFBYSxDQUFDLFFBQVEsRUFBRSxDQUFDO0FBQ2xDLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FDaEMsRUFBMkU7SUFFM0UsTUFBTSxHQUFHLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRXJDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNULHNFQUFzRTtRQUN0RSxzRUFBc0U7UUFDdEUsdUVBQXVFO1FBQ3ZFLHNFQUFzRTtRQUN0RSxtRUFBbUU7UUFDbkUsNkJBQTZCO1FBQzdCLE1BQU0sSUFBSSxHQUFHLGNBQWMsRUFBRSxDQUFDO1FBQzlCLElBQUksSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQ2IsdUVBQXVFO2tCQUNyRSx3RUFBd0U7a0JBQ3hFLCtDQUErQyxDQUNsRCxDQUFDO1FBQ0osQ0FBQztRQUNELElBQUksSUFBSSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQ3BCLHNFQUFzRTtZQUN0RSxNQUFNLENBQUMsSUFBSSxDQUFDLG9FQUFvRSxFQUFFO2dCQUNoRixLQUFLLEVBQUUsSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxLQUFLO2FBQ2pELENBQUMsQ0FBQztRQUNMLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxLQUFLLEdBQUcsR0FBRyxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUM7SUFDL0IsTUFBTSxZQUFZLEdBQUcsR0FBRyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFFMUQsTUFBTSxrQkFBa0IsR0FBRyxxQkFBcUIsRUFBRSxDQUFDO0lBRW5ELE9BQU8sRUFBRSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7UUFDakMsMEVBQTBFO1FBQzFFLDJFQUEyRTtRQUMzRSwwRUFBMEU7UUFDMUUsK0JBQStCO1FBQy9CLDBFQUEwRTtRQUMxRSw2RUFBNkU7UUFDN0UscUJBQXFCO1FBQ3JCLEVBQUU7UUFDRiw2RUFBNkU7UUFDN0UsK0VBQStFO1FBQy9FLDZFQUE2RTtRQUM3RSxtRUFBbUU7UUFDbkUsSUFBSSxrQkFBa0IsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMzQixNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQ2QsR0FBRyxDQUFBLG1DQUFtQyxLQUFLLDBDQUEwQyxZQUFZLDRDQUE0QyxNQUFNLENBQUMsa0JBQWtCLENBQUMsU0FBUyxDQUNqTCxDQUFDO1FBQ0osQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQ2QsR0FBRyxDQUFBLG1DQUFtQyxLQUFLLDBDQUEwQyxZQUFZLFNBQVMsQ0FDM0csQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNoQixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBUZW5hbnQtY29udGV4dCBwbHVtYmluZyBmb3IgUG9zdGdyZXMgcm93LWxldmVsIHNlY3VyaXR5LlxuICpcbiAqIEJhY2tncm91bmQ6IHBvc3RncmVzLWluaXQuc3FsIGluc3RhbGxzIFJMUyBwb2xpY2llcyBvbiBldmVyeSB1c2VyLWRhdGFcbiAqIHRhYmxlIHRoYXQgY29uc3VsdCB0d28gc2Vzc2lvbiBHVUNzIOKAlCBgYXBwLm9yZ19pZGAgYW5kIGBhcHAuaXNfc3lzYWRtaW5gLlxuICogRXZlcnkgdGVuYW50IHRhYmxlIGlzIGBGT1JDRSBST1cgTEVWRUwgU0VDVVJJVFlgLCBzbyB0aGUgcG9saWNpZXMgZW5mb3JjZVxuICogZXZlbiBmb3IgdGhlIChvd25pbmcpIGNvbm5lY3Rpb24gdXNlciDigJQgYSBxdWVyeSBhZ2FpbnN0IG9uZSBtdXN0IHJ1biBpbnNpZGUgYVxuICogdHJhbnNhY3Rpb24gdGhhdCBoYXMgU0VUIExPQ0FMJ2QgYm90aCBHVUNzIG9yIGl0IHJldHVybnMgemVybyByb3dzIGZvclxuICogbm9uLXN5c2FkbWlucyAoYW5kIG1heSBmYWlsIHRvIHdyaXRlIGZvciBhbnkgY2FsbGVyKS4gVGhlIGNyb3NzLW9yZyBwbHVnaW5cbiAqIGVjb3N5c3RlbSBjYXRhbG9nIHRhYmxlcyBjYXJyeSBhIHBlcm1pc3NpdmUgYXBwLXJvbGUgcG9saWN5IGluc3RlYWQuXG4gKlxuICogVGhpcyBtb2R1bGUgaXMgdGhlIHNlYW0uXG4gKlxuICogICAxLiBgdGVuYW50Q29udGV4dGAgKEFzeW5jTG9jYWxTdG9yYWdlKSBjYXJyaWVzIGB7b3JnSWQsIGlzU3VwZXJBZG1pbn1gXG4gKiAgICAgIGRvd24gdGhlIGNhbGwgY2hhaW4gd2l0aG91dCBwcm9wLWRyaWxsaW5nLiBTZXQgb25jZSBhdCB0aGUgcmVxdWVzdFxuICogICAgICBib3VuZGFyeTsgcmVhZGFibGUgZnJvbSBhbnl3aGVyZS5cbiAqXG4gKiAgIDIuIGB3aXRoVGVuYW50VHgoZm4pYCBvcGVucyBhIHRyYW5zYWN0aW9uLCBTRVQgTE9DQUwncyBib3RoIEdVQ3MgZnJvbVxuICogICAgICB0aGUgc3Vycm91bmRpbmcgY29udGV4dCwgdGhlbiBpbnZva2VzIGBmbih0eClgLiBTZXJ2aWNlcyBtaWdyYXRlIG9uZVxuICogICAgICBhdCBhIHRpbWUgYnkgd3JhcHBpbmcgdGhlaXIgZXhpc3RpbmcgZHJpenpsZSBjYWxscyBpbiB0aGlzIGhlbHBlciDigJRcbiAqICAgICAgYGRiLnNlbGVjdCgpLmZyb20oLi4uKWAgYmVjb21lcyBgd2l0aFRlbmFudFR4KHR4ID0+IHR4LnNlbGVjdCgpLmZyb20oLi4uKSlgLlxuICpcbiAqICAgMy4gYHJ1bldpdGhUZW5hbnRDb250ZXh0KGN0eCwgZm4pYCBpcyB0aGUgRXhwcmVzcy1taWRkbGV3YXJlLXNpZGVcbiAqICAgICAgYm9va2VuZCB0aGF0IGVzdGFibGlzaGVzIHRoZSBBc3luY0xvY2FsU3RvcmFnZSBzY29wZSBmb3IgdGhlIHJlcXVlc3QuXG4gKlxuICogTWlncmF0aW9uIG9yZGVyOlxuICogICAtIEZpcnN0IGFkb3B0ZXJzOiBuZXdseS13cml0dGVuIHNlcnZpY2VzIChkYXNoYm9hcmQsIGFsZXJ0LWRlc3RpbmF0aW9uKVxuICogICAgIHdoZXJlIHRoZSBjaGFuZ2UgaXMgbWVjaGFuaWNhbC5cbiAqICAgLSBUaGVuIGhpZ2gtd3JpdGUgc2VydmljZXMgKHBsdWdpbiwgcGlwZWxpbmUpLiBTb2FrIGVhY2ggb25lIGJlZm9yZVxuICogICAgIGZsaXBwaW5nIHRoZSB1bmRlcmx5aW5nIHRhYmxlIHRvIEZPUkNFLlxuICogICAtIGFkbWluX2F1ZGl0X2xvZyB1c2VzIGEgc3lzYWRtaW4tb25seSBwb2xpY3kg4oCUIHN5c2FkbWluIHBhdGhzIG11c3RcbiAqICAgICBhbHNvIHNldCBgYXBwLmlzX3N5c2FkbWluID0gJ3RydWUnYCBvciB0aGV5J2xsIGxvc2UgYWNjZXNzIHRvIGl0LlxuICovXG5cbmltcG9ydCB7IEFzeW5jTG9jYWxTdG9yYWdlIH0gZnJvbSAnbm9kZTphc3luY19ob29rcyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIsIGVudkludCB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IHNxbCB9IGZyb20gJ2RyaXp6bGUtb3JtJztcbmltcG9ydCB7IGRiIH0gZnJvbSAnLi9wb3N0Z3Jlcy1jb25uZWN0aW9uLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCd0ZW5hbnQtY29udGV4dCcpO1xuXG4vKipcbiAqIEJlaGF2aW9yIHdoZW4gYHdpdGhUZW5hbnRUeGAgaXMgaW52b2tlZCBvdXRzaWRlIGFueSBgcnVuV2l0aFRlbmFudENvbnRleHRgXG4gKiBzY29wZS4gVGhlIGVmZmVjdGl2ZSBkZWZhdWx0IGlzIGZhaWwtZmFzdCAoJ3N0cmljdCcpIGluIHByb2R1Y3Rpb24gc28gYVxuICogbWlzc2luZyB0ZW5hbnQgc2NvcGUgc3VyZmFjZXMgYXQgdGhlIGJhZCBjYWxsIHNpdGUgaW5zdGVhZCBvZiBzaWxlbnRseVxuICogcnVubmluZyB3aXRoIGVtcHR5IFJMUyBHVUNzOyBvdXRzaWRlIHByb2R1Y3Rpb24gaXQgZGVmYXVsdHMgdG8gJ3dhcm4nIHNvXG4gKiB1bi1hdWRpdGVkIGRldi90ZXN0IHBhdGhzIGtlZXAgd29ya2luZyBidXQgbG9nIGFuIGFjdGlvbmFibGUgdHJhY2UuIEFuXG4gKiBleHBsaWNpdCBSTFNfQ09OVEVYVF9NT0RFIGVudiBvdmVycmlkZSBhbHdheXMgd2lucy5cbiAqXG4gKiAnc2lsZW50JyBpcyBrZXB0IGZvciB0ZXN0cyBhbmQgc2NyaXB0cyB0aGF0IGludGVudGlvbmFsbHkgZW50ZXIgdGhlIERCXG4gKiB3aXRob3V0IGNvbnRleHQgKGUuZy4gaW50ZWdyYXRpb24gdGVzdCBzZXR1cCB0aGF0IHJ1bnMgYXMgdGhlIGNvbm5lY3Rpb25cbiAqIG93bmVyIGJlZm9yZSBhbnkgdGVuYW50IGlzIHByb3Zpc2lvbmVkKS5cbiAqL1xudHlwZSBDb250ZXh0TW9kZSA9ICdzaWxlbnQnIHwgJ3dhcm4nIHwgJ3N0cmljdCc7XG5mdW5jdGlvbiBnZXRDb250ZXh0TW9kZSgpOiBDb250ZXh0TW9kZSB7XG4gIGNvbnN0IHJhdyA9IHByb2Nlc3MuZW52LlJMU19DT05URVhUX01PREU/LnRvTG93ZXJDYXNlKCk7XG4gIGlmIChyYXcgPT09ICdzaWxlbnQnIHx8IHJhdyA9PT0gJ3dhcm4nIHx8IHJhdyA9PT0gJ3N0cmljdCcpIHJldHVybiByYXc7XG4gIC8vIE5vIGV4cGxpY2l0IG92ZXJyaWRlOiBmYWlsLWZhc3QgaW4gcHJvZHVjdGlvbiwgd2FybiBldmVyeXdoZXJlIGVsc2UuXG4gIHJldHVybiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ3Byb2R1Y3Rpb24nID8gJ3N0cmljdCcgOiAnd2Fybic7XG59XG5cbi8qKlxuICogUGVyLXN0YXRlbWVudCBzZXJ2ZXItc2lkZSB0aW1lb3V0IChtcykgYXBwbGllZCBJTlNJREUgZXZlcnkgYHdpdGhUZW5hbnRUeGBcbiAqIHRyYW5zYWN0aW9uLCBuZXh0IHRvIHRoZSBSTFMgR1VDcy4gYHdpdGhUZW5hbnRUeGAgaXMgdGhlIHJlYWwgcXVlcnkgcGF0aCBmb3JcbiAqIGFsbCBSTFMvQ1JVRCByZWFkcyBhbmQgd3JpdGVzLCBzbyB0aGlzIGlzIHdoZXJlIHRoZSBEQi1zaWRlIGd1YXJkIGJlbG9uZ3MgKHRoZVxuICogb2xkIGBDb25uZWN0aW9uLnRyYW5zYWN0aW9uYCB3cmFwcGVyIHRoYXQgc2V0IGl0IGlzIGRlYWQg4oCUIHNlZVxuICogcG9zdGdyZXMtY29ubmVjdGlvbi50cykuIEJvdW5kcyBhIHJ1bmF3YXkgcXVlcnkgc28gaXQgY2FuJ3QgaG9sZCBhIHBvb2xlZFxuICogY29ubmVjdGlvbiBvcGVuIGluZGVmaW5pdGVseS4gRW52LWNvbmZpZ3VyYWJsZSB2aWEgYERCX1NUQVRFTUVOVF9USU1FT1VUX01TYFxuICogKGRlZmF1bHQgMzBzKTsgc2V0IHRvIGAwYCB0byBkaXNhYmxlIHRoZSB0aW1lb3V0LlxuICovXG5mdW5jdGlvbiBnZXRTdGF0ZW1lbnRUaW1lb3V0TXMoKTogbnVtYmVyIHtcbiAgY29uc3QgbXMgPSBlbnZJbnQoJ0RCX1NUQVRFTUVOVF9USU1FT1VUX01TJywgMzBfMDAwKTtcbiAgcmV0dXJuIG1zID49IDAgPyBtcyA6IDMwXzAwMDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUZW5hbnRDb250ZXh0IHtcbiAgLyoqIENhbGxlcidzIG9yZy4gVW5kZWZpbmVkIGZvciB1bi1hdXRoZW50aWNhdGVkIC8gc3lzdGVtIGpvYnMuICovXG4gIG9yZ0lkPzogc3RyaW5nO1xuICAvKiogQ2FsbGVyJ3MgdXNlciBpZC4gTm90IHVzZWQgZm9yIFJMUyDigJQgUG9zdGdyZXMgcG9saWNpZXMgYXJlIG9yZy1ncmFpbmVkLFxuICAgKiAgd2l0aCBubyBwZXItdXNlciBzZXNzaW9uIEdVQyDigJQgYnV0IGNhcnJpZWQgc28gYXBwLWxheWVyIHByZWRpY2F0ZXMgd2l0aCBhXG4gICAqICBQRVItVVNFUiBydW5nIChwaXBlbGluZSB0ZW1wbGF0ZXMnIGBwcml2YXRlYCB2aXNpYmlsaXR5KSBjYW4gcmVzb2x2ZSBcIndob1xuICAgKiAgaXMgYXNraW5nXCIgb24gcGF0aHMgdGhhdCBoYXZlIG5vd2hlcmUgdG8gcGFzcyBpdCwgbm90YWJseSBDcnVkU2VydmljZSdzXG4gICAqICBgd3JpdGVDb25kaXRpb25zYC4gQWJzZW50IGZvciBzeXN0ZW0gam9icywgd2hpY2ggaXMgdGhlIGZhaWwtY2xvc2VkIGNhc2U6XG4gICAqICBubyB2aWV3ZXIg4oeSIG5vIHByaXZhdGUgcm93cy4gKi9cbiAgdXNlcklkPzogc3RyaW5nO1xuICAvKiogVHJ1ZSB3aGVuIHRoZSBjYWxsZXIgaXMgYSBzeXNhZG1pbiAoc3lzdGVtLW9yZyBhZG1pbikuIEJ5cGFzc2VzIFJMU1xuICAgKiAgcG9saWNpZXMgdmlhIHRoZSBzeXNhZG1pbi1ieXBhc3MgYnJhbmNoIGluIGBjdXJyZW50X2lzX3N5c2FkbWluKClgLiAqL1xuICBpc1N1cGVyQWRtaW46IGJvb2xlYW47XG4gIC8qKiBPcmcg4oaSIHRlYW0gaGllcmFyY2h5OiB0aGUgY2FsbGVyJ3MgYWN0aXZlLW9yZyBwYXJlbnQgKHByZXNlbnQgb25seSB3aGVuIHRoZVxuICAgKiAgYWN0aXZlIG9yZyBpcyBhIHRlYW0pLiBOb3QgdXNlZCBmb3IgUkxTOyBjYXJyaWVkIHNvIGRvd25zdHJlYW0gc2lkZS1lZmZlY3RzXG4gICAqICAoZS5nLiBlbnRpdHktZXZlbnQgY29tcGxpYW5jZSBldmFsKSBjYW4gaG9ub3IgcGFyZW50IGBwcm9wYWdhdGVUb0NoaWxkcmVuYFxuICAgKiAgcnVsZXMgd2l0aG91dCBhIHJlcXVlc3QgaW4gc2NvcGUuICovXG4gIHBhcmVudE9yZ0lkPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFBlci1yZXF1ZXN0IHRlbmFudCBjb250ZXh0LiBQb3B1bGF0ZWQgYnkgRXhwcmVzcyBtaWRkbGV3YXJlIG9uY2UgdGhlIEpXVFxuICogaXMgdmFsaWRhdGVkOyBjb25zdW1lZCBieSBgd2l0aFRlbmFudFR4YCBhbmQgYW55IGhlbHBlciB0aGF0IG5lZWRzIHRvXG4gKiBrbm93IFwid2hvIGlzIHRoZSBjYWxsZXJcIiB3aXRob3V0IHJlLWRlcml2aW5nIGZyb20gdGhlIHJlcXVlc3Qgb2JqZWN0LlxuICovXG5leHBvcnQgY29uc3QgdGVuYW50Q29udGV4dCA9IG5ldyBBc3luY0xvY2FsU3RvcmFnZTxUZW5hbnRDb250ZXh0PigpO1xuXG4vKipcbiAqIFJ1biBgZm5gIGluc2lkZSB0aGUgZ2l2ZW4gdGVuYW50IHNjb3BlLiBUaGUgQXN5bmNMb2NhbFN0b3JhZ2Ugc2NvcGVcbiAqIHN1cnZpdmVzIGFjcm9zcyBhc3luYyBib3VuZGFyaWVzIChQcm9taXNlcywgc2V0VGltZW91dCwgZXRjLiksIHNvIGFueVxuICogYHdpdGhUZW5hbnRUeGAgY2FsbCBpbnNpZGUgYGZuYCDigJQgZGlyZWN0bHkgb3IgdGhyb3VnaCBhbnkgZGVwdGggb2ZcbiAqIGFzeW5jIGhlbHBlcnMg4oCUIHBpY2tzIHVwIGBjdHhgLlxuICpcbiAqIFdyYXAgYG5leHQoKWAgZnJvbSBhbiBFeHByZXNzIG1pZGRsZXdhcmUgdG8gZXN0YWJsaXNoIHBlci1yZXF1ZXN0IHNjb3BlOlxuICpcbiAqICAgICBhcHAudXNlKChyZXEsIHJlcywgbmV4dCkgPT4ge1xuICogICAgICAgcnVuV2l0aFRlbmFudENvbnRleHQoeyBvcmdJZCwgaXNTdXBlckFkbWluIH0sICgpID0+IG5leHQoKSk7XG4gKiAgICAgfSk7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBydW5XaXRoVGVuYW50Q29udGV4dDxUPihjdHg6IFRlbmFudENvbnRleHQsIGZuOiAoKSA9PiBUKTogVCB7XG4gIHJldHVybiB0ZW5hbnRDb250ZXh0LnJ1bihjdHgsIGZuKTtcbn1cblxuLyoqIFJlYWQgdGhlIGN1cnJlbnQgdGVuYW50IGNvbnRleHQuIFJldHVybnMgdW5kZWZpbmVkIG91dHNpZGUgYW5cbiAqICBgcnVuV2l0aFRlbmFudENvbnRleHRgIHNjb3BlIChlLmcuIGJhY2tncm91bmQgd29ya2VycywgbWlncmF0aW9ucykuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGVuYW50Q29udGV4dCgpOiBUZW5hbnRDb250ZXh0IHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHRlbmFudENvbnRleHQuZ2V0U3RvcmUoKTtcbn1cblxuLyoqXG4gKiBPcGVuIGEgdHJhbnNhY3Rpb24gd2l0aCBSTFMgR1VDcyBTRVQgTE9DQUwgZnJvbSB0aGUgY3VycmVudFxuICogQXN5bmNMb2NhbFN0b3JhZ2UgdGVuYW50IGNvbnRleHQsIHRoZW4gaW52b2tlIGBmbih0eClgLlxuICpcbiAqIC0gV2hlbiBjYWxsZWQgb3V0c2lkZSBhIHRlbmFudCBzY29wZSAoZS5nLiBmcm9tIGEgYmFja2dyb3VuZCBqb2IgdGhhdFxuICogICBkb2Vzbid0IGhhdmUgYSBjYWxsZXIgaWRlbnRpdHkpLCB0aGUgdHJhbnNhY3Rpb24gaXMgb3BlbmVkIHdpdGggYm90aFxuICogICBHVUNzIGNsZWFyZWQg4oCUIFJMUyBwb2xpY2llcyB3aWxsIHJldHVybiB6ZXJvIHJvd3Mgb24gRk9SQ0UnZCB0YWJsZXMuXG4gKiAgIENhbGxlcnMgdGhhdCBuZWVkIGEgc3lzYWRtaW4tZXF1aXZhbGVudCBzY29wZSBzaG91bGQgd3JhcCB0aGVtc2VsdmVzIGluXG4gKiAgIGBydW5XaXRoVGVuYW50Q29udGV4dCh7IGlzU3VwZXJBZG1pbjogdHJ1ZSB9LCDigKYpYCBleHBsaWNpdGx5LlxuICpcbiAqIC0gVXNlcyBgc2V0X2NvbmZpZyhrZXksIHZhbHVlLCB0cnVlKWAgKHRoZSBib29sZWFuID0gYGlzX2xvY2FsYCwgaS5lLlxuICogICB0cmFuc2FjdGlvbi1zY29wZWQpLiBFcXVpdmFsZW50IHRvIGBTRVQgTE9DQUxgIGJ1dCB0YWtlcyB0aGUgdmFsdWUgYXNcbiAqICAgYSBwYXJhbWV0ZXIsIHdoaWNoIGlzIHdoYXQgd2Ugd2FudCDigJQgRHJpenpsZSdzIGBzcWxgIHRlbXBsYXRlIGJpbmRzIHRoZVxuICogICB2YWx1ZSBzYWZlbHkgc28gYW4gYXR0YWNrZXItY29udHJvbGxlZCBgb3JnSWRgIGNhbid0IGVzY2FwZSB0aGUgcXVvdGluZy5cbiAqXG4gKiAtIFJldHVybnMgd2hhdGV2ZXIgYGZuYCByZXR1cm5zOyB0aHJvd3Mgd2hhdGV2ZXIgYGZuYCB0aHJvd3MgKERyaXp6bGVcbiAqICAgaGFuZGxlcyB0aGUgQ09NTUlUL1JPTExCQUNLIGZvciB1cykuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3aXRoVGVuYW50VHg8VD4oXG4gIGZuOiAodHg6IFBhcmFtZXRlcnM8UGFyYW1ldGVyczx0eXBlb2YgZGIudHJhbnNhY3Rpb24+WzBdPlswXSkgPT4gUHJvbWlzZTxUPixcbik6IFByb21pc2U8VD4ge1xuICBjb25zdCBjdHggPSB0ZW5hbnRDb250ZXh0LmdldFN0b3JlKCk7XG5cbiAgaWYgKCFjdHgpIHtcbiAgICAvLyBTdXJmYWNlIHRoZSBidWcgaW5zdGVhZCBvZiBzaWxlbnRseSBTRVQnaW5nIGVtcHR5IEdVQ3MuIFRoZSBkZWZhdWx0XG4gICAgLy8gbW9kZSAoJ3dhcm4nKSBsb2dzIGFuIGFjdGlvbmFibGUgdHJhY2Ugc28gdGhlIGJhZCBjYWxsIHNpdGUgaXMgZWFzeVxuICAgIC8vIHRvIGZpbmQ7IHByb2R1Y3Rpb24gZGVwbG95bWVudHMgdGhhdCBoYXZlIGZpbmlzaGVkIHRoZSBhdWRpdCBjYW4gc2V0XG4gICAgLy8gUkxTX0NPTlRFWFRfTU9ERT1zdHJpY3QgdG8gZmFpbC1mYXN0IGF0IHRoZSBjYWxsIHNpdGUgKGJldHRlciBzdGFja1xuICAgIC8vIHRyYWNlIHRoYW4gdGhlIFBvc3RncmVzIFwicGVybWlzc2lvbiBkZW5pZWRcIiB0aGF0IHdvdWxkIG90aGVyd2lzZVxuICAgIC8vIHN1cmZhY2UgZnJvbSBGT1JDRSdkIFJMUykuXG4gICAgY29uc3QgbW9kZSA9IGdldENvbnRleHRNb2RlKCk7XG4gICAgaWYgKG1vZGUgPT09ICdzdHJpY3QnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd3aXRoVGVuYW50VHggY2FsbGVkIG91dHNpZGUgYSB0ZW5hbnQgc2NvcGUuIFdyYXAgeW91ciBoYW5kbGVyL3dvcmtlciAnXG4gICAgICAgICsgJ2luIHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgb3JnSWQsIGlzU3VwZXJBZG1pbiB9LCAuLi4pIGJlZm9yZSBpbnZva2luZyAnXG4gICAgICAgICsgJ2FueSBzZXJ2aWNlIHRoYXQgdG91Y2hlcyBSTFMtZW5mb3JjZWQgdGFibGVzLicsXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAobW9kZSA9PT0gJ3dhcm4nKSB7XG4gICAgICAvLyBJbmNsdWRlIGEgc3ludGhldGljIHN0YWNrIHNvIHRoZSBvZmZlbmRpbmcgY2FsbCBzaXRlIGlzIGluIHRoZSBsb2cuXG4gICAgICBsb2dnZXIud2Fybignd2l0aFRlbmFudFR4IGNhbGxlZCBvdXRzaWRlIGEgdGVuYW50IHNjb3BlOyBSTFMgR1VDcyB3aWxsIGJlIGVtcHR5Jywge1xuICAgICAgICBzdGFjazogbmV3IEVycm9yKCd0ZW5hbnQtY29udGV4dCBtaXNzaW5nJykuc3RhY2ssXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBvcmdJZCA9IGN0eD8ub3JnSWQgPz8gJyc7XG4gIGNvbnN0IGlzU3VwZXJBZG1pbiA9IGN0eD8uaXNTdXBlckFkbWluID8gJ3RydWUnIDogJ2ZhbHNlJztcblxuICBjb25zdCBzdGF0ZW1lbnRUaW1lb3V0TXMgPSBnZXRTdGF0ZW1lbnRUaW1lb3V0TXMoKTtcblxuICByZXR1cm4gZGIudHJhbnNhY3Rpb24oYXN5bmMgKHR4KSA9PiB7XG4gICAgLy8gU2V0IGV2ZXJ5IHRyYW5zYWN0aW9uLXNjb3BlZCBHVUMgaW4gYSBTSU5HTEUgcm91bmQtdHJpcC4gYHNldF9jb25maWcoKWBcbiAgICAvLyByZXR1cm5zIGl0cyB2YWx1ZSwgc28gbXVsdGlwbGUgY2FsbHMgY29tcG9zZSBpbiBvbmUgU0VMRUNUIHJhdGhlciB0aGFuIGFcbiAgICAvLyByb3VuZC10cmlwIHBlciBHVUMg4oCUIGVhY2ggYSBuZXR3b3JrIGhvcCB0aGF0IHRheGVzIHRoZSBzaGFyZWQgUGdCb3VuY2VyXG4gICAgLy8gcG9vbCBvbiB0aGUgaG90IHBhdGguIGB0cnVlYFxuICAgIC8vID0gaXNfbG9jYWwgKFNFVCBMT0NBTCBzZW1hbnRpY3M6IGF1dG8tcmVsZWFzZWQgb24gQ09NTUlUL1JPTExCQUNLKS4gVGhlXG4gICAgLy8gZHJpdmVyIGJpbmRzIHRoZSB2YWx1ZXMgYXMgcGFyYW1ldGVycywgc28gYSBob3N0aWxlIG9yZ19pZCBjYW4ndCBicmVhayBvdXRcbiAgICAvLyBvZiB0aGUgR1VDIHN5bnRheC5cbiAgICAvL1xuICAgIC8vIFRoZSBzdGF0ZW1lbnRfdGltZW91dCBHVUMgYm91bmRzIGEgcnVuYXdheSBxdWVyeSBzbyBpdCBjYW4ndCBob2xkIGEgcG9vbGVkXG4gICAgLy8gY29ubmVjdGlvbiBvcGVuIGluZGVmaW5pdGVseSAoYFNFVCBMT0NBTCBzdGF0ZW1lbnRfdGltZW91dCA9ICQxYCBpcyByZWplY3RlZFxuICAgIC8vIGJ5IFBvc3RncmVzLCBoZW5jZSBzZXRfY29uZmlnIHdpdGggYSBib3VuZCBtcyB2YWx1ZSkuIEluY2x1ZGVkIGluIHRoZSBzYW1lXG4gICAgLy8gc3RhdGVtZW50IHdoZW4gZW5hYmxlZCAoPjApLCBkcm9wcGVkIGVudGlyZWx5IHdoZW4gZGlzYWJsZWQgKDApLlxuICAgIGlmIChzdGF0ZW1lbnRUaW1lb3V0TXMgPiAwKSB7XG4gICAgICBhd2FpdCB0eC5leGVjdXRlKFxuICAgICAgICBzcWxgU0VMRUNUIHNldF9jb25maWcoJ2FwcC5vcmdfaWQnLCAke29yZ0lkfSwgdHJ1ZSksIHNldF9jb25maWcoJ2FwcC5pc19zeXNhZG1pbicsICR7aXNTdXBlckFkbWlufSwgdHJ1ZSksIHNldF9jb25maWcoJ3N0YXRlbWVudF90aW1lb3V0JywgJHtTdHJpbmcoc3RhdGVtZW50VGltZW91dE1zKX0sIHRydWUpYCxcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IHR4LmV4ZWN1dGUoXG4gICAgICAgIHNxbGBTRUxFQ1Qgc2V0X2NvbmZpZygnYXBwLm9yZ19pZCcsICR7b3JnSWR9LCB0cnVlKSwgc2V0X2NvbmZpZygnYXBwLmlzX3N5c2FkbWluJywgJHtpc1N1cGVyQWRtaW59LCB0cnVlKWAsXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gZm4odHgpO1xuICB9KTtcbn1cbiJdfQ==
|
|
177
|
+
/**
|
|
178
|
+
* Where a read goes, and why.
|
|
179
|
+
*
|
|
180
|
+
* SYSTEM gates are checked before REQUEST gates, which matters only for the
|
|
181
|
+
* explanation — but the explanation is what an operator reads when asking why
|
|
182
|
+
* the replica is idle, and "my request wrote" is a far less useful answer than
|
|
183
|
+
* "the standby is 40 seconds behind".
|
|
184
|
+
*/
|
|
185
|
+
export function chooseReadTarget(ctx, health) {
|
|
186
|
+
if (!health.available)
|
|
187
|
+
return { useReplica: false, reason: 'no-replica' };
|
|
188
|
+
if (health.lag !== 'ok')
|
|
189
|
+
return { useReplica: false, reason: 'replica-lagging' };
|
|
190
|
+
if (!ctx)
|
|
191
|
+
return { useReplica: false, reason: 'no-context' };
|
|
192
|
+
if (ctx.mayHaveWritten)
|
|
193
|
+
return { useReplica: false, reason: 'may-have-written' };
|
|
194
|
+
return { useReplica: true, reason: 'replica' };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Open a READ-ONLY transaction with the same RLS GUCs `withTenantTx` sets,
|
|
198
|
+
* routed to the replica when {@link chooseReadTarget} allows it.
|
|
199
|
+
*
|
|
200
|
+
* `SET TRANSACTION READ ONLY` is the guardrail, not decoration. Several queries
|
|
201
|
+
* in this codebase look like reads at the call site and are not —
|
|
202
|
+
* `SELECT … FOR UPDATE` in crud-service's setDefault, `pg_advisory_xact_lock`
|
|
203
|
+
* in pipeline-service and compliance's subscription-service. Routed to a
|
|
204
|
+
* standby those fail anyway; declaring the transaction read-only makes them
|
|
205
|
+
* fail HERE, with a clear message, including on the primary where they would
|
|
206
|
+
* otherwise succeed and quietly acquire a lock the caller did not intend.
|
|
207
|
+
*
|
|
208
|
+
* Falls back to the primary transparently, so a caller never has to ask
|
|
209
|
+
* whether a replica exists.
|
|
210
|
+
*/
|
|
211
|
+
export async function withTenantTxRead(fn) {
|
|
212
|
+
const ctx = tenantContext.getStore();
|
|
213
|
+
const readDb = getReadDb();
|
|
214
|
+
const readPool = getReadPool();
|
|
215
|
+
// Keep the lag sample warm. Fire-and-forget by design: it is debounced to
|
|
216
|
+
// DB_READ_LAG_SAMPLE_MS, never throws, and must not add a round-trip to this
|
|
217
|
+
// read. The first read of a process therefore routes to the primary (no
|
|
218
|
+
// sample yet) and warms the circuit for the ones after it.
|
|
219
|
+
if (readPool)
|
|
220
|
+
void maybeSampleLag(readPool);
|
|
221
|
+
const target = chooseReadTarget(ctx, { available: readDb !== null, lag: lagVerdict() });
|
|
222
|
+
emitCounter('postgres_read_routed_total', { target: target.useReplica ? 'replica' : 'primary', reason: target.reason });
|
|
223
|
+
if (!target.useReplica) {
|
|
224
|
+
// Primary path: identical semantics to withTenantTx, plus the read-only
|
|
225
|
+
// declaration so an accidental write is caught on every target — including
|
|
226
|
+
// the ones that will never have a replica.
|
|
227
|
+
return withTenantTx(async (tx) => {
|
|
228
|
+
await tx.execute(sql `SET TRANSACTION READ ONLY`);
|
|
229
|
+
return fn(tx);
|
|
230
|
+
}, /* markWritten */ false);
|
|
231
|
+
}
|
|
232
|
+
// Non-null by construction: chooseReadTarget only returns useReplica for a
|
|
233
|
+
// ctx that exists. Read the GUCs off it directly rather than defaulting to
|
|
234
|
+
// '' — a default here would silently re-open the hole that 'no-context'
|
|
235
|
+
// closes, because an empty app.org_id is a VALID-looking RLS scope.
|
|
236
|
+
const { orgId } = ctx;
|
|
237
|
+
const isSuperAdmin = ctx.isSuperAdmin ? 'true' : 'false';
|
|
238
|
+
const statementTimeoutMs = getStatementTimeoutMs();
|
|
239
|
+
return readDb.transaction(async (tx) => {
|
|
240
|
+
await tx.execute(sql `SET TRANSACTION READ ONLY`);
|
|
241
|
+
if (statementTimeoutMs > 0) {
|
|
242
|
+
await tx.execute(sql `SELECT set_config('app.org_id', ${orgId}, true), set_config('app.is_sysadmin', ${isSuperAdmin}, true), set_config('statement_timeout', ${String(statementTimeoutMs)}, true)`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
await tx.execute(sql `SELECT set_config('app.org_id', ${orgId}, true), set_config('app.is_sysadmin', ${isSuperAdmin}, true)`);
|
|
246
|
+
}
|
|
247
|
+
return fn(tx);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW5jeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYXRhYmFzZS90ZW5hbmN5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0NHO0FBRUgsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsTUFBTSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDL0UsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsQyxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDOUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUM5RCxPQUFPLEVBQUUsVUFBVSxFQUFFLGNBQWMsRUFBbUIsTUFBTSxlQUFlLENBQUM7QUFFNUUsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGdCQUFnQixDQUFDLENBQUM7QUFlOUMsU0FBUyxjQUFjO0lBQ3JCLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLENBQUM7SUFDeEQsSUFBSSxHQUFHLEtBQUssUUFBUSxJQUFJLEdBQUcsS0FBSyxNQUFNLElBQUksR0FBRyxLQUFLLFFBQVE7UUFBRSxPQUFPLEdBQUcsQ0FBQztJQUN2RSx1RUFBdUU7SUFDdkUsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxZQUFZLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0FBQ25FLENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILFNBQVMscUJBQXFCO0lBQzVCLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyx5QkFBeUIsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNyRCxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDO0FBQy9CLENBQUM7QUF5Q0Q7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxJQUFJLGlCQUFpQixFQUFpQixDQUFDO0FBRXBFOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFJLEdBQWtCLEVBQUUsRUFBVztJQUNyRSxPQUFPLGFBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ3BDLENBQUM7QUFFRDswRUFDMEU7QUFDMUUsTUFBTSxVQUFVLGdCQUFnQjtJQUM5QixPQUFPLGFBQWEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztBQUNsQyxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxZQUFZLENBQ2hDLEVBQTJFO0FBQzNFOzs7OztHQUtHO0FBQ0gsV0FBVyxHQUFHLElBQUk7SUFFbEIsTUFBTSxHQUFHLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBRXJDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNULHNFQUFzRTtRQUN0RSxzRUFBc0U7UUFDdEUsdUVBQXVFO1FBQ3ZFLHNFQUFzRTtRQUN0RSxtRUFBbUU7UUFDbkUsNkJBQTZCO1FBQzdCLE1BQU0sSUFBSSxHQUFHLGNBQWMsRUFBRSxDQUFDO1FBQzlCLElBQUksSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQ2IsdUVBQXVFO2tCQUNyRSx3RUFBd0U7a0JBQ3hFLCtDQUErQyxDQUNsRCxDQUFDO1FBQ0osQ0FBQztRQUNELElBQUksSUFBSSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQ3BCLHNFQUFzRTtZQUN0RSxNQUFNLENBQUMsSUFBSSxDQUFDLG9FQUFvRSxFQUFFO2dCQUNoRixLQUFLLEVBQUUsSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxLQUFLO2FBQ2pELENBQUMsQ0FBQztRQUNMLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxLQUFLLEdBQUcsR0FBRyxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUM7SUFDL0IsTUFBTSxZQUFZLEdBQUcsR0FBRyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFFMUQsTUFBTSxrQkFBa0IsR0FBRyxxQkFBcUIsRUFBRSxDQUFDO0lBRW5ELDJFQUEyRTtJQUMzRSw4RUFBOEU7SUFDOUUseUVBQXlFO0lBQ3pFLHlDQUF5QztJQUN6QyxFQUFFO0lBQ0YsMkVBQTJFO0lBQzNFLDhFQUE4RTtJQUM5RSwwRUFBMEU7SUFDMUUsMkVBQTJFO0lBQzNFLDZFQUE2RTtJQUM3RSx3RUFBd0U7SUFDeEUsMkVBQTJFO0lBQzNFLDRFQUE0RTtJQUM1RSxzRUFBc0U7SUFDdEUsSUFBSSxHQUFHLElBQUksV0FBVztRQUFFLEdBQUcsQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBRWxELE9BQU8sRUFBRSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7UUFDakMsMEVBQTBFO1FBQzFFLDJFQUEyRTtRQUMzRSwwRUFBMEU7UUFDMUUsK0JBQStCO1FBQy9CLDBFQUEwRTtRQUMxRSw2RUFBNkU7UUFDN0UscUJBQXFCO1FBQ3JCLEVBQUU7UUFDRiw2RUFBNkU7UUFDN0UsK0VBQStFO1FBQy9FLDZFQUE2RTtRQUM3RSxtRUFBbUU7UUFDbkUsSUFBSSxrQkFBa0IsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMzQixNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQ2QsR0FBRyxDQUFBLG1DQUFtQyxLQUFLLDBDQUEwQyxZQUFZLDRDQUE0QyxNQUFNLENBQUMsa0JBQWtCLENBQUMsU0FBUyxDQUNqTCxDQUFDO1FBQ0osQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQ2QsR0FBRyxDQUFBLG1DQUFtQyxLQUFLLDBDQUEwQyxZQUFZLFNBQVMsQ0FDM0csQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNoQixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFzREQ7Ozs7Ozs7R0FPRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxHQUE4QixFQUFFLE1BQXlCO0lBQ3hGLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUztRQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsQ0FBQztJQUMxRSxJQUFJLE1BQU0sQ0FBQyxHQUFHLEtBQUssSUFBSTtRQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxpQkFBaUIsRUFBRSxDQUFDO0lBQ2pGLElBQUksQ0FBQyxHQUFHO1FBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxDQUFDO0lBQzdELElBQUksR0FBRyxDQUFDLGNBQWM7UUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQztJQUNqRixPQUFPLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDakQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FDcEMsRUFBMkU7SUFFM0UsTUFBTSxHQUFHLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3JDLE1BQU0sTUFBTSxHQUFHLFNBQVMsRUFBRSxDQUFDO0lBQzNCLE1BQU0sUUFBUSxHQUFHLFdBQVcsRUFBRSxDQUFDO0lBRS9CLDBFQUEwRTtJQUMxRSw2RUFBNkU7SUFDN0Usd0VBQXdFO0lBQ3hFLDJEQUEyRDtJQUMzRCxJQUFJLFFBQVE7UUFBRSxLQUFLLGNBQWMsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUU1QyxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsTUFBTSxLQUFLLElBQUksRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3hGLFdBQVcsQ0FBQyw0QkFBNEIsRUFBRSxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFFeEgsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUN2Qix3RUFBd0U7UUFDeEUsMkVBQTJFO1FBQzNFLDJDQUEyQztRQUMzQyxPQUFPLFlBQVksQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7WUFDL0IsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQSwyQkFBMkIsQ0FBQyxDQUFDO1lBQ2pELE9BQU8sRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ2hCLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM5QixDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLDJFQUEyRTtJQUMzRSx3RUFBd0U7SUFDeEUsb0VBQW9FO0lBQ3BFLE1BQU0sRUFBRSxLQUFLLEVBQUUsR0FBRyxHQUFJLENBQUM7SUFDdkIsTUFBTSxZQUFZLEdBQUcsR0FBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFDMUQsTUFBTSxrQkFBa0IsR0FBRyxxQkFBcUIsRUFBRSxDQUFDO0lBRW5ELE9BQU8sTUFBTyxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEVBQUU7UUFDdEMsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQSwyQkFBMkIsQ0FBQyxDQUFDO1FBQ2pELElBQUksa0JBQWtCLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDM0IsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUNkLEdBQUcsQ0FBQSxtQ0FBbUMsS0FBSywwQ0FBMEMsWUFBWSw0Q0FBNEMsTUFBTSxDQUFDLGtCQUFrQixDQUFDLFNBQVMsQ0FDakwsQ0FBQztRQUNKLENBQUM7YUFBTSxDQUFDO1lBQ04sTUFBTSxFQUFFLENBQUMsT0FBTyxDQUNkLEdBQUcsQ0FBQSxtQ0FBbUMsS0FBSywwQ0FBMEMsWUFBWSxTQUFTLENBQzNHLENBQUM7UUFDSixDQUFDO1FBQ0QsT0FBTyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDaEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogVGVuYW50LWNvbnRleHQgcGx1bWJpbmcgZm9yIFBvc3RncmVzIHJvdy1sZXZlbCBzZWN1cml0eS5cbiAqXG4gKiBCYWNrZ3JvdW5kOiBwb3N0Z3Jlcy1pbml0LnNxbCBpbnN0YWxscyBSTFMgcG9saWNpZXMgb24gZXZlcnkgdXNlci1kYXRhXG4gKiB0YWJsZSB0aGF0IGNvbnN1bHQgdHdvIHNlc3Npb24gR1VDcyDigJQgYGFwcC5vcmdfaWRgIGFuZCBgYXBwLmlzX3N5c2FkbWluYC5cbiAqIEV2ZXJ5IHRlbmFudCB0YWJsZSBpcyBgRk9SQ0UgUk9XIExFVkVMIFNFQ1VSSVRZYCwgc28gdGhlIHBvbGljaWVzIGVuZm9yY2VcbiAqIGV2ZW4gZm9yIHRoZSAob3duaW5nKSBjb25uZWN0aW9uIHVzZXIg4oCUIGEgcXVlcnkgYWdhaW5zdCBvbmUgbXVzdCBydW4gaW5zaWRlIGFcbiAqIHRyYW5zYWN0aW9uIHRoYXQgaGFzIFNFVCBMT0NBTCdkIGJvdGggR1VDcyBvciBpdCByZXR1cm5zIHplcm8gcm93cyBmb3JcbiAqIG5vbi1zeXNhZG1pbnMgKGFuZCBtYXkgZmFpbCB0byB3cml0ZSBmb3IgYW55IGNhbGxlcikuIFRoZSBjcm9zcy1vcmcgcGx1Z2luXG4gKiBlY29zeXN0ZW0gY2F0YWxvZyB0YWJsZXMgY2FycnkgYSBwZXJtaXNzaXZlIGFwcC1yb2xlIHBvbGljeSBpbnN0ZWFkLlxuICpcbiAqIFRoaXMgbW9kdWxlIGlzIHRoZSBzZWFtLlxuICpcbiAqICAgMS4gYHRlbmFudENvbnRleHRgIChBc3luY0xvY2FsU3RvcmFnZSkgY2FycmllcyBge29yZ0lkLCBpc1N1cGVyQWRtaW59YFxuICogICAgICBkb3duIHRoZSBjYWxsIGNoYWluIHdpdGhvdXQgcHJvcC1kcmlsbGluZy4gU2V0IG9uY2UgYXQgdGhlIHJlcXVlc3RcbiAqICAgICAgYm91bmRhcnk7IHJlYWRhYmxlIGZyb20gYW55d2hlcmUuXG4gKlxuICogICAyLiBgd2l0aFRlbmFudFR4KGZuKWAgb3BlbnMgYSB0cmFuc2FjdGlvbiwgU0VUIExPQ0FMJ3MgYm90aCBHVUNzIGZyb21cbiAqICAgICAgdGhlIHN1cnJvdW5kaW5nIGNvbnRleHQsIHRoZW4gaW52b2tlcyBgZm4odHgpYC4gU2VydmljZXMgbWlncmF0ZSBvbmVcbiAqICAgICAgYXQgYSB0aW1lIGJ5IHdyYXBwaW5nIHRoZWlyIGV4aXN0aW5nIGRyaXp6bGUgY2FsbHMgaW4gdGhpcyBoZWxwZXIg4oCUXG4gKiAgICAgIGBkYi5zZWxlY3QoKS5mcm9tKC4uLilgIGJlY29tZXMgYHdpdGhUZW5hbnRUeCh0eCA9PiB0eC5zZWxlY3QoKS5mcm9tKC4uLikpYC5cbiAqXG4gKiAgIDMuIGBydW5XaXRoVGVuYW50Q29udGV4dChjdHgsIGZuKWAgaXMgdGhlIEV4cHJlc3MtbWlkZGxld2FyZS1zaWRlXG4gKiAgICAgIGJvb2tlbmQgdGhhdCBlc3RhYmxpc2hlcyB0aGUgQXN5bmNMb2NhbFN0b3JhZ2Ugc2NvcGUgZm9yIHRoZSByZXF1ZXN0LlxuICpcbiAqIE1pZ3JhdGlvbiBvcmRlcjpcbiAqICAgLSBGaXJzdCBhZG9wdGVyczogbmV3bHktd3JpdHRlbiBzZXJ2aWNlcyAoZGFzaGJvYXJkLCBhbGVydC1kZXN0aW5hdGlvbilcbiAqICAgICB3aGVyZSB0aGUgY2hhbmdlIGlzIG1lY2hhbmljYWwuXG4gKiAgIC0gVGhlbiBoaWdoLXdyaXRlIHNlcnZpY2VzIChwbHVnaW4sIHBpcGVsaW5lKS4gU29hayBlYWNoIG9uZSBiZWZvcmVcbiAqICAgICBmbGlwcGluZyB0aGUgdW5kZXJseWluZyB0YWJsZSB0byBGT1JDRS5cbiAqICAgLSBhZG1pbl9hdWRpdF9sb2cgdXNlcyBhIHN5c2FkbWluLW9ubHkgcG9saWN5IOKAlCBzeXNhZG1pbiBwYXRocyBtdXN0XG4gKiAgICAgYWxzbyBzZXQgYGFwcC5pc19zeXNhZG1pbiA9ICd0cnVlJ2Agb3IgdGhleSdsbCBsb3NlIGFjY2VzcyB0byBpdC5cbiAqL1xuXG5pbXBvcnQgeyBBc3luY0xvY2FsU3RvcmFnZSB9IGZyb20gJ25vZGU6YXN5bmNfaG9va3MnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyLCBlbWl0Q291bnRlciwgZW52SW50IH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHsgc3FsIH0gZnJvbSAnZHJpenpsZS1vcm0nO1xuaW1wb3J0IHsgZGIgfSBmcm9tICcuL3Bvc3RncmVzLWNvbm5lY3Rpb24uanMnO1xuaW1wb3J0IHsgZ2V0UmVhZERiLCBnZXRSZWFkUG9vbCB9IGZyb20gJy4vcmVhZC1jb25uZWN0aW9uLmpzJztcbmltcG9ydCB7IGxhZ1ZlcmRpY3QsIG1heWJlU2FtcGxlTGFnLCB0eXBlIExhZ1ZlcmRpY3QgfSBmcm9tICcuL3JlYWQtbGFnLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCd0ZW5hbnQtY29udGV4dCcpO1xuXG4vKipcbiAqIEJlaGF2aW9yIHdoZW4gYHdpdGhUZW5hbnRUeGAgaXMgaW52b2tlZCBvdXRzaWRlIGFueSBgcnVuV2l0aFRlbmFudENvbnRleHRgXG4gKiBzY29wZS4gVGhlIGVmZmVjdGl2ZSBkZWZhdWx0IGlzIGZhaWwtZmFzdCAoJ3N0cmljdCcpIGluIHByb2R1Y3Rpb24gc28gYVxuICogbWlzc2luZyB0ZW5hbnQgc2NvcGUgc3VyZmFjZXMgYXQgdGhlIGJhZCBjYWxsIHNpdGUgaW5zdGVhZCBvZiBzaWxlbnRseVxuICogcnVubmluZyB3aXRoIGVtcHR5IFJMUyBHVUNzOyBvdXRzaWRlIHByb2R1Y3Rpb24gaXQgZGVmYXVsdHMgdG8gJ3dhcm4nIHNvXG4gKiB1bi1hdWRpdGVkIGRldi90ZXN0IHBhdGhzIGtlZXAgd29ya2luZyBidXQgbG9nIGFuIGFjdGlvbmFibGUgdHJhY2UuIEFuXG4gKiBleHBsaWNpdCBSTFNfQ09OVEVYVF9NT0RFIGVudiBvdmVycmlkZSBhbHdheXMgd2lucy5cbiAqXG4gKiAnc2lsZW50JyBpcyBrZXB0IGZvciB0ZXN0cyBhbmQgc2NyaXB0cyB0aGF0IGludGVudGlvbmFsbHkgZW50ZXIgdGhlIERCXG4gKiB3aXRob3V0IGNvbnRleHQgKGUuZy4gaW50ZWdyYXRpb24gdGVzdCBzZXR1cCB0aGF0IHJ1bnMgYXMgdGhlIGNvbm5lY3Rpb25cbiAqIG93bmVyIGJlZm9yZSBhbnkgdGVuYW50IGlzIHByb3Zpc2lvbmVkKS5cbiAqL1xudHlwZSBDb250ZXh0TW9kZSA9ICdzaWxlbnQnIHwgJ3dhcm4nIHwgJ3N0cmljdCc7XG5mdW5jdGlvbiBnZXRDb250ZXh0TW9kZSgpOiBDb250ZXh0TW9kZSB7XG4gIGNvbnN0IHJhdyA9IHByb2Nlc3MuZW52LlJMU19DT05URVhUX01PREU/LnRvTG93ZXJDYXNlKCk7XG4gIGlmIChyYXcgPT09ICdzaWxlbnQnIHx8IHJhdyA9PT0gJ3dhcm4nIHx8IHJhdyA9PT0gJ3N0cmljdCcpIHJldHVybiByYXc7XG4gIC8vIE5vIGV4cGxpY2l0IG92ZXJyaWRlOiBmYWlsLWZhc3QgaW4gcHJvZHVjdGlvbiwgd2FybiBldmVyeXdoZXJlIGVsc2UuXG4gIHJldHVybiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ3Byb2R1Y3Rpb24nID8gJ3N0cmljdCcgOiAnd2Fybic7XG59XG5cbi8qKlxuICogUGVyLXN0YXRlbWVudCBzZXJ2ZXItc2lkZSB0aW1lb3V0IChtcykgYXBwbGllZCBJTlNJREUgZXZlcnkgYHdpdGhUZW5hbnRUeGBcbiAqIHRyYW5zYWN0aW9uLCBuZXh0IHRvIHRoZSBSTFMgR1VDcy4gYHdpdGhUZW5hbnRUeGAgaXMgdGhlIHJlYWwgcXVlcnkgcGF0aCBmb3JcbiAqIGFsbCBSTFMvQ1JVRCByZWFkcyBhbmQgd3JpdGVzLCBzbyB0aGlzIGlzIHdoZXJlIHRoZSBEQi1zaWRlIGd1YXJkIGJlbG9uZ3MgKHRoZVxuICogb2xkIGBDb25uZWN0aW9uLnRyYW5zYWN0aW9uYCB3cmFwcGVyIHRoYXQgc2V0IGl0IGlzIGRlYWQg4oCUIHNlZVxuICogcG9zdGdyZXMtY29ubmVjdGlvbi50cykuIEJvdW5kcyBhIHJ1bmF3YXkgcXVlcnkgc28gaXQgY2FuJ3QgaG9sZCBhIHBvb2xlZFxuICogY29ubmVjdGlvbiBvcGVuIGluZGVmaW5pdGVseS4gRW52LWNvbmZpZ3VyYWJsZSB2aWEgYERCX1NUQVRFTUVOVF9USU1FT1VUX01TYFxuICogKGRlZmF1bHQgMzBzKTsgc2V0IHRvIGAwYCB0byBkaXNhYmxlIHRoZSB0aW1lb3V0LlxuICovXG5mdW5jdGlvbiBnZXRTdGF0ZW1lbnRUaW1lb3V0TXMoKTogbnVtYmVyIHtcbiAgY29uc3QgbXMgPSBlbnZJbnQoJ0RCX1NUQVRFTUVOVF9USU1FT1VUX01TJywgMzBfMDAwKTtcbiAgcmV0dXJuIG1zID49IDAgPyBtcyA6IDMwXzAwMDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUZW5hbnRDb250ZXh0IHtcbiAgLyoqIENhbGxlcidzIG9yZy4gVW5kZWZpbmVkIGZvciB1bi1hdXRoZW50aWNhdGVkIC8gc3lzdGVtIGpvYnMuICovXG4gIG9yZ0lkPzogc3RyaW5nO1xuICAvKiogQ2FsbGVyJ3MgdXNlciBpZC4gTm90IHVzZWQgZm9yIFJMUyDigJQgUG9zdGdyZXMgcG9saWNpZXMgYXJlIG9yZy1ncmFpbmVkLFxuICAgKiAgd2l0aCBubyBwZXItdXNlciBzZXNzaW9uIEdVQyDigJQgYnV0IGNhcnJpZWQgc28gYXBwLWxheWVyIHByZWRpY2F0ZXMgd2l0aCBhXG4gICAqICBQRVItVVNFUiBydW5nIChwaXBlbGluZSB0ZW1wbGF0ZXMnIGBwcml2YXRlYCB2aXNpYmlsaXR5KSBjYW4gcmVzb2x2ZSBcIndob1xuICAgKiAgaXMgYXNraW5nXCIgb24gcGF0aHMgdGhhdCBoYXZlIG5vd2hlcmUgdG8gcGFzcyBpdCwgbm90YWJseSBDcnVkU2VydmljZSdzXG4gICAqICBgd3JpdGVDb25kaXRpb25zYC4gQWJzZW50IGZvciBzeXN0ZW0gam9icywgd2hpY2ggaXMgdGhlIGZhaWwtY2xvc2VkIGNhc2U6XG4gICAqICBubyB2aWV3ZXIg4oeSIG5vIHByaXZhdGUgcm93cy4gKi9cbiAgdXNlcklkPzogc3RyaW5nO1xuICAvKiogVHJ1ZSB3aGVuIHRoZSBjYWxsZXIgaXMgYSBzeXNhZG1pbiAoc3lzdGVtLW9yZyBhZG1pbikuIEJ5cGFzc2VzIFJMU1xuICAgKiAgcG9saWNpZXMgdmlhIHRoZSBzeXNhZG1pbi1ieXBhc3MgYnJhbmNoIGluIGBjdXJyZW50X2lzX3N5c2FkbWluKClgLiAqL1xuICBpc1N1cGVyQWRtaW46IGJvb2xlYW47XG4gIC8qKiBPcmcg4oaSIHRlYW0gaGllcmFyY2h5OiB0aGUgY2FsbGVyJ3MgYWN0aXZlLW9yZyBwYXJlbnQgKHByZXNlbnQgb25seSB3aGVuIHRoZVxuICAgKiAgYWN0aXZlIG9yZyBpcyBhIHRlYW0pLiBOb3QgdXNlZCBmb3IgUkxTOyBjYXJyaWVkIHNvIGRvd25zdHJlYW0gc2lkZS1lZmZlY3RzXG4gICAqICAoZS5nLiBlbnRpdHktZXZlbnQgY29tcGxpYW5jZSBldmFsKSBjYW4gaG9ub3IgcGFyZW50IGBwcm9wYWdhdGVUb0NoaWxkcmVuYFxuICAgKiAgcnVsZXMgd2l0aG91dCBhIHJlcXVlc3QgaW4gc2NvcGUuICovXG4gIHBhcmVudE9yZ0lkPzogc3RyaW5nO1xuICAvKipcbiAgICogU2V0IG9uY2UgdGhpcyByZXF1ZXN0IGhhcyBvcGVuZWQgYSBQUklNQVJZIHRyYW5zYWN0aW9uLCBpLmUuIG1heSBoYXZlXG4gICAqIHdyaXR0ZW4uIFN1YnNlcXVlbnQgYHdpdGhUZW5hbnRUeFJlYWRgIGNhbGxzIGluIHRoZSBzYW1lIHJlcXVlc3QgdGhlbiBnbyB0b1xuICAgKiB0aGUgcHJpbWFyeSB0b28sIHNvIGEgaGFuZGxlciBjYW5ub3QgcmVhZCBpdHMgb3duIHdyaXRlIG9mZiBhIHJlcGxpY2EgdGhhdFxuICAgKiBoYXMgbm90IHJlcGxheWVkIGl0IHlldC5cbiAgICpcbiAgICogRGVsaWJlcmF0ZWx5IHNldCBieSBgd2l0aFRlbmFudFR4YCByYXRoZXIgdGhhbiBieSB3cml0ZSBjYWxsIHNpdGVzOiB0aGF0XG4gICAqIHBhdGggaXMgdGhlIG9uZSB0aGF0IGNhbiB3cml0ZSwgaXQgbmVlZHMgbm8gcGVyLWNhbGwtc2l0ZSBkaXNjaXBsaW5lLCBhbmRcbiAgICogYmVpbmcgd3JvbmcgaW4gdGhpcyBkaXJlY3Rpb24gb25seSBjb3N0cyBhIHJlcGxpY2EgaGl0LiBNdXRhdGVkIGluIHBsYWNlIG9uXG4gICAqIHRoZSBBc3luY0xvY2FsU3RvcmFnZSBzdG9yZSDigJQgdGhlIHN0b3JlIGlzIHBlci1yZXF1ZXN0LCBzbyB0aGlzIGNhbm5vdCBsZWFrXG4gICAqIGJldHdlZW4gcmVxdWVzdHMuXG4gICAqXG4gICAqIFNDT1BFOiB0aGlzIGNvdmVycyByZWFkcyBBRlRFUiBhIHdyaXRlIElOIFRIRSBTQU1FIFJFUVVFU1QuIEl0IGRvZXMgTk9UXG4gICAqIGNvdmVyIHRoZSBuZXh0IHJlcXVlc3QgKFBPU1QgdGhlbiBhIHBhZ2UgcmVsb2FkIGlzc3VpbmcgR0VUKS4gVGhhdCBuZWVkc1xuICAgKiBzaGFyZWQgc3RhdGUg4oCUIHNlZSB0aGUgY3Jvc3MtcmVxdWVzdCBzZWN0aW9uIG9mXG4gICAqIGRvY3MvcGxhbnMvcG9zdGdyZXMtcmVhZC13cml0ZS1zcGxpdC5tZCDigJQgYW5kIG11c3QgYmUgc2V0dGxlZCBiZWZvcmUgY2FsbFxuICAgKiBzaXRlcyBhcmUgbWlncmF0ZWQgaW4gYnVsay5cbiAgICovXG4gIG1heUhhdmVXcml0dGVuPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBQZXItcmVxdWVzdCB0ZW5hbnQgY29udGV4dC4gUG9wdWxhdGVkIGJ5IEV4cHJlc3MgbWlkZGxld2FyZSBvbmNlIHRoZSBKV1RcbiAqIGlzIHZhbGlkYXRlZDsgY29uc3VtZWQgYnkgYHdpdGhUZW5hbnRUeGAgYW5kIGFueSBoZWxwZXIgdGhhdCBuZWVkcyB0b1xuICoga25vdyBcIndobyBpcyB0aGUgY2FsbGVyXCIgd2l0aG91dCByZS1kZXJpdmluZyBmcm9tIHRoZSByZXF1ZXN0IG9iamVjdC5cbiAqL1xuZXhwb3J0IGNvbnN0IHRlbmFudENvbnRleHQgPSBuZXcgQXN5bmNMb2NhbFN0b3JhZ2U8VGVuYW50Q29udGV4dD4oKTtcblxuLyoqXG4gKiBSdW4gYGZuYCBpbnNpZGUgdGhlIGdpdmVuIHRlbmFudCBzY29wZS4gVGhlIEFzeW5jTG9jYWxTdG9yYWdlIHNjb3BlXG4gKiBzdXJ2aXZlcyBhY3Jvc3MgYXN5bmMgYm91bmRhcmllcyAoUHJvbWlzZXMsIHNldFRpbWVvdXQsIGV0Yy4pLCBzbyBhbnlcbiAqIGB3aXRoVGVuYW50VHhgIGNhbGwgaW5zaWRlIGBmbmAg4oCUIGRpcmVjdGx5IG9yIHRocm91Z2ggYW55IGRlcHRoIG9mXG4gKiBhc3luYyBoZWxwZXJzIOKAlCBwaWNrcyB1cCBgY3R4YC5cbiAqXG4gKiBXcmFwIGBuZXh0KClgIGZyb20gYW4gRXhwcmVzcyBtaWRkbGV3YXJlIHRvIGVzdGFibGlzaCBwZXItcmVxdWVzdCBzY29wZTpcbiAqXG4gKiAgICAgYXBwLnVzZSgocmVxLCByZXMsIG5leHQpID0+IHtcbiAqICAgICAgIHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgb3JnSWQsIGlzU3VwZXJBZG1pbiB9LCAoKSA9PiBuZXh0KCkpO1xuICogICAgIH0pO1xuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuV2l0aFRlbmFudENvbnRleHQ8VD4oY3R4OiBUZW5hbnRDb250ZXh0LCBmbjogKCkgPT4gVCk6IFQge1xuICByZXR1cm4gdGVuYW50Q29udGV4dC5ydW4oY3R4LCBmbik7XG59XG5cbi8qKiBSZWFkIHRoZSBjdXJyZW50IHRlbmFudCBjb250ZXh0LiBSZXR1cm5zIHVuZGVmaW5lZCBvdXRzaWRlIGFuXG4gKiAgYHJ1bldpdGhUZW5hbnRDb250ZXh0YCBzY29wZSAoZS5nLiBiYWNrZ3JvdW5kIHdvcmtlcnMsIG1pZ3JhdGlvbnMpLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFRlbmFudENvbnRleHQoKTogVGVuYW50Q29udGV4dCB8IHVuZGVmaW5lZCB7XG4gIHJldHVybiB0ZW5hbnRDb250ZXh0LmdldFN0b3JlKCk7XG59XG5cbi8qKlxuICogT3BlbiBhIHRyYW5zYWN0aW9uIHdpdGggUkxTIEdVQ3MgU0VUIExPQ0FMIGZyb20gdGhlIGN1cnJlbnRcbiAqIEFzeW5jTG9jYWxTdG9yYWdlIHRlbmFudCBjb250ZXh0LCB0aGVuIGludm9rZSBgZm4odHgpYC5cbiAqXG4gKiAtIFdoZW4gY2FsbGVkIG91dHNpZGUgYSB0ZW5hbnQgc2NvcGUgKGUuZy4gZnJvbSBhIGJhY2tncm91bmQgam9iIHRoYXRcbiAqICAgZG9lc24ndCBoYXZlIGEgY2FsbGVyIGlkZW50aXR5KSwgdGhlIHRyYW5zYWN0aW9uIGlzIG9wZW5lZCB3aXRoIGJvdGhcbiAqICAgR1VDcyBjbGVhcmVkIOKAlCBSTFMgcG9saWNpZXMgd2lsbCByZXR1cm4gemVybyByb3dzIG9uIEZPUkNFJ2QgdGFibGVzLlxuICogICBDYWxsZXJzIHRoYXQgbmVlZCBhIHN5c2FkbWluLWVxdWl2YWxlbnQgc2NvcGUgc2hvdWxkIHdyYXAgdGhlbXNlbHZlcyBpblxuICogICBgcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwg4oCmKWAgZXhwbGljaXRseS5cbiAqXG4gKiAtIFVzZXMgYHNldF9jb25maWcoa2V5LCB2YWx1ZSwgdHJ1ZSlgICh0aGUgYm9vbGVhbiA9IGBpc19sb2NhbGAsIGkuZS5cbiAqICAgdHJhbnNhY3Rpb24tc2NvcGVkKS4gRXF1aXZhbGVudCB0byBgU0VUIExPQ0FMYCBidXQgdGFrZXMgdGhlIHZhbHVlIGFzXG4gKiAgIGEgcGFyYW1ldGVyLCB3aGljaCBpcyB3aGF0IHdlIHdhbnQg4oCUIERyaXp6bGUncyBgc3FsYCB0ZW1wbGF0ZSBiaW5kcyB0aGVcbiAqICAgdmFsdWUgc2FmZWx5IHNvIGFuIGF0dGFja2VyLWNvbnRyb2xsZWQgYG9yZ0lkYCBjYW4ndCBlc2NhcGUgdGhlIHF1b3RpbmcuXG4gKlxuICogLSBSZXR1cm5zIHdoYXRldmVyIGBmbmAgcmV0dXJuczsgdGhyb3dzIHdoYXRldmVyIGBmbmAgdGhyb3dzIChEcml6emxlXG4gKiAgIGhhbmRsZXMgdGhlIENPTU1JVC9ST0xMQkFDSyBmb3IgdXMpLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gd2l0aFRlbmFudFR4PFQ+KFxuICBmbjogKHR4OiBQYXJhbWV0ZXJzPFBhcmFtZXRlcnM8dHlwZW9mIGRiLnRyYW5zYWN0aW9uPlswXT5bMF0pID0+IFByb21pc2U8VD4sXG4gIC8qKlxuICAgKiBJTlRFUk5BTC4gV2hldGhlciB0aGlzIHRyYW5zYWN0aW9uIHNob3VsZCBtYXJrIHRoZSByZXF1ZXN0IGFzIHBvc3NpYmx5XG4gICAqIGhhdmluZyB3cml0dGVuLiBBbHdheXMgdHJ1ZSBmb3IgYSByZWFsIGNhbGxlcjsgb25seSB3aXRoVGVuYW50VHhSZWFkJ3NcbiAgICogcHJpbWFyeS1mYWxsYmFjayBwYXRoIHBhc3NlcyBmYWxzZSwgYmVjYXVzZSBhIHJlYWQgdGhhdCBsYW5kcyBvbiB0aGVcbiAgICogcHJpbWFyeSBoYXMgbm90IHdyaXR0ZW4uIFNlZSB0aGUgY29tbWVudCBhdCB0aGUgYXNzaWdubWVudCBiZWxvdy5cbiAgICovXG4gIG1hcmtXcml0dGVuID0gdHJ1ZSxcbik6IFByb21pc2U8VD4ge1xuICBjb25zdCBjdHggPSB0ZW5hbnRDb250ZXh0LmdldFN0b3JlKCk7XG5cbiAgaWYgKCFjdHgpIHtcbiAgICAvLyBTdXJmYWNlIHRoZSBidWcgaW5zdGVhZCBvZiBzaWxlbnRseSBTRVQnaW5nIGVtcHR5IEdVQ3MuIFRoZSBkZWZhdWx0XG4gICAgLy8gbW9kZSAoJ3dhcm4nKSBsb2dzIGFuIGFjdGlvbmFibGUgdHJhY2Ugc28gdGhlIGJhZCBjYWxsIHNpdGUgaXMgZWFzeVxuICAgIC8vIHRvIGZpbmQ7IHByb2R1Y3Rpb24gZGVwbG95bWVudHMgdGhhdCBoYXZlIGZpbmlzaGVkIHRoZSBhdWRpdCBjYW4gc2V0XG4gICAgLy8gUkxTX0NPTlRFWFRfTU9ERT1zdHJpY3QgdG8gZmFpbC1mYXN0IGF0IHRoZSBjYWxsIHNpdGUgKGJldHRlciBzdGFja1xuICAgIC8vIHRyYWNlIHRoYW4gdGhlIFBvc3RncmVzIFwicGVybWlzc2lvbiBkZW5pZWRcIiB0aGF0IHdvdWxkIG90aGVyd2lzZVxuICAgIC8vIHN1cmZhY2UgZnJvbSBGT1JDRSdkIFJMUykuXG4gICAgY29uc3QgbW9kZSA9IGdldENvbnRleHRNb2RlKCk7XG4gICAgaWYgKG1vZGUgPT09ICdzdHJpY3QnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICd3aXRoVGVuYW50VHggY2FsbGVkIG91dHNpZGUgYSB0ZW5hbnQgc2NvcGUuIFdyYXAgeW91ciBoYW5kbGVyL3dvcmtlciAnXG4gICAgICAgICsgJ2luIHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgb3JnSWQsIGlzU3VwZXJBZG1pbiB9LCAuLi4pIGJlZm9yZSBpbnZva2luZyAnXG4gICAgICAgICsgJ2FueSBzZXJ2aWNlIHRoYXQgdG91Y2hlcyBSTFMtZW5mb3JjZWQgdGFibGVzLicsXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAobW9kZSA9PT0gJ3dhcm4nKSB7XG4gICAgICAvLyBJbmNsdWRlIGEgc3ludGhldGljIHN0YWNrIHNvIHRoZSBvZmZlbmRpbmcgY2FsbCBzaXRlIGlzIGluIHRoZSBsb2cuXG4gICAgICBsb2dnZXIud2Fybignd2l0aFRlbmFudFR4IGNhbGxlZCBvdXRzaWRlIGEgdGVuYW50IHNjb3BlOyBSTFMgR1VDcyB3aWxsIGJlIGVtcHR5Jywge1xuICAgICAgICBzdGFjazogbmV3IEVycm9yKCd0ZW5hbnQtY29udGV4dCBtaXNzaW5nJykuc3RhY2ssXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICBjb25zdCBvcmdJZCA9IGN0eD8ub3JnSWQgPz8gJyc7XG4gIGNvbnN0IGlzU3VwZXJBZG1pbiA9IGN0eD8uaXNTdXBlckFkbWluID8gJ3RydWUnIDogJ2ZhbHNlJztcblxuICBjb25zdCBzdGF0ZW1lbnRUaW1lb3V0TXMgPSBnZXRTdGF0ZW1lbnRUaW1lb3V0TXMoKTtcblxuICAvLyBUaGlzIHRyYW5zYWN0aW9uIHJ1bnMgb24gdGhlIFBSSU1BUlkgYW5kIG1heSB3cml0ZSwgc28gYW55IGxhdGVyIHJlYWQgaW5cbiAgLy8gdGhpcyByZXF1ZXN0IG11c3Qgc3RheSBvbiB0aGUgcHJpbWFyeSB0b28uIFNldCBiZWZvcmUgdGhlIHRyYW5zYWN0aW9uIG9wZW5zXG4gIC8vIHJhdGhlciB0aGFuIGFmdGVyIGl0IGNvbW1pdHM6IGEgcmVhZCBpc3N1ZWQgY29uY3VycmVudGx5IGZyb20gdGhlIHNhbWVcbiAgLy8gaGFuZGxlciB3b3VsZCBvdGhlcndpc2UgcmFjZSB0aGUgZmxhZy5cbiAgLy9cbiAgLy8gYG1hcmtXcml0dGVuOiBmYWxzZWAgaXMgcGFzc2VkIE9OTFkgYnkgd2l0aFRlbmFudFR4UmVhZCdzIGZhbGxiYWNrIHBhdGgsXG4gIC8vIGFuZCBpdCBpcyBub3QgYW4gb3B0aW1pc2F0aW9uIOKAlCB3aXRob3V0IGl0IHRoZSBmZWF0dXJlIGRlZmVhdHMgaXRzZWxmLiBUaGF0XG4gIC8vIHBhdGggcnVucyBvbiB0aGUgcHJpbWFyeSwgc28gaXQgd291bGQgc2V0IHRoaXMgZmxhZywgd2hpY2ggYXBpLXNlcnZlcidzXG4gIC8vIG1pZGRsZXdhcmUgcmVhZHMgYWZ0ZXIgdGhlIHJlc3BvbnNlIHRvIHNldCB0aGUgUmVkaXMgd3JpdGUgcGluLiBBIHB1cmVseVxuICAvLyBSRUFELU9OTFkgcmVxdWVzdCB3b3VsZCB0aGVyZWZvcmUgcGluIGl0cyB3aG9sZSBvcmcgdG8gdGhlIHByaW1hcnkgZm9yIHRoZVxuICAvLyBwaW4gVFRMLCBldmVyeSB0aW1lLCBhbmQgc2luY2UgcmVhZHMgZmFsbCBiYWNrIHdoZW5ldmVyIG5vIHJlcGxpY2EgaXNcbiAgLy8gY29uZmlndXJlZCAodGhlIHNoaXBwaW5nIGRlZmF1bHQpIGV2ZXJ5IG9yZyB3b3VsZCBiZSBwZXJtYW5lbnRseSBwaW5uZWQuXG4gIC8vIFRoZSBmbGFnIG1lYW5zIFwidGhpcyByZXF1ZXN0IG1heSBoYXZlIFdSSVRURU5cIiwgbm90IFwidGhpcyByZXF1ZXN0IHRvdWNoZWRcbiAgLy8gdGhlIHByaW1hcnlcIiwgYW5kIHRoZSBkaWZmZXJlbmNlIGlzIHRoZSB3aG9sZSByZWFkLW9mZmxvYWQgYmVuZWZpdC5cbiAgaWYgKGN0eCAmJiBtYXJrV3JpdHRlbikgY3R4Lm1heUhhdmVXcml0dGVuID0gdHJ1ZTtcblxuICByZXR1cm4gZGIudHJhbnNhY3Rpb24oYXN5bmMgKHR4KSA9PiB7XG4gICAgLy8gU2V0IGV2ZXJ5IHRyYW5zYWN0aW9uLXNjb3BlZCBHVUMgaW4gYSBTSU5HTEUgcm91bmQtdHJpcC4gYHNldF9jb25maWcoKWBcbiAgICAvLyByZXR1cm5zIGl0cyB2YWx1ZSwgc28gbXVsdGlwbGUgY2FsbHMgY29tcG9zZSBpbiBvbmUgU0VMRUNUIHJhdGhlciB0aGFuIGFcbiAgICAvLyByb3VuZC10cmlwIHBlciBHVUMg4oCUIGVhY2ggYSBuZXR3b3JrIGhvcCB0aGF0IHRheGVzIHRoZSBzaGFyZWQgUGdCb3VuY2VyXG4gICAgLy8gcG9vbCBvbiB0aGUgaG90IHBhdGguIGB0cnVlYFxuICAgIC8vID0gaXNfbG9jYWwgKFNFVCBMT0NBTCBzZW1hbnRpY3M6IGF1dG8tcmVsZWFzZWQgb24gQ09NTUlUL1JPTExCQUNLKS4gVGhlXG4gICAgLy8gZHJpdmVyIGJpbmRzIHRoZSB2YWx1ZXMgYXMgcGFyYW1ldGVycywgc28gYSBob3N0aWxlIG9yZ19pZCBjYW4ndCBicmVhayBvdXRcbiAgICAvLyBvZiB0aGUgR1VDIHN5bnRheC5cbiAgICAvL1xuICAgIC8vIFRoZSBzdGF0ZW1lbnRfdGltZW91dCBHVUMgYm91bmRzIGEgcnVuYXdheSBxdWVyeSBzbyBpdCBjYW4ndCBob2xkIGEgcG9vbGVkXG4gICAgLy8gY29ubmVjdGlvbiBvcGVuIGluZGVmaW5pdGVseSAoYFNFVCBMT0NBTCBzdGF0ZW1lbnRfdGltZW91dCA9ICQxYCBpcyByZWplY3RlZFxuICAgIC8vIGJ5IFBvc3RncmVzLCBoZW5jZSBzZXRfY29uZmlnIHdpdGggYSBib3VuZCBtcyB2YWx1ZSkuIEluY2x1ZGVkIGluIHRoZSBzYW1lXG4gICAgLy8gc3RhdGVtZW50IHdoZW4gZW5hYmxlZCAoPjApLCBkcm9wcGVkIGVudGlyZWx5IHdoZW4gZGlzYWJsZWQgKDApLlxuICAgIGlmIChzdGF0ZW1lbnRUaW1lb3V0TXMgPiAwKSB7XG4gICAgICBhd2FpdCB0eC5leGVjdXRlKFxuICAgICAgICBzcWxgU0VMRUNUIHNldF9jb25maWcoJ2FwcC5vcmdfaWQnLCAke29yZ0lkfSwgdHJ1ZSksIHNldF9jb25maWcoJ2FwcC5pc19zeXNhZG1pbicsICR7aXNTdXBlckFkbWlufSwgdHJ1ZSksIHNldF9jb25maWcoJ3N0YXRlbWVudF90aW1lb3V0JywgJHtTdHJpbmcoc3RhdGVtZW50VGltZW91dE1zKX0sIHRydWUpYCxcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IHR4LmV4ZWN1dGUoXG4gICAgICAgIHNxbGBTRUxFQ1Qgc2V0X2NvbmZpZygnYXBwLm9yZ19pZCcsICR7b3JnSWR9LCB0cnVlKSwgc2V0X2NvbmZpZygnYXBwLmlzX3N5c2FkbWluJywgJHtpc1N1cGVyQWRtaW59LCB0cnVlKWAsXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gZm4odHgpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBXaHkgYSByZWFkIHdhcyBvciB3YXMgbm90IHNlbnQgdG8gdGhlIHJlcGxpY2EuIFJldHVybmVkIGJ5XG4gKiB7QGxpbmsgY2hvb3NlUmVhZFRhcmdldH0gc28gdGhlIGRlY2lzaW9uIGlzIGluc3BlY3RhYmxlIOKAlCBhbmQgdGVzdGFibGVcbiAqIHdpdGhvdXQgYSByZXBsaWNhLCB3aGljaCBtYXR0ZXJzIGJlY2F1c2UgdGhlIHRlc3Qgc3Vic3RyYXRlIChQR2xpdGUpIGhhcyBub1xuICogcmVwbGljYXRpb24gYXQgYWxsLlxuICovXG5leHBvcnQgdHlwZSBSZWFkVGFyZ2V0UmVhc29uID1cbiAgLyoqIE5vIHJlcGxpY2EgY29uZmlndXJlZCwgb3IgREJfUkVBRF9FTkFCTEVEPWZhbHNlLiAqL1xuICB8ICduby1yZXBsaWNhJ1xuICAvKiogVGhpcyByZXF1ZXN0IGFscmVhZHkgb3BlbmVkIGEgcHJpbWFyeSB0cmFuc2FjdGlvbiBhbmQgbWF5IGhhdmUgd3JpdHRlbi4gKi9cbiAgfCAnbWF5LWhhdmUtd3JpdHRlbidcbiAgLyoqXG4gICAqIE5vIHRlbmFudCBjb250ZXh0LiBBIGNhbGxlciBvdXRzaWRlIGBydW5XaXRoVGVuYW50Q29udGV4dGAgaGFzIG5vIG9yZyB0b1xuICAgKiBzY29wZSBSTFMgYnkgYW5kIG5vIHdhdGVybWFyayB0byBjb21wYXJlLCBzbyBpdCBnb2VzIHRvIHRoZSBwcmltYXJ5IOKAlCB3aGVyZVxuICAgKiBgd2l0aFRlbmFudFR4YCdzIFJMU19DT05URVhUX01PREUgZ3VhcmQgbG9ncyBvciB0aHJvd3MuIFJvdXRlZCB0byBhIHJlcGxpY2FcbiAgICogaW5zdGVhZCwgdGhlIHNhbWUgY2FsbCB3b3VsZCBzZXQgYGFwcC5vcmdfaWQgPSAnJ2Agd2l0aCBubyBkaWFnbm9zdGljIGF0IGFsbC5cbiAgICovXG4gIHwgJ25vLWNvbnRleHQnXG4gIC8qKlxuICAgKiBBIHJlcGxpY2EgZXhpc3RzIGJ1dCBpdHMgcmVwbGF5IGxhZyBpcyBiZXlvbmQgYERCX1JFQURfTUFYX0xBR19TRUNPTkRTYCxcbiAgICogb3IgaXMgVU5LTk9XTiAobm8gc2FtcGxlIHlldCwgdGhlIHNhbXBsZSBmYWlsZWQsIG9yIHRoZSBjYWNoZWQgc2FtcGxlIGhhc1xuICAgKiBnb25lIHN0YWxlKS4gVW5rbm93biBhbmQgbGFnZ2luZyBhcmUgdGhlIHNhbWUgZGVjaXNpb24gb24gcHVycG9zZTogdGhlXG4gICAqIGNpcmN1aXQgZmFpbHMgY2xvc2VkLlxuICAgKi9cbiAgfCAncmVwbGljYS1sYWdnaW5nJ1xuICAvKiogUm91dGVkIHRvIHRoZSByZXBsaWNhLiAqL1xuICB8ICdyZXBsaWNhJztcblxuZXhwb3J0IGludGVyZmFjZSBSZWFkVGFyZ2V0IHtcbiAgdXNlUmVwbGljYTogYm9vbGVhbjtcbiAgcmVhc29uOiBSZWFkVGFyZ2V0UmVhc29uO1xufVxuXG4vKipcbiAqIERlY2lkZSB3aGVyZSBhIHJlYWQtb25seSB0cmFuc2FjdGlvbiBzaG91bGQgcnVuLlxuICpcbiAqIFB1cmUgYW5kIHNpZGUtZWZmZWN0IGZyZWUgb24gcHVycG9zZTogdGhpcyBpcyB0aGUgd2hvbGUgb2YgdGhlIHJvdXRpbmdcbiAqIHBvbGljeSwgYW5kIGl0IGlzIHRoZSBwYXJ0IHdvcnRoIHRlc3RpbmcuIFJlcGxpY2F0aW9uIGl0c2VsZiBjYW5ub3QgYmVcbiAqIHJlcHJvZHVjZWQgbG9jYWxseSwgc28gdGhlIHRlc3RhYmxlIHVuaXQgaXMgdGhlIERFQ0lTSU9OLCBub3QgdGhlIGxhZy5cbiAqXG4gKiBUaGUgYmlhcyBpcyBkZWxpYmVyYXRlOiBhbnl0aGluZyB1bmNlcnRhaW4gcmVzb2x2ZXMgdG8gdGhlIHByaW1hcnkuIEEgcmVhZFxuICogc2VydmVkIGJ5IHRoZSBwcmltYXJ5IGlzIG1lcmVseSBsZXNzIGVmZmljaWVudDsgYSByZWFkIHNlcnZlZCBieSBhIHN0YWxlXG4gKiByZXBsaWNhIGlzIGEgdXNlci12aXNpYmxlIGJ1ZyBub2JvZHkgY2FuIHJlcHJvZHVjZS5cbiAqL1xuLyoqIEV2ZXJ5dGhpbmcgYWJvdXQgdGhlIHJlcGxpY2EgdGhhdCBpcyBub3QgcmVxdWVzdC1zcGVjaWZpYy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVhZFJlcGxpY2FIZWFsdGgge1xuICAvKiogQSByZXBsaWNhIHBvb2wgZXhpc3RzIEFORCBgREJfUkVBRF9FTkFCTEVEYCBpcyBub3QgZmFsc2UuICovXG4gIGF2YWlsYWJsZTogYm9vbGVhbjtcbiAgLyoqIFRoZSBsYWcgY2lyY3VpdCdzIHZlcmRpY3QuIGB1bmtub3duYCBpcyB0cmVhdGVkIGV4YWN0bHkgbGlrZSBgbGFnZ2luZ2AuICovXG4gIGxhZzogTGFnVmVyZGljdDtcbn1cblxuLyoqXG4gKiBXaGVyZSBhIHJlYWQgZ29lcywgYW5kIHdoeS5cbiAqXG4gKiBTWVNURU0gZ2F0ZXMgYXJlIGNoZWNrZWQgYmVmb3JlIFJFUVVFU1QgZ2F0ZXMsIHdoaWNoIG1hdHRlcnMgb25seSBmb3IgdGhlXG4gKiBleHBsYW5hdGlvbiDigJQgYnV0IHRoZSBleHBsYW5hdGlvbiBpcyB3aGF0IGFuIG9wZXJhdG9yIHJlYWRzIHdoZW4gYXNraW5nIHdoeVxuICogdGhlIHJlcGxpY2EgaXMgaWRsZSwgYW5kIFwibXkgcmVxdWVzdCB3cm90ZVwiIGlzIGEgZmFyIGxlc3MgdXNlZnVsIGFuc3dlciB0aGFuXG4gKiBcInRoZSBzdGFuZGJ5IGlzIDQwIHNlY29uZHMgYmVoaW5kXCIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjaG9vc2VSZWFkVGFyZ2V0KGN0eDogVGVuYW50Q29udGV4dCB8IHVuZGVmaW5lZCwgaGVhbHRoOiBSZWFkUmVwbGljYUhlYWx0aCk6IFJlYWRUYXJnZXQge1xuICBpZiAoIWhlYWx0aC5hdmFpbGFibGUpIHJldHVybiB7IHVzZVJlcGxpY2E6IGZhbHNlLCByZWFzb246ICduby1yZXBsaWNhJyB9O1xuICBpZiAoaGVhbHRoLmxhZyAhPT0gJ29rJykgcmV0dXJuIHsgdXNlUmVwbGljYTogZmFsc2UsIHJlYXNvbjogJ3JlcGxpY2EtbGFnZ2luZycgfTtcbiAgaWYgKCFjdHgpIHJldHVybiB7IHVzZVJlcGxpY2E6IGZhbHNlLCByZWFzb246ICduby1jb250ZXh0JyB9O1xuICBpZiAoY3R4Lm1heUhhdmVXcml0dGVuKSByZXR1cm4geyB1c2VSZXBsaWNhOiBmYWxzZSwgcmVhc29uOiAnbWF5LWhhdmUtd3JpdHRlbicgfTtcbiAgcmV0dXJuIHsgdXNlUmVwbGljYTogdHJ1ZSwgcmVhc29uOiAncmVwbGljYScgfTtcbn1cblxuLyoqXG4gKiBPcGVuIGEgUkVBRC1PTkxZIHRyYW5zYWN0aW9uIHdpdGggdGhlIHNhbWUgUkxTIEdVQ3MgYHdpdGhUZW5hbnRUeGAgc2V0cyxcbiAqIHJvdXRlZCB0byB0aGUgcmVwbGljYSB3aGVuIHtAbGluayBjaG9vc2VSZWFkVGFyZ2V0fSBhbGxvd3MgaXQuXG4gKlxuICogYFNFVCBUUkFOU0FDVElPTiBSRUFEIE9OTFlgIGlzIHRoZSBndWFyZHJhaWwsIG5vdCBkZWNvcmF0aW9uLiBTZXZlcmFsIHF1ZXJpZXNcbiAqIGluIHRoaXMgY29kZWJhc2UgbG9vayBsaWtlIHJlYWRzIGF0IHRoZSBjYWxsIHNpdGUgYW5kIGFyZSBub3Qg4oCUXG4gKiBgU0VMRUNUIOKApiBGT1IgVVBEQVRFYCBpbiBjcnVkLXNlcnZpY2UncyBzZXREZWZhdWx0LCBgcGdfYWR2aXNvcnlfeGFjdF9sb2NrYFxuICogaW4gcGlwZWxpbmUtc2VydmljZSBhbmQgY29tcGxpYW5jZSdzIHN1YnNjcmlwdGlvbi1zZXJ2aWNlLiBSb3V0ZWQgdG8gYVxuICogc3RhbmRieSB0aG9zZSBmYWlsIGFueXdheTsgZGVjbGFyaW5nIHRoZSB0cmFuc2FjdGlvbiByZWFkLW9ubHkgbWFrZXMgdGhlbVxuICogZmFpbCBIRVJFLCB3aXRoIGEgY2xlYXIgbWVzc2FnZSwgaW5jbHVkaW5nIG9uIHRoZSBwcmltYXJ5IHdoZXJlIHRoZXkgd291bGRcbiAqIG90aGVyd2lzZSBzdWNjZWVkIGFuZCBxdWlldGx5IGFjcXVpcmUgYSBsb2NrIHRoZSBjYWxsZXIgZGlkIG5vdCBpbnRlbmQuXG4gKlxuICogRmFsbHMgYmFjayB0byB0aGUgcHJpbWFyeSB0cmFuc3BhcmVudGx5LCBzbyBhIGNhbGxlciBuZXZlciBoYXMgdG8gYXNrXG4gKiB3aGV0aGVyIGEgcmVwbGljYSBleGlzdHMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3aXRoVGVuYW50VHhSZWFkPFQ+KFxuICBmbjogKHR4OiBQYXJhbWV0ZXJzPFBhcmFtZXRlcnM8dHlwZW9mIGRiLnRyYW5zYWN0aW9uPlswXT5bMF0pID0+IFByb21pc2U8VD4sXG4pOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgY3R4ID0gdGVuYW50Q29udGV4dC5nZXRTdG9yZSgpO1xuICBjb25zdCByZWFkRGIgPSBnZXRSZWFkRGIoKTtcbiAgY29uc3QgcmVhZFBvb2wgPSBnZXRSZWFkUG9vbCgpO1xuXG4gIC8vIEtlZXAgdGhlIGxhZyBzYW1wbGUgd2FybS4gRmlyZS1hbmQtZm9yZ2V0IGJ5IGRlc2lnbjogaXQgaXMgZGVib3VuY2VkIHRvXG4gIC8vIERCX1JFQURfTEFHX1NBTVBMRV9NUywgbmV2ZXIgdGhyb3dzLCBhbmQgbXVzdCBub3QgYWRkIGEgcm91bmQtdHJpcCB0byB0aGlzXG4gIC8vIHJlYWQuIFRoZSBmaXJzdCByZWFkIG9mIGEgcHJvY2VzcyB0aGVyZWZvcmUgcm91dGVzIHRvIHRoZSBwcmltYXJ5IChub1xuICAvLyBzYW1wbGUgeWV0KSBhbmQgd2FybXMgdGhlIGNpcmN1aXQgZm9yIHRoZSBvbmVzIGFmdGVyIGl0LlxuICBpZiAocmVhZFBvb2wpIHZvaWQgbWF5YmVTYW1wbGVMYWcocmVhZFBvb2wpO1xuXG4gIGNvbnN0IHRhcmdldCA9IGNob29zZVJlYWRUYXJnZXQoY3R4LCB7IGF2YWlsYWJsZTogcmVhZERiICE9PSBudWxsLCBsYWc6IGxhZ1ZlcmRpY3QoKSB9KTtcbiAgZW1pdENvdW50ZXIoJ3Bvc3RncmVzX3JlYWRfcm91dGVkX3RvdGFsJywgeyB0YXJnZXQ6IHRhcmdldC51c2VSZXBsaWNhID8gJ3JlcGxpY2EnIDogJ3ByaW1hcnknLCByZWFzb246IHRhcmdldC5yZWFzb24gfSk7XG5cbiAgaWYgKCF0YXJnZXQudXNlUmVwbGljYSkge1xuICAgIC8vIFByaW1hcnkgcGF0aDogaWRlbnRpY2FsIHNlbWFudGljcyB0byB3aXRoVGVuYW50VHgsIHBsdXMgdGhlIHJlYWQtb25seVxuICAgIC8vIGRlY2xhcmF0aW9uIHNvIGFuIGFjY2lkZW50YWwgd3JpdGUgaXMgY2F1Z2h0IG9uIGV2ZXJ5IHRhcmdldCDigJQgaW5jbHVkaW5nXG4gICAgLy8gdGhlIG9uZXMgdGhhdCB3aWxsIG5ldmVyIGhhdmUgYSByZXBsaWNhLlxuICAgIHJldHVybiB3aXRoVGVuYW50VHgoYXN5bmMgKHR4KSA9PiB7XG4gICAgICBhd2FpdCB0eC5leGVjdXRlKHNxbGBTRVQgVFJBTlNBQ1RJT04gUkVBRCBPTkxZYCk7XG4gICAgICByZXR1cm4gZm4odHgpO1xuICAgIH0sIC8qIG1hcmtXcml0dGVuICovIGZhbHNlKTtcbiAgfVxuXG4gIC8vIE5vbi1udWxsIGJ5IGNvbnN0cnVjdGlvbjogY2hvb3NlUmVhZFRhcmdldCBvbmx5IHJldHVybnMgdXNlUmVwbGljYSBmb3IgYVxuICAvLyBjdHggdGhhdCBleGlzdHMuIFJlYWQgdGhlIEdVQ3Mgb2ZmIGl0IGRpcmVjdGx5IHJhdGhlciB0aGFuIGRlZmF1bHRpbmcgdG9cbiAgLy8gJycg4oCUIGEgZGVmYXVsdCBoZXJlIHdvdWxkIHNpbGVudGx5IHJlLW9wZW4gdGhlIGhvbGUgdGhhdCAnbm8tY29udGV4dCdcbiAgLy8gY2xvc2VzLCBiZWNhdXNlIGFuIGVtcHR5IGFwcC5vcmdfaWQgaXMgYSBWQUxJRC1sb29raW5nIFJMUyBzY29wZS5cbiAgY29uc3QgeyBvcmdJZCB9ID0gY3R4ITtcbiAgY29uc3QgaXNTdXBlckFkbWluID0gY3R4IS5pc1N1cGVyQWRtaW4gPyAndHJ1ZScgOiAnZmFsc2UnO1xuICBjb25zdCBzdGF0ZW1lbnRUaW1lb3V0TXMgPSBnZXRTdGF0ZW1lbnRUaW1lb3V0TXMoKTtcblxuICByZXR1cm4gcmVhZERiIS50cmFuc2FjdGlvbihhc3luYyAodHgpID0+IHtcbiAgICBhd2FpdCB0eC5leGVjdXRlKHNxbGBTRVQgVFJBTlNBQ1RJT04gUkVBRCBPTkxZYCk7XG4gICAgaWYgKHN0YXRlbWVudFRpbWVvdXRNcyA+IDApIHtcbiAgICAgIGF3YWl0IHR4LmV4ZWN1dGUoXG4gICAgICAgIHNxbGBTRUxFQ1Qgc2V0X2NvbmZpZygnYXBwLm9yZ19pZCcsICR7b3JnSWR9LCB0cnVlKSwgc2V0X2NvbmZpZygnYXBwLmlzX3N5c2FkbWluJywgJHtpc1N1cGVyQWRtaW59LCB0cnVlKSwgc2V0X2NvbmZpZygnc3RhdGVtZW50X3RpbWVvdXQnLCAke1N0cmluZyhzdGF0ZW1lbnRUaW1lb3V0TXMpfSwgdHJ1ZSlgLFxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgYXdhaXQgdHguZXhlY3V0ZShcbiAgICAgICAgc3FsYFNFTEVDVCBzZXRfY29uZmlnKCdhcHAub3JnX2lkJywgJHtvcmdJZH0sIHRydWUpLCBzZXRfY29uZmlnKCdhcHAuaXNfc3lzYWRtaW4nLCAke2lzU3VwZXJBZG1pbn0sIHRydWUpYCxcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBmbih0eCk7XG4gIH0pO1xufVxuIl19
|
package/lib/testing/exports.json
CHANGED
|
@@ -20,10 +20,12 @@
|
|
|
20
20
|
"buildPipelineTemplateConditions": "function",
|
|
21
21
|
"buildPluginConditions": "function",
|
|
22
22
|
"buildPublishedRuleCatalogConditions": "function",
|
|
23
|
+
"chooseReadTarget": "function",
|
|
23
24
|
"classifyFailure": "function",
|
|
24
25
|
"closeConnection": "function",
|
|
25
26
|
"closeFixedExposures": "function",
|
|
26
27
|
"closePublicReader": "function",
|
|
28
|
+
"closeReadConnection": "function",
|
|
27
29
|
"COMMUNITY_PUBLISHER_HANDLE": "value",
|
|
28
30
|
"compareSemver": "function",
|
|
29
31
|
"compareSemverParts": "function",
|
|
@@ -77,6 +79,8 @@
|
|
|
77
79
|
"getPublicListedVersion": "function",
|
|
78
80
|
"getPublicListing": "function",
|
|
79
81
|
"getPublicReaderDb": "function",
|
|
82
|
+
"getReadDb": "function",
|
|
83
|
+
"getReadPool": "function",
|
|
80
84
|
"getResourceConsumption": "function",
|
|
81
85
|
"getSection": "function",
|
|
82
86
|
"getSslConfig": "function",
|
|
@@ -95,6 +99,7 @@
|
|
|
95
99
|
"isTeamActionable": "function",
|
|
96
100
|
"isUniqueViolation": "function",
|
|
97
101
|
"isVersionRange": "function",
|
|
102
|
+
"lagVerdict": "function",
|
|
98
103
|
"lapsedAcceptances": "function",
|
|
99
104
|
"leadTimeGapOf": "function",
|
|
100
105
|
"listedPluginRecord": "function",
|
|
@@ -108,6 +113,7 @@
|
|
|
108
113
|
"LOW_SAMPLE_THRESHOLD": "value",
|
|
109
114
|
"MAX_BOUNCES": "value",
|
|
110
115
|
"MAX_SHARE_LINK_TTL_DAYS": "value",
|
|
116
|
+
"maybeSampleLag": "function",
|
|
111
117
|
"message": "value",
|
|
112
118
|
"messageAttachment": "value",
|
|
113
119
|
"mintToken": "function",
|
|
@@ -170,6 +176,8 @@
|
|
|
170
176
|
"reportRecipient": "value",
|
|
171
177
|
"reportRun": "value",
|
|
172
178
|
"reportShareLink": "value",
|
|
179
|
+
"resetLagStateForTests": "function",
|
|
180
|
+
"resetReadConnectionForTests": "function",
|
|
173
181
|
"resolvableListings": "function",
|
|
174
182
|
"resolveListingReference": "function",
|
|
175
183
|
"resolvePeriod": "function",
|
|
@@ -188,6 +196,7 @@
|
|
|
188
196
|
"searchPublicListings": "function",
|
|
189
197
|
"sectionIds": "function",
|
|
190
198
|
"semverOrderBy": "function",
|
|
199
|
+
"setLagSampleForTests": "function",
|
|
191
200
|
"softDeleteRetentionMs": "function",
|
|
192
201
|
"stakeholderReportStore": "value",
|
|
193
202
|
"StakeholderReportStore": "class",
|
|
@@ -203,6 +212,7 @@
|
|
|
203
212
|
"versionGap": "function",
|
|
204
213
|
"viewerCacheSegment": "function",
|
|
205
214
|
"withTenantTx": "function",
|
|
215
|
+
"withTenantTxRead": "function",
|
|
206
216
|
"withViewerContext": "function"
|
|
207
217
|
}
|
|
208
218
|
}
|
package/package.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@pipeline-builder/api-core": "5.0.
|
|
27
|
+
"@pipeline-builder/api-core": "5.0.22",
|
|
28
28
|
"drizzle-orm": "0.45.4",
|
|
29
29
|
"pg": "8.23.1"
|
|
30
30
|
},
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"access": "public",
|
|
86
86
|
"registry": "https://registry.npmjs.org/"
|
|
87
87
|
},
|
|
88
|
-
"version": "4.1.
|
|
88
|
+
"version": "4.1.47",
|
|
89
89
|
"bugs": {
|
|
90
90
|
"url": "https://github.com/mwashburn160/pipeline-builder/issues"
|
|
91
91
|
},
|