@pipeline-builder/pipeline-data 4.1.46 → 4.2.0

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.
@@ -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>): 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>;
@@ -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
@@ -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.21",
27
+ "@pipeline-builder/api-core": "5.1.0",
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.46",
88
+ "version": "4.2.0",
89
89
  "bugs": {
90
90
  "url": "https://github.com/mwashburn160/pipeline-builder/issues"
91
91
  },