@cliwant/mcp-sam-gov 0.3.0 → 1.0.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.
- package/LICENSE +21 -21
- package/README.ja.md +48 -16
- package/README.ko.md +48 -16
- package/README.md +226 -64
- package/dist/attachments.d.ts +63 -0
- package/dist/attachments.d.ts.map +1 -0
- package/dist/attachments.js +576 -0
- package/dist/attachments.js.map +1 -0
- package/dist/bls.d.ts +267 -0
- package/dist/bls.d.ts.map +1 -0
- package/dist/bls.js +1549 -0
- package/dist/bls.js.map +1 -0
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +4 -0
- package/dist/cache.js.map +1 -1
- package/dist/census.d.ts +182 -0
- package/dist/census.d.ts.map +1 -0
- package/dist/census.js +576 -0
- package/dist/census.js.map +1 -0
- package/dist/ckan.d.ts +141 -0
- package/dist/ckan.d.ts.map +1 -0
- package/dist/ckan.js +393 -0
- package/dist/ckan.js.map +1 -0
- package/dist/clinicaltrials.d.ts +180 -0
- package/dist/clinicaltrials.d.ts.map +1 -0
- package/dist/clinicaltrials.js +730 -0
- package/dist/clinicaltrials.js.map +1 -0
- package/dist/cms.d.ts +140 -0
- package/dist/cms.d.ts.map +1 -0
- package/dist/cms.js +482 -0
- package/dist/cms.js.map +1 -0
- package/dist/coerce.d.ts +32 -0
- package/dist/coerce.d.ts.map +1 -0
- package/dist/coerce.js +50 -0
- package/dist/coerce.js.map +1 -0
- package/dist/datagov-catalog.d.ts +84 -0
- package/dist/datagov-catalog.d.ts.map +1 -0
- package/dist/datagov-catalog.js +233 -0
- package/dist/datagov-catalog.js.map +1 -0
- package/dist/datagov.d.ts +146 -0
- package/dist/datagov.d.ts.map +1 -0
- package/dist/datagov.js +689 -0
- package/dist/datagov.js.map +1 -0
- package/dist/datagovKey.d.ts +36 -0
- package/dist/datagovKey.d.ts.map +1 -0
- package/dist/datagovKey.js +60 -0
- package/dist/datagovKey.js.map +1 -0
- package/dist/datasource.d.ts +313 -0
- package/dist/datasource.d.ts.map +1 -0
- package/dist/datasource.js +551 -0
- package/dist/datasource.js.map +1 -0
- package/dist/disclosure.d.ts +55 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +57 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/ecfr.d.ts +4 -2
- package/dist/ecfr.d.ts.map +1 -1
- package/dist/ecfr.js +74 -1
- package/dist/ecfr.js.map +1 -1
- package/dist/echo.d.ts +143 -0
- package/dist/echo.d.ts.map +1 -0
- package/dist/echo.js +424 -0
- package/dist/echo.js.map +1 -0
- package/dist/edgar.d.ts +377 -0
- package/dist/edgar.d.ts.map +1 -0
- package/dist/edgar.js +2418 -0
- package/dist/edgar.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +69 -1
- package/dist/errors.js.map +1 -1
- package/dist/fac.d.ts +180 -0
- package/dist/fac.d.ts.map +1 -0
- package/dist/fac.js +416 -0
- package/dist/fac.js.map +1 -0
- package/dist/far.d.ts +170 -0
- package/dist/far.d.ts.map +1 -0
- package/dist/far.js +804 -0
- package/dist/far.js.map +1 -0
- package/dist/fdic.d.ts +599 -0
- package/dist/fdic.d.ts.map +1 -0
- package/dist/fdic.js +1624 -0
- package/dist/fdic.js.map +1 -0
- package/dist/federal-register.d.ts +139 -2
- package/dist/federal-register.d.ts.map +1 -1
- package/dist/federal-register.js +419 -3
- package/dist/federal-register.js.map +1 -1
- package/dist/fema.d.ts +181 -0
- package/dist/fema.d.ts.map +1 -0
- package/dist/fema.js +436 -0
- package/dist/fema.js.map +1 -0
- package/dist/fpds.d.ts +108 -0
- package/dist/fpds.d.ts.map +1 -0
- package/dist/fpds.js +519 -0
- package/dist/fpds.js.map +1 -0
- package/dist/gao.d.ts +64 -0
- package/dist/gao.d.ts.map +1 -0
- package/dist/gao.js +640 -0
- package/dist/gao.js.map +1 -0
- package/dist/govinfo.d.ts +111 -0
- package/dist/govinfo.d.ts.map +1 -0
- package/dist/govinfo.js +422 -0
- package/dist/govinfo.js.map +1 -0
- package/dist/grants.d.ts +27 -4
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +111 -6
- package/dist/grants.js.map +1 -1
- package/dist/gsa-csv.d.ts +249 -0
- package/dist/gsa-csv.d.ts.map +1 -0
- package/dist/gsa-csv.js +784 -0
- package/dist/gsa-csv.js.map +1 -0
- package/dist/integrity.d.ts +212 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +707 -0
- package/dist/integrity.js.map +1 -0
- package/dist/meta.d.ts +165 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/meta.js +162 -0
- package/dist/meta.js.map +1 -0
- package/dist/nih.d.ts +117 -0
- package/dist/nih.d.ts.map +1 -0
- package/dist/nih.js +291 -0
- package/dist/nih.js.map +1 -0
- package/dist/nppes.d.ts +157 -0
- package/dist/nppes.d.ts.map +1 -0
- package/dist/nppes.js +648 -0
- package/dist/nppes.js.map +1 -0
- package/dist/nsf.d.ts +176 -0
- package/dist/nsf.d.ts.map +1 -0
- package/dist/nsf.js +554 -0
- package/dist/nsf.js.map +1 -0
- package/dist/nvd.d.ts +176 -0
- package/dist/nvd.d.ts.map +1 -0
- package/dist/nvd.js +912 -0
- package/dist/nvd.js.map +1 -0
- package/dist/ofac.d.ts +205 -0
- package/dist/ofac.d.ts.map +1 -0
- package/dist/ofac.js +919 -0
- package/dist/ofac.js.map +1 -0
- package/dist/pricing.d.ts +110 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +843 -0
- package/dist/pricing.js.map +1 -0
- package/dist/sam-gov/client.d.ts +60 -2
- package/dist/sam-gov/client.d.ts.map +1 -1
- package/dist/sam-gov/client.js +320 -54
- package/dist/sam-gov/client.js.map +1 -1
- package/dist/sam-gov/index.d.ts +1 -1
- package/dist/sam-gov/index.d.ts.map +1 -1
- package/dist/sam-gov/index.js +1 -1
- package/dist/sam-gov/index.js.map +1 -1
- package/dist/sam-gov/types.d.ts +24 -0
- package/dist/sam-gov/types.d.ts.map +1 -1
- package/dist/sba.d.ts +72 -0
- package/dist/sba.d.ts.map +1 -0
- package/dist/sba.js +281 -0
- package/dist/sba.js.map +1 -0
- package/dist/server.d.ts +14 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3889 -296
- package/dist/server.js.map +1 -1
- package/dist/snapshot.d.ts +98 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +146 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/socrata.d.ts +157 -0
- package/dist/socrata.d.ts.map +1 -0
- package/dist/socrata.js +448 -0
- package/dist/socrata.js.map +1 -0
- package/dist/treasury.d.ts +143 -0
- package/dist/treasury.d.ts.map +1 -0
- package/dist/treasury.js +436 -0
- package/dist/treasury.js.map +1 -0
- package/dist/usaspending.d.ts +260 -65
- package/dist/usaspending.d.ts.map +1 -1
- package/dist/usaspending.js +1629 -201
- package/dist/usaspending.js.map +1 -1
- package/dist/usitc.d.ts +142 -0
- package/dist/usitc.d.ts.map +1 -0
- package/dist/usitc.js +339 -0
- package/dist/usitc.js.map +1 -0
- package/package.json +24 -2
- package/src/attachments.ts +652 -0
- package/src/bls.ts +1943 -0
- package/src/cache.ts +5 -0
- package/src/census.ts +735 -0
- package/src/ckan.ts +495 -0
- package/src/clinicaltrials.ts +923 -0
- package/src/cms.ts +634 -0
- package/src/coerce.ts +47 -0
- package/src/datagov-catalog.ts +296 -0
- package/src/datagov.ts +907 -0
- package/src/datagovKey.ts +68 -0
- package/src/datasource.ts +721 -0
- package/src/disclosure.ts +61 -0
- package/src/ecfr.ts +98 -1
- package/src/echo.ts +496 -0
- package/src/edgar.ts +3014 -0
- package/src/errors.ts +84 -1
- package/src/fac.ts +529 -0
- package/src/far.ts +1007 -0
- package/src/fdic.ts +2052 -0
- package/src/federal-register.ts +512 -3
- package/src/fema.ts +541 -0
- package/src/fpds.ts +620 -0
- package/src/gao.ts +744 -0
- package/src/govinfo.ts +497 -0
- package/src/grants.ts +139 -7
- package/src/gsa-csv.ts +992 -0
- package/src/integrity.ts +928 -0
- package/src/meta.ts +292 -0
- package/src/nih.ts +375 -0
- package/src/nppes.ts +834 -0
- package/src/nsf.ts +706 -0
- package/src/nvd.ts +1124 -0
- package/src/ofac.ts +1166 -0
- package/src/pricing.ts +1075 -0
- package/src/sam-gov/client.ts +345 -63
- package/src/sam-gov/index.ts +5 -1
- package/src/sam-gov/types.ts +22 -0
- package/src/sba.ts +357 -0
- package/src/server.ts +4551 -328
- package/src/snapshot.ts +192 -0
- package/src/socrata.ts +532 -0
- package/src/treasury.ts +575 -0
- package/src/usaspending.ts +1946 -214
- package/src/usitc.ts +420 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* datasource.ts — the shared fetch envelope for the keyless DataSources
|
|
3
|
+
* (ADR-0005). This is the ONE audited `assemble init → fetchWithRetry → r.json()`
|
|
4
|
+
* skeleton the keyless adapters share; the per-source quirks (timeout / headers /
|
|
5
|
+
* redirect) are OPTIONS, not three bespoke fetch fns. `RequestInit` already
|
|
6
|
+
* carries `headers` and `redirect`, so getJson forwards them without adding logic
|
|
7
|
+
* — folding the fetch fns into one is lossless.
|
|
8
|
+
*
|
|
9
|
+
* R2 SCOPE (ADR-0005 v2 FIX-B): getJson ships ONLY { label, headers?, redirect?,
|
|
10
|
+
* timeoutMs? }. `redirect:"error"` STAYS: a zero-logic RequestInit passthrough,
|
|
11
|
+
* absent for Treasury, and Socrata + the queued CKAN connector need the identical
|
|
12
|
+
* SSRF hardening.
|
|
13
|
+
*
|
|
14
|
+
* THE MIN-INTERVAL GATE (ADR-0011 orchestrator, v6 cycle 15 — the R2 deferred
|
|
15
|
+
* slice, now landed): `throughGate(key, minIntervalMs, fn)` is a STANDALONE
|
|
16
|
+
* exported primitive (below), the shared home for EDGAR's former module-singleton
|
|
17
|
+
* self-throttle. It is deliberately NOT wired into getJson and `gateKey`/
|
|
18
|
+
* `minIntervalMs` are deliberately NOT added to GetJsonOptions — there is no
|
|
19
|
+
* getJson consumer that throttles, so that would be dead option-surface (FIX-B
|
|
20
|
+
* "no dead surface"). If a future source makes a throttled getJson call, wiring
|
|
21
|
+
* the gate into getJson can be revisited then.
|
|
22
|
+
*
|
|
23
|
+
* `label` is the fetchWithRetry taxonomy key AND surfaces verbatim in
|
|
24
|
+
* ToolError.upstreamEndpoint to the MCP caller — it MUST be HOST-ONLY and never
|
|
25
|
+
* contain a token (Socrata/EDGAR m7).
|
|
26
|
+
*
|
|
27
|
+
* The shape-drift CHECK stays at each call site (bespoke per-source fields); only
|
|
28
|
+
* the THROW is standardized via `driftError`. This is deliberately NOT a `guard`
|
|
29
|
+
* hook baked into getJson: a hook would collide with Socrata's count(*) companion
|
|
30
|
+
* (which must degrade to total:null and NEVER throw schema_drift) and its
|
|
31
|
+
* discoverDatasets memoize boundary — leaving the check at the call site keeps
|
|
32
|
+
* that split honest (ADR-0005 §1c / Q4).
|
|
33
|
+
*/
|
|
34
|
+
import { fetchWithRetry, errorFromResponse, ToolErrorCarrier, isHonorRetryAfter, } from "./errors.js";
|
|
35
|
+
/**
|
|
36
|
+
* GET + parse one JSON resource through the shared envelope. Assembles `init`
|
|
37
|
+
* (a fresh `AbortSignal.timeout` always; `headers`/`redirect` set ONLY when the
|
|
38
|
+
* option is provided — byte-identical to each source's prior hand-rolled init),
|
|
39
|
+
* calls `fetchWithRetry` (retry/backoff + the 429/5xx/404/400 taxonomy), then
|
|
40
|
+
* returns the parsed body. The caller validates the shape and throws
|
|
41
|
+
* `driftError` on drift.
|
|
42
|
+
*/
|
|
43
|
+
export async function getJson(url, opts) {
|
|
44
|
+
const init = {
|
|
45
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15_000),
|
|
46
|
+
};
|
|
47
|
+
if (opts.headers !== undefined)
|
|
48
|
+
init.headers = opts.headers;
|
|
49
|
+
if (opts.redirect)
|
|
50
|
+
init.redirect = opts.redirect;
|
|
51
|
+
// POST/body passthrough (ADR-0014) — set ONLY when defined, matching the
|
|
52
|
+
// `headers` idiom, so a GET caller's init stays byte-identical (no `method`/
|
|
53
|
+
// `body` key). `method`/`body` are RequestInit fields forwarded without logic.
|
|
54
|
+
if (opts.method !== undefined)
|
|
55
|
+
init.method = opts.method;
|
|
56
|
+
if (opts.body !== undefined)
|
|
57
|
+
init.body = opts.body;
|
|
58
|
+
const r = await fetchWithRetry(url, init, opts.label);
|
|
59
|
+
return (await r.json());
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* getJsonWithHeaders — the header-exposing sibling of `getJson` (ADR-0038 M1).
|
|
63
|
+
*
|
|
64
|
+
* WHY a SEPARATE primitive (and NOT a mutation of getJson): a PostgREST source
|
|
65
|
+
* (FAC / any Range-paginating REST API) carries its EXACT total in the
|
|
66
|
+
* `Content-Range` RESPONSE HEADER, but `getJson` returns ONLY `await r.json()`
|
|
67
|
+
* (the parsed body) and DISCARDS the `Response`/headers — so the header is
|
|
68
|
+
* unreachable through it. Mutating getJson's return shape would break every one
|
|
69
|
+
* of its ~9 callers (NOT byte-identical); this additive variant leaves getJson
|
|
70
|
+
* untouched. It runs the IDENTICAL init assembly + `fetchWithRetry(url, init,
|
|
71
|
+
* opts.label)` envelope as getJson (same headers / `redirect` / timeout / method
|
|
72
|
+
* / body / retry-taxonomy), and returns the parsed body PLUS ONLY the
|
|
73
|
+
* `content-range` header string — it NEVER surfaces the raw `Headers` object, so
|
|
74
|
+
* no incidental response header (Set-Cookie, a rate-limit token, etc.) can reach
|
|
75
|
+
* a consumer's `_meta`/output. A 200 non-JSON body makes `r.json()` throw a
|
|
76
|
+
* `SyntaxError`, exactly as with getJson — the caller reclassifies it (the
|
|
77
|
+
* fdic.ts / ADR-0038 S1 pattern), keeping the shared envelope free of source
|
|
78
|
+
* quirks.
|
|
79
|
+
*/
|
|
80
|
+
export async function getJsonWithHeaders(url, opts) {
|
|
81
|
+
const init = {
|
|
82
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15_000),
|
|
83
|
+
};
|
|
84
|
+
if (opts.headers !== undefined)
|
|
85
|
+
init.headers = opts.headers;
|
|
86
|
+
if (opts.redirect)
|
|
87
|
+
init.redirect = opts.redirect;
|
|
88
|
+
if (opts.method !== undefined)
|
|
89
|
+
init.method = opts.method;
|
|
90
|
+
if (opts.body !== undefined)
|
|
91
|
+
init.body = opts.body;
|
|
92
|
+
const r = await fetchWithRetry(url, init, opts.label);
|
|
93
|
+
return {
|
|
94
|
+
body: (await r.json()),
|
|
95
|
+
contentRange: r.headers.get("content-range"),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* GET one text resource through the shared envelope. Assembles `init`
|
|
100
|
+
* (byte-identical to `getJson`'s rule: a fresh `AbortSignal.timeout` always;
|
|
101
|
+
* `headers`/`redirect` set ONLY when the option is provided), then either
|
|
102
|
+
* retries via `fetchWithRetry` (default) or does a single classified `fetch`
|
|
103
|
+
* (retry:false), and returns the raw `r.text()` body.
|
|
104
|
+
*/
|
|
105
|
+
export async function getText(url, opts) {
|
|
106
|
+
const init = {
|
|
107
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15_000),
|
|
108
|
+
};
|
|
109
|
+
if (opts.headers !== undefined)
|
|
110
|
+
init.headers = opts.headers;
|
|
111
|
+
if (opts.redirect)
|
|
112
|
+
init.redirect = opts.redirect;
|
|
113
|
+
// Default path (far/gao): retry transient errors up to 3× via the shared
|
|
114
|
+
// primitive, then return the body text.
|
|
115
|
+
if (opts.retry !== false) {
|
|
116
|
+
const r = await fetchWithRetry(url, init, opts.label);
|
|
117
|
+
return await r.text();
|
|
118
|
+
}
|
|
119
|
+
// Single-attempt path (fpds m-redirect): the fetch is done HERE so a
|
|
120
|
+
// redirect:"error" TypeError is classified as a NON-retryable schema_drift
|
|
121
|
+
// (never routed through fetchWithRetry, which would retry it 3× as a retryable
|
|
122
|
+
// upstream_unavailable). A 5xx/429/404/timeout is classified + THROWS (never a
|
|
123
|
+
// fake empty). Byte-for-byte the shipped fpds single-fetch body.
|
|
124
|
+
let r;
|
|
125
|
+
try {
|
|
126
|
+
r = await fetch(url, init);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
if (isRedirectError(e)) {
|
|
130
|
+
// Fail closed — NEVER follow the off-host redirect, NEVER read its body,
|
|
131
|
+
// and do NOT let it masquerade as a retryable outage.
|
|
132
|
+
throw driftError(opts.label, opts.redirectMessage ??
|
|
133
|
+
`Off-host redirect refused (redirect:"error") while fetching ${opts.label}.`);
|
|
134
|
+
}
|
|
135
|
+
// timeout / abort / network — retryable upstream, but THROWS (never fake-empty).
|
|
136
|
+
throw new ToolErrorCarrier({
|
|
137
|
+
kind: "upstream_unavailable",
|
|
138
|
+
message: `Network error reaching ${opts.label}: ${e instanceof Error ? e.message : String(e)}`,
|
|
139
|
+
retryable: true,
|
|
140
|
+
retryAfterSeconds: 30,
|
|
141
|
+
upstreamEndpoint: opts.label,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (!r.ok) {
|
|
145
|
+
// 404/429/5xx/4xx → the errors.ts taxonomy. A DOWN service NEVER reads empty.
|
|
146
|
+
throw new ToolErrorCarrier(errorFromResponse(r, opts.label));
|
|
147
|
+
}
|
|
148
|
+
return await r.text();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Is a thrown error the redirect:"error" TypeError (undici: cause "unexpected
|
|
152
|
+
* redirect")? The live FPDS search.do→sam.gov 301 is the concrete case
|
|
153
|
+
* (ADR-0012 §1a). Moved here from fpds.ts (ADR-0013) as `getText`'s audited home
|
|
154
|
+
* — it is only reachable when a caller sets `redirect:"error"` (undici throws
|
|
155
|
+
* the unexpected-redirect TypeError only in `"error"` mode), so it is inert for
|
|
156
|
+
* any retry:false caller that does NOT set redirect.
|
|
157
|
+
*/
|
|
158
|
+
export function isRedirectError(e) {
|
|
159
|
+
if (!(e instanceof TypeError))
|
|
160
|
+
return false;
|
|
161
|
+
const causeMsg = e.cause && typeof e.cause.message === "string"
|
|
162
|
+
? e.cause.message
|
|
163
|
+
: "";
|
|
164
|
+
return /redirect/i.test(causeMsg) || /redirect/i.test(e.message);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The shared `schema_drift` constructor (all sources threw the identical
|
|
168
|
+
* carrier). Each source keeps its bespoke field-CHECK inline and calls this to
|
|
169
|
+
* standardize only the THROW. `label` becomes `upstreamEndpoint` — host-only,
|
|
170
|
+
* never a token.
|
|
171
|
+
*/
|
|
172
|
+
export function driftError(label, message) {
|
|
173
|
+
return new ToolErrorCarrier({
|
|
174
|
+
kind: "schema_drift",
|
|
175
|
+
message,
|
|
176
|
+
retryable: false,
|
|
177
|
+
upstreamEndpoint: label,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// ─── Per-key min-interval gate (shared self-throttle primitive) ────
|
|
181
|
+
/**
|
|
182
|
+
* One serialized promise chain + last-run timestamp PER KEY. Module-level so a
|
|
183
|
+
* key's chain persists across every call in the process (the whole point of a
|
|
184
|
+
* self-throttle: a tool call can fan out and many tools may run). Different keys
|
|
185
|
+
* are independent chains — they never block each other.
|
|
186
|
+
*/
|
|
187
|
+
const gates = new Map();
|
|
188
|
+
/**
|
|
189
|
+
* Serialize every call sharing `key` through a single promise chain, spacing the
|
|
190
|
+
* START of consecutive runs by ≥ `minIntervalMs`. This is the generalization of
|
|
191
|
+
* EDGAR's former module singleton (edgar.ts `edgarGateChain`/`edgarLastFetchAt`/
|
|
192
|
+
* `throughEdgarGate`); `throughGate("edgar", 110, fn)` reproduces its behavior
|
|
193
|
+
* EXACTLY. Semantics that MUST stay byte-identical to the old EDGAR gate:
|
|
194
|
+
* - the Map entry is fetched-or-created and MUTATED IN PLACE (never replaced),
|
|
195
|
+
* so a key keeps one chain across the process;
|
|
196
|
+
* - before invoking `fn()`, wait `max(0, minIntervalMs - (now - lastAt))` using
|
|
197
|
+
* the **bare global `setTimeout`** (NOT `node:timers/promises`) — the fault
|
|
198
|
+
* suite's timer-neutralizing patch makes this offline-instant; an import-based
|
|
199
|
+
* timer would break that (and offline determinism);
|
|
200
|
+
* - `lastAt` is stamped with `Date.now()` **BEFORE** `fn()` is called, so the
|
|
201
|
+
* spacing is measured from the START of the previous run (edgar L97-98);
|
|
202
|
+
* - the chain SWALLOWS each step's error so the queue keeps flowing, while the
|
|
203
|
+
* real result/error still propagates on the promise returned to THAT caller.
|
|
204
|
+
*/
|
|
205
|
+
export function throughGate(key, minIntervalMs, fn) {
|
|
206
|
+
let gate = gates.get(key);
|
|
207
|
+
if (!gate) {
|
|
208
|
+
gate = { chain: Promise.resolve(), lastAt: 0 };
|
|
209
|
+
gates.set(key, gate);
|
|
210
|
+
}
|
|
211
|
+
const g = gate;
|
|
212
|
+
const run = g.chain.then(async () => {
|
|
213
|
+
const wait = g.lastAt + minIntervalMs - Date.now();
|
|
214
|
+
if (wait > 0)
|
|
215
|
+
await new Promise((res) => setTimeout(res, wait));
|
|
216
|
+
g.lastAt = Date.now();
|
|
217
|
+
return fn();
|
|
218
|
+
});
|
|
219
|
+
// Keep the chain alive whether this link resolves or rejects.
|
|
220
|
+
g.chain = run.then(() => undefined, () => undefined);
|
|
221
|
+
return run;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* getJsonWithProvenance — the resilient-fetch primitive (ADR-0045 B2). Runs the
|
|
225
|
+
* IDENTICAL init-assembly + `fetchWithRetry(url, init, opts.label)` envelope as
|
|
226
|
+
* `getJson`, and returns `{ body, provenance }`. The port ALWAYS returns an
|
|
227
|
+
* explicit provenance (honesty-B2 + regression-M1 reconciled): in Phase 1 there
|
|
228
|
+
* is a SINGLE live path, so `provenance` is ALWAYS `{ dataPath:"live" }` (asOf
|
|
229
|
+
* omitted). Because the port always stamps provenance, a mirror/snapshot body can
|
|
230
|
+
* never be structurally mislabelled as live (a future adapter that forgets to
|
|
231
|
+
* thread it still cannot claim live).
|
|
232
|
+
*
|
|
233
|
+
* ★INERT: NO adapter calls this yet — it is dormant infrastructure. It is a
|
|
234
|
+
* SEPARATE primitive (getJson is untouched, byte-identical), exported for the
|
|
235
|
+
* path-chain and for direct unit tests.
|
|
236
|
+
*/
|
|
237
|
+
export async function getJsonWithProvenance(url, opts) {
|
|
238
|
+
const init = {
|
|
239
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15_000),
|
|
240
|
+
};
|
|
241
|
+
if (opts.headers !== undefined)
|
|
242
|
+
init.headers = opts.headers;
|
|
243
|
+
if (opts.redirect)
|
|
244
|
+
init.redirect = opts.redirect;
|
|
245
|
+
if (opts.method !== undefined)
|
|
246
|
+
init.method = opts.method;
|
|
247
|
+
if (opts.body !== undefined)
|
|
248
|
+
init.body = opts.body;
|
|
249
|
+
const r = await fetchWithRetry(url, init, opts.label);
|
|
250
|
+
return {
|
|
251
|
+
body: (await r.json()),
|
|
252
|
+
// Single live path in Phase 1 — always live, no asOf.
|
|
253
|
+
provenance: { dataPath: "live" },
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// ─── Per-host circuit breaker (ADR-0045 B1) ────────────────────────────────
|
|
257
|
+
/**
|
|
258
|
+
* A per-host circuit breaker (bounded, keyed by a FIXED host set — the same
|
|
259
|
+
* discipline as `gates` above, so the Map can NEVER grow unboundedly; m3-reg).
|
|
260
|
+
*
|
|
261
|
+
* Trip condition (INTENTIONALLY narrow, B1-regression): 5 CONSECUTIVE HARD
|
|
262
|
+
* failures on a host — where HARD = a 5xx or a network `TypeError` ONLY. It is
|
|
263
|
+
* EXPLICITLY not tripped by 429 (rate-limited), a 5xx bearing Retry-After
|
|
264
|
+
* (isHonorRetryAfter — B1-policy), 404, or Timeout/Abort (errors.ts:162-172's
|
|
265
|
+
* existing fast-fail). Once open, it stays open 30s, then admits a SINGLE
|
|
266
|
+
* half-open probe; a probe success closes it, a probe failure re-opens it.
|
|
267
|
+
*
|
|
268
|
+
* ★INERT in Phase 1: the breaker is only ever CONSULTED by `throughPathChain`
|
|
269
|
+
* for a ≥2-path chain. No source declares ≥2 paths this phase, so it never
|
|
270
|
+
* activates in production — but it is unit-tested directly.
|
|
271
|
+
*/
|
|
272
|
+
const BREAKER_TRIP_THRESHOLD = 5;
|
|
273
|
+
const BREAKER_OPEN_MS = 30_000;
|
|
274
|
+
export class CircuitBreaker {
|
|
275
|
+
/** Bounded: only hosts in the fixed set are ever tracked (m3-regression). */
|
|
276
|
+
hosts;
|
|
277
|
+
states = new Map();
|
|
278
|
+
/** Injectable clock for deterministic offline unit tests (defaults Date.now). */
|
|
279
|
+
now;
|
|
280
|
+
constructor(hosts, now = () => Date.now()) {
|
|
281
|
+
this.hosts = new Set(hosts);
|
|
282
|
+
this.now = now;
|
|
283
|
+
}
|
|
284
|
+
state(host) {
|
|
285
|
+
if (!this.hosts.has(host))
|
|
286
|
+
return undefined; // untracked host ⇒ pure no-op
|
|
287
|
+
let s = this.states.get(host);
|
|
288
|
+
if (!s) {
|
|
289
|
+
s = { consecutiveHardFailures: 0, openUntil: 0, probeInFlight: false };
|
|
290
|
+
this.states.set(host, s);
|
|
291
|
+
}
|
|
292
|
+
return s;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Should the live attempt to `host` be SKIPPED right now? True only while the
|
|
296
|
+
* breaker is OPEN and its 30s window has not elapsed AND a half-open probe is
|
|
297
|
+
* already in flight. When the window elapses, exactly ONE caller is admitted as
|
|
298
|
+
* the half-open probe (this returns false and marks probeInFlight) and the rest
|
|
299
|
+
* are skipped until that probe reports back. An untracked host is NEVER skipped.
|
|
300
|
+
*/
|
|
301
|
+
shouldSkip(host) {
|
|
302
|
+
const s = this.state(host);
|
|
303
|
+
if (!s || s.openUntil === 0)
|
|
304
|
+
return false; // closed (or untracked) ⇒ attempt
|
|
305
|
+
if (this.now() < s.openUntil) {
|
|
306
|
+
// Fully OPEN within the 30s window ⇒ skip every live attempt.
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
// Window elapsed → HALF-OPEN. Admit exactly ONE probe (returns false so it
|
|
310
|
+
// attempts live); skip all others until that probe reports back.
|
|
311
|
+
if (!s.probeInFlight) {
|
|
312
|
+
s.probeInFlight = true;
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
/** Record a live success — closes the breaker and resets the failure run. */
|
|
318
|
+
onSuccess(host) {
|
|
319
|
+
const s = this.state(host);
|
|
320
|
+
if (!s)
|
|
321
|
+
return;
|
|
322
|
+
s.consecutiveHardFailures = 0;
|
|
323
|
+
s.openUntil = 0;
|
|
324
|
+
s.probeInFlight = false;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Record a live failure. A NON-hard error (429 / honor-Retry-After / 404 /
|
|
328
|
+
* timeout / abort) is IGNORED — it neither counts toward the trip threshold nor
|
|
329
|
+
* resets the run (it is orthogonal to host health). A HARD error increments the
|
|
330
|
+
* consecutive count and trips the breaker at the threshold; if it arrives during
|
|
331
|
+
* a half-open probe it immediately re-opens the window.
|
|
332
|
+
*/
|
|
333
|
+
onFailure(host, err) {
|
|
334
|
+
const s = this.state(host);
|
|
335
|
+
if (!s)
|
|
336
|
+
return;
|
|
337
|
+
if (!isHardFailure(err))
|
|
338
|
+
return; // 429/honor-RA/404/timeout ⇒ not host health
|
|
339
|
+
if (s.probeInFlight) {
|
|
340
|
+
// Half-open probe failed → re-open for another full window.
|
|
341
|
+
s.openUntil = this.now() + BREAKER_OPEN_MS;
|
|
342
|
+
s.probeInFlight = false;
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
s.consecutiveHardFailures += 1;
|
|
346
|
+
if (s.consecutiveHardFailures >= BREAKER_TRIP_THRESHOLD) {
|
|
347
|
+
s.openUntil = this.now() + BREAKER_OPEN_MS;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/** Test-only introspection: is the breaker currently OPEN for `host`? */
|
|
351
|
+
isOpen(host) {
|
|
352
|
+
const s = this.states.get(host);
|
|
353
|
+
return !!s && s.openUntil !== 0 && this.now() < s.openUntil;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Is a thrown error a HARD failure for circuit-breaker purposes (ADR-0045 B1)?
|
|
358
|
+
* HARD = a 5xx or a network `TypeError` ONLY. Everything else is excluded:
|
|
359
|
+
* • isHonorRetryAfter (429 / 5xx+Retry-After) — B1-policy, honor the wait;
|
|
360
|
+
* • Timeout/Abort — classified upstream_unavailable with retryable:FALSE
|
|
361
|
+
* (errors.ts:169), so the `retryable===true` gate excludes it;
|
|
362
|
+
* • 404 (not_found), 4xx (invalid_input), schema_drift, unknown — not outages.
|
|
363
|
+
* A 5xx-after-retries and a network TypeError both surface from `fetchWithRetry`
|
|
364
|
+
* as a `ToolErrorCarrier` upstream_unavailable with retryable:TRUE — the one
|
|
365
|
+
* signature this admits. A raw (unclassified) `TypeError` also counts, for
|
|
366
|
+
* robustness when the breaker is driven directly.
|
|
367
|
+
*/
|
|
368
|
+
export function isHardFailure(err) {
|
|
369
|
+
if (isHonorRetryAfter(err))
|
|
370
|
+
return false;
|
|
371
|
+
if (err instanceof ToolErrorCarrier) {
|
|
372
|
+
const te = err.toolError;
|
|
373
|
+
return te.kind === "upstream_unavailable" && te.retryable === true;
|
|
374
|
+
}
|
|
375
|
+
return err instanceof TypeError;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Try an ordered array of access paths, returning the first success + its
|
|
379
|
+
* provenance (ADR-0045 §"경로 체인").
|
|
380
|
+
*
|
|
381
|
+
* ★A SINGLE-entry chain is a PURE PASSTHROUGH — no breaker consult, no try/catch
|
|
382
|
+
* overhead, byte-for-byte today's behavior (B1-regression). The breaker is
|
|
383
|
+
* consulted ONLY for a ≥2-path chain, so the 27 single-path sources are never
|
|
384
|
+
* affected.
|
|
385
|
+
*
|
|
386
|
+
* For a multi-path chain: an open breaker on a path's host SKIPS that path (no
|
|
387
|
+
* live attempt); a HARD failure records against the breaker and falls through to
|
|
388
|
+
* the next path; a SUCCESS records success and returns. An isHonorRetryAfter
|
|
389
|
+
* error (429 / 5xx+Retry-After) is RE-THROWN IMMEDIATELY (B1-policy) — it never
|
|
390
|
+
* counts against the breaker and never falls through to a mirror/snapshot; we
|
|
391
|
+
* wait and fail honestly. If every path is exhausted, the last error is thrown
|
|
392
|
+
* (honest failure — never a fabricated empty).
|
|
393
|
+
*/
|
|
394
|
+
export async function throughPathChain(paths, breaker) {
|
|
395
|
+
if (paths.length === 0) {
|
|
396
|
+
throw new ToolErrorCarrier({
|
|
397
|
+
kind: "unknown",
|
|
398
|
+
message: "throughPathChain called with no paths.",
|
|
399
|
+
retryable: false,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
// Single-path fast path: pure passthrough, no breaker, no overhead (=today).
|
|
403
|
+
if (paths.length === 1) {
|
|
404
|
+
const only = paths[0];
|
|
405
|
+
return { body: await only.run(), provenance: only.provenance };
|
|
406
|
+
}
|
|
407
|
+
let lastErr;
|
|
408
|
+
for (const path of paths) {
|
|
409
|
+
if (breaker && breaker.shouldSkip(path.host)) {
|
|
410
|
+
// Breaker open for this host → skip the live attempt, try the next path.
|
|
411
|
+
lastErr =
|
|
412
|
+
lastErr ??
|
|
413
|
+
new ToolErrorCarrier({
|
|
414
|
+
kind: "upstream_unavailable",
|
|
415
|
+
message: `Circuit breaker open for ${path.host}; skipped.`,
|
|
416
|
+
retryable: true,
|
|
417
|
+
upstreamEndpoint: path.host,
|
|
418
|
+
});
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const body = await path.run();
|
|
423
|
+
if (breaker)
|
|
424
|
+
breaker.onSuccess(path.host);
|
|
425
|
+
return { body, provenance: path.provenance };
|
|
426
|
+
}
|
|
427
|
+
catch (e) {
|
|
428
|
+
// Honor an explicit upstream wait — NEVER count it against the breaker and
|
|
429
|
+
// NEVER fall through to a fallback path (B1-policy). Wait + fail honestly.
|
|
430
|
+
if (isHonorRetryAfter(e))
|
|
431
|
+
throw e;
|
|
432
|
+
if (breaker)
|
|
433
|
+
breaker.onFailure(path.host, e);
|
|
434
|
+
lastErr = e;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
throw lastErr;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* getJsonConditional — a SEPARATE conditional-GET primitive (ADR-0045
|
|
441
|
+
* B2-regression). It is DELIBERATELY NOT folded into the shared getJson/getText:
|
|
442
|
+
* adding If-None-Match/If-Modified-Since to getJson would make a 304 (a bodiless
|
|
443
|
+
* response) hit `r.json()` → a SyntaxError that fdic:340 / fedreg:588 reclassify
|
|
444
|
+
* as schema_drift — a hard regression. So this primitive:
|
|
445
|
+
* (a) sends validators ONLY when it holds a cache entry;
|
|
446
|
+
* (b) intercepts a 304 BEFORE the r.ok gate and returns the cached body;
|
|
447
|
+
* (c) NEVER calls `r.json()` on a 304.
|
|
448
|
+
* It keeps getJson's retry taxonomy for the non-304 case (5xx/429/network retry;
|
|
449
|
+
* timeout/abort fast-fail), via a local loop that mirrors fetchWithRetry but adds
|
|
450
|
+
* the 304 short-circuit ABOVE the r.ok gate.
|
|
451
|
+
*
|
|
452
|
+
* ★INERT: no adapter uses it yet. A cache MISS (no entry) behaves like getJson
|
|
453
|
+
* (no validators sent, 200 parsed) — so an adapter that later adopts it without a
|
|
454
|
+
* warm cache is envelope-identical.
|
|
455
|
+
*/
|
|
456
|
+
export async function getJsonConditional(url, opts, cache) {
|
|
457
|
+
// (a) Send validators ONLY when a cache entry is held.
|
|
458
|
+
const headers = { ...(opts.headers ?? {}) };
|
|
459
|
+
if (cache?.etag !== undefined)
|
|
460
|
+
headers["If-None-Match"] = cache.etag;
|
|
461
|
+
if (cache?.lastModified !== undefined)
|
|
462
|
+
headers["If-Modified-Since"] = cache.lastModified;
|
|
463
|
+
const buildInit = () => {
|
|
464
|
+
const init = {
|
|
465
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 15_000),
|
|
466
|
+
};
|
|
467
|
+
if (Object.keys(headers).length > 0)
|
|
468
|
+
init.headers = headers;
|
|
469
|
+
if (opts.redirect)
|
|
470
|
+
init.redirect = opts.redirect;
|
|
471
|
+
if (opts.method !== undefined)
|
|
472
|
+
init.method = opts.method;
|
|
473
|
+
if (opts.body !== undefined)
|
|
474
|
+
init.body = opts.body;
|
|
475
|
+
return init;
|
|
476
|
+
};
|
|
477
|
+
const maxAttempts = 3;
|
|
478
|
+
let lastErr;
|
|
479
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
480
|
+
try {
|
|
481
|
+
const r = await fetch(url, buildInit());
|
|
482
|
+
// (b)/(c) Intercept 304 BEFORE the r.ok gate; NEVER read its (absent) body.
|
|
483
|
+
if (r.status === 304) {
|
|
484
|
+
if (!cache) {
|
|
485
|
+
// A 304 with no held validators is a protocol violation — fail honestly
|
|
486
|
+
// rather than fabricate an empty body.
|
|
487
|
+
throw new ToolErrorCarrier({
|
|
488
|
+
kind: "schema_drift",
|
|
489
|
+
message: `Unexpected 304 from ${opts.label} without a cache entry.`,
|
|
490
|
+
retryable: false,
|
|
491
|
+
upstreamEndpoint: opts.label,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
body: cache.body,
|
|
496
|
+
notModified: true,
|
|
497
|
+
provenance: cache.asOf
|
|
498
|
+
? { dataPath: "snapshot", asOf: cache.asOf }
|
|
499
|
+
: { dataPath: "live" },
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if (r.ok) {
|
|
503
|
+
return {
|
|
504
|
+
body: (await r.json()),
|
|
505
|
+
notModified: false,
|
|
506
|
+
provenance: { dataPath: "live" },
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
const err = errorFromResponse(r, opts.label);
|
|
510
|
+
if (!err.retryable || attempt === maxAttempts) {
|
|
511
|
+
throw new ToolErrorCarrier(err);
|
|
512
|
+
}
|
|
513
|
+
lastErr = err;
|
|
514
|
+
const wait = err.retryAfterSeconds
|
|
515
|
+
? Math.min(err.retryAfterSeconds, 60)
|
|
516
|
+
: Math.pow(2, attempt - 1);
|
|
517
|
+
await new Promise((res) => setTimeout(res, wait * 1000));
|
|
518
|
+
}
|
|
519
|
+
catch (e) {
|
|
520
|
+
if (e instanceof ToolErrorCarrier)
|
|
521
|
+
throw e;
|
|
522
|
+
// Timeout/abort ⇒ fast-fail non-retryable (mirrors fetchWithRetry:162-172).
|
|
523
|
+
if (e instanceof Error &&
|
|
524
|
+
(e.name === "TimeoutError" || e.name === "AbortError")) {
|
|
525
|
+
throw new ToolErrorCarrier({
|
|
526
|
+
kind: "upstream_unavailable",
|
|
527
|
+
message: `Request to ${opts.label} timed out.`,
|
|
528
|
+
retryable: false,
|
|
529
|
+
upstreamEndpoint: opts.label,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
lastErr = {
|
|
533
|
+
kind: "upstream_unavailable",
|
|
534
|
+
message: `Network error reaching ${opts.label}: ${e.message}`,
|
|
535
|
+
retryable: true,
|
|
536
|
+
retryAfterSeconds: 30,
|
|
537
|
+
upstreamEndpoint: opts.label,
|
|
538
|
+
};
|
|
539
|
+
if (attempt === maxAttempts)
|
|
540
|
+
throw new ToolErrorCarrier(lastErr);
|
|
541
|
+
await new Promise((res) => setTimeout(res, Math.pow(2, attempt - 1) * 1000));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
throw new ToolErrorCarrier(lastErr ?? {
|
|
545
|
+
kind: "unknown",
|
|
546
|
+
message: `${opts.label} failed after ${maxAttempts} attempts.`,
|
|
547
|
+
retryable: false,
|
|
548
|
+
upstreamEndpoint: opts.label,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
//# sourceMappingURL=datasource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datasource.js","sourceRoot":"","sources":["../src/datasource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* disclosure.ts — the single audited disclosure-note TOKENIZER shared across the
|
|
3
|
+
* keyless DataSources (ADR-0022: an honesty-layer primitive, split into its own
|
|
4
|
+
* tiny single-concern module — the sibling of coerce.ts's map-layer primitives).
|
|
5
|
+
*
|
|
6
|
+
* WHY IT EXISTS: two independent sources (NSF Awards C108 = the OR-note,
|
|
7
|
+
* ClinicalTrials.gov C109 = the AND-note) each built a mandatory multi-token
|
|
8
|
+
* disclosure note from the SAME character-for-character delimiter class, and
|
|
9
|
+
* adversarial verification caught the SAME latent gap on BOTH — a whitespace-only
|
|
10
|
+
* `.split(/\s+/)` misses the API's PUNCTUATION delimiters, so a compound that only
|
|
11
|
+
* LOOKS like one token ("coral-reef" = coral OR reef; "Sanofi-Aventis" = Sanofi
|
|
12
|
+
* AND Aventis) leaks through and the mandatory note is silently SKIPPED. Hoisting
|
|
13
|
+
* ONE audited tokenizer removes the drift risk (a class regression now fails NSF
|
|
14
|
+
* AND ClinicalTrials suites at once instead of silently in one) and gives the lint
|
|
15
|
+
* guardrail (lint-invariants.mjs) + the parity fault test a single home to point
|
|
16
|
+
* at: "the ONLY sanctioned way to tokenize a disclosure value lives here."
|
|
17
|
+
*
|
|
18
|
+
* THE CLASS (`DISCLOSURE_SPLIT_RE`) is the PRECISE ES/Essie confirmed-splitter set,
|
|
19
|
+
* live-verified byte-identically on BOTH sources 2026-07-12:
|
|
20
|
+
* - SPLIT (→ multi-token, the note fires): whitespace + `- , / ; + & | @ # =`
|
|
21
|
+
* - DO NOT split (→ one token, no note): `. : _ '` (NSF also `\` `*`)
|
|
22
|
+
* It is deliberately NOT a `[^A-Za-z0-9]+` superset — that would over-disclose a
|
|
23
|
+
* split the APIs did NOT make on `.`/`_`/`'` (a fabricated union/conjunction). `-`
|
|
24
|
+
* is placed LAST (a literal, not a range); `/` is escaped for the regex delimiter.
|
|
25
|
+
*
|
|
26
|
+
* NOTE-AGNOSTIC BY DESIGN: this returns ONLY the token array. The `.length > 1`
|
|
27
|
+
* decision, the per-source note WORDING (NSF = OR-union, ClinicalTrials = AND
|
|
28
|
+
* co-occurrence), and any downstream suppression (ClinicalTrials' sponsor
|
|
29
|
+
* broadening-note) stay in each CALLER — they are genuinely source-specific and do
|
|
30
|
+
* NOT belong to the tokenizer.
|
|
31
|
+
*
|
|
32
|
+
* THE OPTIONAL `splitRe` PARAM is the sanctioned, honest escape hatch: a FUTURE
|
|
33
|
+
* source whose analyzer genuinely splits on a DIFFERENT class routes through THIS
|
|
34
|
+
* helper with its own `splitRe` (explicit, greppable, reviewable) rather than
|
|
35
|
+
* re-inlining a raw `.split(/\s+/)`. That is exactly what lets the lint ban the
|
|
36
|
+
* bare whitespace split outright (there is one sanctioned tokenizer, not many).
|
|
37
|
+
*
|
|
38
|
+
* No new dep, no I/O, pure function — the exact shape of a coerce.ts primitive.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The shared ES/Essie confirmed-splitter class (byte-identical to NSF's former
|
|
42
|
+
* `NSF_KEYWORD_SPLIT_RE` and ClinicalTrials' former `CT_TOKEN_SPLIT_RE`). Stateless
|
|
43
|
+
* (no `/g`), so a single module-level RegExp is safe to share across callers.
|
|
44
|
+
*/
|
|
45
|
+
export declare const DISCLOSURE_SPLIT_RE: RegExp;
|
|
46
|
+
/**
|
|
47
|
+
* Tokenize a disclosure value for a multi-token honesty note: trim, split on the
|
|
48
|
+
* confirmed-splitter class (default `DISCLOSURE_SPLIT_RE`), and drop empty tokens.
|
|
49
|
+
* Returns ONLY the token array — the caller owns the `.length > 1` gate + note
|
|
50
|
+
* wording. `tokenizeForDisclosure("coral-reef")` → `["coral", "reef"]`;
|
|
51
|
+
* `tokenizeForDisclosure("web_service")` → `["web_service"]` (the class does NOT
|
|
52
|
+
* split `_`); `tokenizeForDisclosure("robotics")` → `["robotics"]`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function tokenizeForDisclosure(value: string, splitRe?: RegExp): string[];
|
|
55
|
+
//# sourceMappingURL=disclosure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disclosure.d.ts","sourceRoot":"","sources":["../src/disclosure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAA4B,GACpC,MAAM,EAAE,CAEV"}
|