@cliwant/mcp-sam-gov 1.2.0 → 1.4.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.
Files changed (91) hide show
  1. package/README.ja.md +22 -9
  2. package/README.ko.md +22 -9
  3. package/README.md +70 -12
  4. package/dist/bea.d.ts +105 -0
  5. package/dist/bea.d.ts.map +1 -0
  6. package/dist/bea.js +303 -0
  7. package/dist/bea.js.map +1 -0
  8. package/dist/census-economic.d.ts +1 -1
  9. package/dist/census-economic.d.ts.map +1 -1
  10. package/dist/census-economic.js +12 -6
  11. package/dist/census-economic.js.map +1 -1
  12. package/dist/cms-facility.d.ts +112 -0
  13. package/dist/cms-facility.d.ts.map +1 -0
  14. package/dist/cms-facility.js +311 -0
  15. package/dist/cms-facility.js.map +1 -0
  16. package/dist/cms-hospital.d.ts +105 -0
  17. package/dist/cms-hospital.d.ts.map +1 -0
  18. package/dist/cms-hospital.js +290 -0
  19. package/dist/cms-hospital.js.map +1 -0
  20. package/dist/cms-supplier.d.ts +133 -0
  21. package/dist/cms-supplier.d.ts.map +1 -0
  22. package/dist/cms-supplier.js +414 -0
  23. package/dist/cms-supplier.js.map +1 -0
  24. package/dist/cms-utilization.d.ts +113 -0
  25. package/dist/cms-utilization.d.ts.map +1 -0
  26. package/dist/cms-utilization.js +328 -0
  27. package/dist/cms-utilization.js.map +1 -0
  28. package/dist/courtlistener.d.ts +115 -0
  29. package/dist/courtlistener.d.ts.map +1 -0
  30. package/dist/courtlistener.js +398 -0
  31. package/dist/courtlistener.js.map +1 -0
  32. package/dist/cpsc.d.ts +81 -0
  33. package/dist/cpsc.d.ts.map +1 -0
  34. package/dist/cpsc.js +283 -0
  35. package/dist/cpsc.js.map +1 -0
  36. package/dist/dol.d.ts +118 -0
  37. package/dist/dol.d.ts.map +1 -0
  38. package/dist/dol.js +421 -0
  39. package/dist/dol.js.map +1 -0
  40. package/dist/epa-envirofacts.d.ts +97 -0
  41. package/dist/epa-envirofacts.d.ts.map +1 -0
  42. package/dist/epa-envirofacts.js +292 -0
  43. package/dist/epa-envirofacts.js.map +1 -0
  44. package/dist/fred.d.ts +1 -1
  45. package/dist/fred.js +1 -1
  46. package/dist/keys.d.ts +11 -8
  47. package/dist/keys.d.ts.map +1 -1
  48. package/dist/keys.js +55 -8
  49. package/dist/keys.js.map +1 -1
  50. package/dist/lda.d.ts +105 -0
  51. package/dist/lda.d.ts.map +1 -0
  52. package/dist/lda.js +317 -0
  53. package/dist/lda.js.map +1 -0
  54. package/dist/nhtsa.d.ts +91 -0
  55. package/dist/nhtsa.d.ts.map +1 -0
  56. package/dist/nhtsa.js +263 -0
  57. package/dist/nhtsa.js.map +1 -0
  58. package/dist/nonprofit.d.ts +116 -0
  59. package/dist/nonprofit.d.ts.map +1 -0
  60. package/dist/nonprofit.js +342 -0
  61. package/dist/nonprofit.js.map +1 -0
  62. package/dist/openfda-device.d.ts +85 -0
  63. package/dist/openfda-device.d.ts.map +1 -0
  64. package/dist/openfda-device.js +277 -0
  65. package/dist/openfda-device.js.map +1 -0
  66. package/dist/openfda.d.ts +133 -0
  67. package/dist/openfda.d.ts.map +1 -0
  68. package/dist/openfda.js +402 -0
  69. package/dist/openfda.js.map +1 -0
  70. package/dist/server.d.ts.map +1 -1
  71. package/dist/server.js +872 -6
  72. package/dist/server.js.map +1 -1
  73. package/package.json +2 -1
  74. package/src/bea.ts +372 -0
  75. package/src/census-economic.ts +12 -6
  76. package/src/cms-facility.ts +379 -0
  77. package/src/cms-hospital.ts +344 -0
  78. package/src/cms-supplier.ts +527 -0
  79. package/src/cms-utilization.ts +389 -0
  80. package/src/courtlistener.ts +465 -0
  81. package/src/cpsc.ts +333 -0
  82. package/src/dol.ts +515 -0
  83. package/src/epa-envirofacts.ts +342 -0
  84. package/src/fred.ts +1 -1
  85. package/src/keys.ts +60 -8
  86. package/src/lda.ts +385 -0
  87. package/src/nhtsa.ts +352 -0
  88. package/src/nonprofit.ts +460 -0
  89. package/src/openfda-device.ts +356 -0
  90. package/src/openfda.ts +495 -0
  91. package/src/server.ts +995 -6
@@ -0,0 +1,277 @@
1
+ /**
2
+ * openfda-device.ts — openFDA 510(k) DEVICE CLEARANCES (api.fda.gov) — the
3
+ * MEDICAL-DEVICE REGULATORY lane (ADR-0056). The FDA's premarket-notification
4
+ * (510(k)) clearances: the applicant, the device, the clearance number (K-number),
5
+ * the decision (date + description), the clearance type, the product code, the
6
+ * advisory committee, and geography.
7
+ *
8
+ * ★ THIS IS THE SAME SOURCE + ENVELOPE + CRUX as openfda.ts (ADR-0054). Same host
9
+ * (api.fda.gov), same response envelope `{ meta:{ results:{ skip, limit, total }},
10
+ * results:[…] }`, same ★no-match→HTTP-404-NOT_FOUND-as-honest-empty crux, same
11
+ * optional query-key K-test, same fixed-host SSRF idiom, same structured-only
12
+ * (no raw Lucene passthrough) search assembly. It REUSES openfda.ts's `fetchOpenfda`
13
+ * (the SSRF-guarded classified fetch) and `readOpenfdaError` (the error-body reader)
14
+ * verbatim — the 404-reclassification logic is NOT reinvented — plus `luceneQuote`
15
+ * (the phrase-escape), `openfdaApiKey` (the optional-key env seam), and `OPENFDA_HOST`.
16
+ *
17
+ * GET https://api.fda.gov/device/510k.json
18
+ * ?search=<lucene>&limit=<1..100>&skip=<offset>[&api_key=<KEY>]
19
+ * → { meta:{ disclaimer, results:{ skip, limit, total }}, results:[ {…} ] }
20
+ * (live total ~175507).
21
+ *
22
+ * ★ HONESTY (mirrors openfda.ts exactly):
23
+ * [P1] totalAvailable = `meta.results.total` EXACT (e.g. 175507), NEVER
24
+ * results.length. skip/limit offset pagination:
25
+ * hasMore = skip + returned < total; nextOffset = hasMore ? skip+returned : null.
26
+ * [★P2] a 404 whose body is `{error:{code:"NOT_FOUND"}}` (a no-match query OR an
27
+ * unknown field) ⇒ HONEST EMPTY (returned:0, totalAvailable:0) — NOT thrown,
28
+ * NOT not_found. Any OTHER 4xx (e.g. a 400 syntax error) ⇒ invalid_input
29
+ * surfacing openFDA's message. 5xx/timeout ⇒ upstream_unavailable THROW. A
30
+ * 200 non-JSON body ⇒ schema_drift. (Reuses openfda.ts's exact fetch path.)
31
+ * [P3] dates (`decision_date`, YYYY-MM-DD) and every scalar surfaced as a STRING
32
+ * via `str` (null-never-empty-string) — no numeric coercion; never fabricated.
33
+ * [P4] `meta.results` or `results` absent / non-array ⇒ driftError (never a
34
+ * fabricated empty).
35
+ * [K-test] OPTIONAL OPENFDA_API_KEY: when set it rides `&api_key=` ONLY (openFDA has
36
+ * no header option — the query-key is inherent). Mitigation: the `label` is
37
+ * host+path ONLY (`openfda:/device/510k`, NO query), so no token reaches
38
+ * ToolError.upstreamEndpoint; `_meta.source` names the MODE only; the key is
39
+ * ABSENT from the serialized {data,_meta}, notes, and any log. Unset ⇒ keyless.
40
+ * [SSRF] fixed host `api.fda.gov` (fetchOpenfda asserts hostname/protocol +
41
+ * redirect:"error"); all filter VALUES are Lucene-escaped + phrase-quoted and
42
+ * ride URLSearchParams `search=`; limit/skip are integers; state is charclass
43
+ * `^[A-Za-z]{2}$`. No raw Lucene passthrough — structured only, injection-safe.
44
+ */
45
+ import { ToolErrorCarrier, errorFromResponse } from "./errors.js";
46
+ import { driftError } from "./datasource.js";
47
+ import { str } from "./coerce.js";
48
+ import { withMeta } from "./meta.js";
49
+ import { OPENFDA_HOST, fetchOpenfda, readOpenfdaError, luceneQuote, openfdaApiKey, } from "./openfda.js";
50
+ // state filter charclass (a 2-letter US state/territory postal code).
51
+ const STATE_RE = /^[A-Za-z]{2}$/;
52
+ const DEFAULT_LIMIT = 25;
53
+ const MAX_LIMIT = 100;
54
+ /** host+path-only label (→ ToolError.upstreamEndpoint); NEVER carries the key. */
55
+ const DEVICE_LABEL = "openfda:/device/510k";
56
+ // ─── Honesty notes (ADR-0056 required set) ────────────────────────
57
+ const NOT_DETERMINATION_NOTE = "openFDA 510(k) device clearances are FDA-published premarket-notification records; treat the decision/date/clearance-type as of the source's last publication. This is reference data, not a live regulatory determination.";
58
+ const KEYLESS_NOTE = "Keyless: no OPENFDA_API_KEY is set. openFDA allows ~1000 requests/day without a key; a free key raises the rate limit (get one at https://open.fda.gov/apis/authentication/).";
59
+ const KEYED_NOTE = "OPENFDA_API_KEY is set — it rides ONLY the &api_key= query parameter to api.fda.gov (openFDA has no header option), raising the rate limit. Its value is NEVER logged, echoed, or placed in this response.";
60
+ const NO_FILTER_NOTE = "No structured filters were applied — this is an unscoped scan of the WHOLE 510(k) device-clearance collection. Add applicant / deviceName / productCode / clearanceType / kNumber / state to scope the result set.";
61
+ /** Map ONE openFDA 510(k) result row → the curated shape. Every scalar via `str`. */
62
+ function mapClearance(row) {
63
+ const r = (row ?? {});
64
+ return {
65
+ applicant: str(r.applicant),
66
+ deviceName: str(r.device_name),
67
+ kNumber: str(r.k_number),
68
+ decisionDate: str(r.decision_date),
69
+ decisionDescription: str(r.decision_description),
70
+ clearanceType: str(r.clearance_type),
71
+ productCode: str(r.product_code),
72
+ advisoryCommittee: str(r.advisory_committee),
73
+ state: str(r.state),
74
+ };
75
+ }
76
+ /**
77
+ * Assemble the openFDA `search=` Lucene string from structured 510(k) filters — each
78
+ * value Lucene-escaped + phrase-quoted (reusing openfda.ts's `luceneQuote`), joined by
79
+ * ` AND `. Returns "" when no filter is present (openFDA then returns the whole
80
+ * collection). The clause → field mapping is FIXED here; a caller can never inject a
81
+ * raw field:value (no raw Lucene passthrough).
82
+ */
83
+ export function buildDeviceSearch(f) {
84
+ const clauses = [];
85
+ if (f.applicant !== undefined)
86
+ clauses.push(`applicant:${luceneQuote(f.applicant)}`);
87
+ if (f.deviceName !== undefined)
88
+ clauses.push(`device_name:${luceneQuote(f.deviceName)}`);
89
+ if (f.productCode !== undefined)
90
+ clauses.push(`product_code:${luceneQuote(f.productCode)}`);
91
+ if (f.clearanceType !== undefined)
92
+ clauses.push(`clearance_type:${luceneQuote(f.clearanceType)}`);
93
+ if (f.kNumber !== undefined)
94
+ clauses.push(`k_number:${luceneQuote(f.kNumber)}`);
95
+ if (f.state !== undefined)
96
+ clauses.push(`state:${luceneQuote(f.state.toUpperCase())}`);
97
+ return clauses.join(" AND ");
98
+ }
99
+ /**
100
+ * Search openFDA 510(k) DEVICE CLEARANCES with structured filters → curated clearance
101
+ * rows + honest `_meta`. KEYLESS (an OPTIONAL OPENFDA_API_KEY only raises the rate
102
+ * limit). totalAvailable = meta.results.total (EXACT); skip/limit offset pagination.
103
+ * ★A no-match query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty, never a throw.
104
+ */
105
+ export async function deviceClearances(args) {
106
+ // ── Validate + default the inputs (belt-and-suspenders behind the server Zod; a
107
+ // DIRECT handler call bypasses Zod). ──
108
+ if (args.state !== undefined && !STATE_RE.test(args.state)) {
109
+ throw new ToolErrorCarrier({
110
+ kind: "invalid_input",
111
+ retryable: false,
112
+ message: `Invalid state ${JSON.stringify(args.state)} — expected a 2-letter US state/territory postal code (^[A-Za-z]{2}$), e.g. "CA".`,
113
+ upstreamEndpoint: DEVICE_LABEL,
114
+ });
115
+ }
116
+ const limit = clampLimit(args.limit);
117
+ const skip = clampSkip(args.skip);
118
+ // ── Assemble the query (structured-only; all VALUES via URLSearchParams — no
119
+ // host/path steer). The OPTIONAL key rides ONLY here in &api_key=. ──
120
+ const filters = {
121
+ applicant: args.applicant,
122
+ deviceName: args.deviceName,
123
+ productCode: args.productCode,
124
+ clearanceType: args.clearanceType,
125
+ kNumber: args.kNumber,
126
+ state: args.state,
127
+ };
128
+ const search = buildDeviceSearch(filters);
129
+ const filtersApplied = [];
130
+ if (args.applicant !== undefined)
131
+ filtersApplied.push("applicant");
132
+ if (args.deviceName !== undefined)
133
+ filtersApplied.push("deviceName");
134
+ if (args.productCode !== undefined)
135
+ filtersApplied.push("productCode");
136
+ if (args.clearanceType !== undefined)
137
+ filtersApplied.push("clearanceType");
138
+ if (args.kNumber !== undefined)
139
+ filtersApplied.push("kNumber");
140
+ if (args.state !== undefined)
141
+ filtersApplied.push("state");
142
+ const params = new URLSearchParams();
143
+ if (search !== "")
144
+ params.set("search", search);
145
+ params.set("limit", String(limit));
146
+ params.set("skip", String(skip));
147
+ const key = openfdaApiKey();
148
+ if (key !== undefined)
149
+ params.set("api_key", key); // OPTIONAL — &api_key= ONLY
150
+ const url = `https://${OPENFDA_HOST}/device/510k.json?${params.toString()}`;
151
+ // ── Fetch + classify. ★P2 CRUX: a 404 whose body is {error:{code:"NOT_FOUND"}} is
152
+ // a genuine no-match (or an unknown field) ⇒ an HONEST EMPTY, never a thrown
153
+ // not_found. Any OTHER 4xx (e.g. 400 syntax) ⇒ invalid_input surfacing openFDA's
154
+ // message; a 5xx/429 ⇒ the shared taxonomy THROWS. (fetchOpenfda/readOpenfdaError
155
+ // are the SAME helpers as openfda.ts — the 404-reclassify logic is not reinvented.) ──
156
+ const res = await fetchOpenfda(url, DEVICE_LABEL);
157
+ if (res.status === 404) {
158
+ const { code, message } = await readOpenfdaError(res);
159
+ if (code === "NOT_FOUND") {
160
+ // Honest empty — NOT thrown, NOT not_found (the openFDA no-match idiom).
161
+ return emptyResult(limit, skip, filtersApplied, key !== undefined);
162
+ }
163
+ // A non-NOT_FOUND 404 ⇒ the shared not_found taxonomy (never a fake-empty).
164
+ throw new ToolErrorCarrier({
165
+ ...errorFromResponse(res, DEVICE_LABEL),
166
+ message: message
167
+ ? `openFDA returned HTTP 404 at ${DEVICE_LABEL}: ${message}`
168
+ : `Resource not found at ${DEVICE_LABEL} (HTTP 404).`,
169
+ });
170
+ }
171
+ if (res.status >= 400 && res.status < 500 && res.status !== 429) {
172
+ // A 4xx OTHER than 404/429 (e.g. 400 syntax) ⇒ invalid_input surfacing the
173
+ // openFDA error message (a caller-fixable request, never a fake-empty).
174
+ const { message } = await readOpenfdaError(res);
175
+ throw new ToolErrorCarrier({
176
+ kind: "invalid_input",
177
+ retryable: false,
178
+ message: message
179
+ ? `openFDA rejected the request (HTTP ${res.status}) at ${DEVICE_LABEL}: ${message}`
180
+ : `Bad request (HTTP ${res.status}) at ${DEVICE_LABEL}.`,
181
+ upstreamStatus: res.status,
182
+ upstreamEndpoint: DEVICE_LABEL,
183
+ });
184
+ }
185
+ if (!res.ok) {
186
+ // 429 → rate_limited; 5xx → upstream_unavailable (a DOWN service is NEVER an empty
187
+ // result). Delegated to the shared errors.ts taxonomy.
188
+ throw new ToolErrorCarrier(errorFromResponse(res, DEVICE_LABEL));
189
+ }
190
+ // ── 200 ⇒ parse JSON; a non-JSON body ⇒ r.json() SyntaxError ⇒ schema_drift (never
191
+ // read as an empty result). ──
192
+ let body;
193
+ try {
194
+ body = await res.json();
195
+ }
196
+ catch (e) {
197
+ if (e instanceof SyntaxError) {
198
+ throw driftError(DEVICE_LABEL, `openFDA ${DEVICE_LABEL} returned a non-JSON body at HTTP 200 — schema drift (never read as an empty result).`);
199
+ }
200
+ throw e;
201
+ }
202
+ // ── [P4] meta.results (the total carrier) and results (the rows) MUST be present +
203
+ // well-shaped; anything else is drift, never a fabricated empty. ──
204
+ const b = (body ?? {});
205
+ const meta = (b.meta ?? {});
206
+ const metaResults = meta.results;
207
+ if (metaResults === undefined ||
208
+ metaResults === null ||
209
+ typeof metaResults !== "object") {
210
+ throw driftError(DEVICE_LABEL, `openFDA ${DEVICE_LABEL} shape drift — meta.results (the skip/limit/total carrier) is missing.`);
211
+ }
212
+ if (!Array.isArray(b.results)) {
213
+ throw driftError(DEVICE_LABEL, `openFDA ${DEVICE_LABEL} shape drift — results must be an array.`);
214
+ }
215
+ const clearances = b.results.map(mapClearance);
216
+ const returned = clearances.length;
217
+ // ── [P1] totalAvailable = meta.results.total EXACT (the REAL total), NEVER
218
+ // results.length. skip/limit offset pagination. ──
219
+ const rawTotal = metaResults.total;
220
+ const totalAvailable = typeof rawTotal === "number" && Number.isFinite(rawTotal) ? rawTotal : null;
221
+ const hasMore = totalAvailable !== null && skip + returned < totalAvailable;
222
+ const nextOffset = hasMore ? skip + returned : null;
223
+ const notes = [NOT_DETERMINATION_NOTE, keyNote(key !== undefined)];
224
+ if (filtersApplied.length === 0)
225
+ notes.push(NO_FILTER_NOTE);
226
+ return withMeta({ clearances }, {
227
+ // MODE only — never the key value (K-test).
228
+ source: `${OPENFDA_HOST} /device/510k (openFDA 510(k) device clearances; ${key !== undefined ? "OPENFDA_API_KEY rate-limit key applied" : "keyless"})`,
229
+ keylessMode: true, // a keyless tool; the optional key only raises the rate limit
230
+ returned,
231
+ totalAvailable,
232
+ filtersApplied,
233
+ filtersDropped: [],
234
+ fieldsUnavailable: [],
235
+ pagination: { offset: skip, limit, hasMore, nextOffset },
236
+ notes,
237
+ });
238
+ }
239
+ // ─── Small helpers ────────────────────────────────────────────────
240
+ function keyNote(hasKey) {
241
+ return hasKey ? KEYED_NOTE : KEYLESS_NOTE;
242
+ }
243
+ /** An honest empty result (★P2: a 404 NOT_FOUND no-match) — returned:0, total:0. */
244
+ function emptyResult(limit, skip, filtersApplied, hasKey) {
245
+ return withMeta({ clearances: [] }, {
246
+ source: `${OPENFDA_HOST} /device/510k (openFDA 510(k) device clearances; ${hasKey ? "OPENFDA_API_KEY rate-limit key applied" : "keyless"})`,
247
+ keylessMode: true,
248
+ returned: 0,
249
+ totalAvailable: 0,
250
+ filtersApplied,
251
+ filtersDropped: [],
252
+ fieldsUnavailable: [],
253
+ pagination: { offset: skip, limit, hasMore: false, nextOffset: null },
254
+ notes: [
255
+ "No 510(k) device clearances matched this query (openFDA returned HTTP 404 NOT_FOUND — the source's honest no-match). This is an exact empty, not an error.",
256
+ NOT_DETERMINATION_NOTE,
257
+ keyNote(hasKey),
258
+ ],
259
+ });
260
+ }
261
+ function clampLimit(v) {
262
+ if (typeof v !== "number" || !Number.isFinite(v))
263
+ return DEFAULT_LIMIT;
264
+ const n = Math.floor(v);
265
+ if (n < 1)
266
+ return 1;
267
+ if (n > MAX_LIMIT)
268
+ return MAX_LIMIT;
269
+ return n;
270
+ }
271
+ function clampSkip(v) {
272
+ if (typeof v !== "number" || !Number.isFinite(v))
273
+ return 0;
274
+ const n = Math.floor(v);
275
+ return n < 0 ? 0 : n;
276
+ }
277
+ //# sourceMappingURL=openfda-device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfda-device.js","sourceRoot":"","sources":["../src/openfda-device.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * openfda.ts — openFDA recall/enforcement records (api.fda.gov) — the
3
+ * PRODUCT-SAFETY / RECALL lane (ADR-0054). Drug / device / food recall
4
+ * enforcement reports: the recalling firm, the product, the reason, the FDA
5
+ * classification (Class I/II/III), status, and geography.
6
+ *
7
+ * ★ THIS IS A KEYLESS TOOL WITH AN *OPTIONAL* RATE-LIMIT KEY. openFDA works
8
+ * keyless (~1000 requests/day); a free OPENFDA_API_KEY only RAISES the rate
9
+ * limit. So — unlike Census/FRED/BEA/DOL (key-REQUIRED, throw without it) —
10
+ * this tool NEVER throws for a missing key. When OPENFDA_API_KEY IS set it
11
+ * rides ONLY the `&api_key=` query param (openFDA has NO header option — query
12
+ * only). See the K-test discipline below.
13
+ *
14
+ * GET https://api.fda.gov/{category}/enforcement.json
15
+ * ?search=<lucene>&limit=<1..100>&skip=<offset>[&api_key=<KEY>]
16
+ * category ∈ {drug, device, food}
17
+ * → { meta: { disclaimer, results: { skip, limit, total } }, results: [ {...} ] }
18
+ *
19
+ * This module COPIES (does NOT import) the census-economic.ts bespoke-fetch idiom
20
+ * (a single classified `fetch` rather than the shared getJson) — the reason is
21
+ * the ★P2 CRUX below: a no-match query returns HTTP 404, and getJson→
22
+ * fetchWithRetry throws a `not_found` ToolErrorCarrier that DISCARDS the body, so
23
+ * we could not distinguish a genuine no-match (→ honest empty) from a real 404.
24
+ * A bespoke fetch returns the Response so we can READ the 404 body and reclassify
25
+ * `{error:{code:"NOT_FOUND"}}` → an honest empty. Coercion/meta code is REUSED
26
+ * (`str` coerce.ts null-never-empty-string, `driftError`, `errorFromResponse`,
27
+ * `withMeta`/`buildMeta` with skip/limit offset pagination). NO local str/num.
28
+ *
29
+ * ★ HONESTY (ADR-0054 P1–P5):
30
+ * [P1] totalAvailable = `meta.results.total` EXACT (the REAL total, e.g. drug
31
+ * 17793), NEVER results.length. skip/limit offset pagination:
32
+ * hasMore = skip + returned < total; nextOffset = hasMore ? skip+returned : null.
33
+ * [★P2] a 404 whose body is `{error:{code:"NOT_FOUND"}}` (a no-match query OR an
34
+ * unknown field) ⇒ HONEST EMPTY (returned:0, totalAvailable:0) — NOT thrown,
35
+ * NOT not_found. Any OTHER 4xx (e.g. a 400 syntax error) ⇒ invalid_input
36
+ * surfacing openFDA's error message. 5xx/timeout ⇒ upstream_unavailable
37
+ * THROW. A 200 non-JSON body ⇒ schema_drift. (Reverting the
38
+ * 404-NOT_FOUND-as-empty handling ⇒ RED.)
39
+ * [P3] dates (`recall_initiation_date`, YYYYMMDD) and every scalar surfaced as a
40
+ * STRING via `str` (null-never-empty-string) — no numeric coercion; never
41
+ * fabricated.
42
+ * [P4] `meta.results` or `results` absent / non-array ⇒ driftError (never a
43
+ * fabricated empty).
44
+ * [K-test] OPTIONAL key: when OPENFDA_API_KEY is set it rides `&api_key=` ONLY,
45
+ * so the key WILL appear in the raw fetch URL (an unavoidable openFDA
46
+ * constraint — no header option). Mitigation: the `label` is host+path
47
+ * ONLY (`openfda:/{category}/enforcement`, NO query), so no token can reach
48
+ * ToolError.upstreamEndpoint; `_meta.source` names the MODE only; the key is
49
+ * ABSENT from the serialized {data,_meta}, notes, and any log. Unset ⇒
50
+ * keyless (no api_key param at all).
51
+ * [SSRF] fixed host `api.fda.gov`; `category` is an ENUM (→ the path segment);
52
+ * all filter VALUES are Lucene-escaped + phrase-quoted and ride
53
+ * URLSearchParams `search=`; limit/skip are integers; state is charclass
54
+ * `^[A-Za-z]{2}$`. A post-construction hostname/protocol assertion +
55
+ * `redirect:"error"` lock it (no raw Lucene passthrough — structured only,
56
+ * injection-safe).
57
+ */
58
+ import { type MetaBundle } from "./meta.js";
59
+ export declare const OPENFDA_HOST = "api.fda.gov";
60
+ export declare const OPENFDA_CATEGORIES: readonly ["drug", "device", "food"];
61
+ export type OpenfdaCategory = (typeof OPENFDA_CATEGORIES)[number];
62
+ export declare const OPENFDA_CLASSIFICATIONS: readonly ["Class I", "Class II", "Class III"];
63
+ /** Read OPENFDA_API_KEY from env; trim; return the value or undefined (unset/blank). */
64
+ export declare function openfdaApiKey(): string | undefined;
65
+ export type OpenfdaRecall = {
66
+ recallingFirm: string | null;
67
+ productDescription: string | null;
68
+ reasonForRecall: string | null;
69
+ classification: string | null;
70
+ status: string | null;
71
+ state: string | null;
72
+ city: string | null;
73
+ recallInitiationDate: string | null;
74
+ recallNumber: string | null;
75
+ voluntaryMandated: string | null;
76
+ distributionPattern: string | null;
77
+ };
78
+ /**
79
+ * Quote a filter value as a Lucene phrase term. Wrapping in double quotes makes a
80
+ * multi-word value (e.g. "Class I", "johnson & johnson") match as a phrase and
81
+ * closes every operator-injection surface; within the quoted term only `"` and
82
+ * `\` are special, so we backslash-escape BOTH (a `"` in the value can never break
83
+ * out of the quotes). There is NO raw Lucene passthrough — the tool assembles the
84
+ * whole `search=` string from validated typed args.
85
+ */
86
+ export declare function luceneQuote(v: string): string;
87
+ /** The structured filter set → openFDA `field:value` clauses. */
88
+ export type OpenfdaFilters = {
89
+ firm?: string;
90
+ product?: string;
91
+ reason?: string;
92
+ classification?: string;
93
+ status?: string;
94
+ state?: string;
95
+ };
96
+ /**
97
+ * Assemble the openFDA `search=` Lucene string from structured filters — each
98
+ * value Lucene-escaped + phrase-quoted, joined by ` AND `. Returns "" when no
99
+ * filter is present (openFDA then returns the whole category). The mapping of
100
+ * clause → field is FIXED here; a caller can never inject a raw field:value.
101
+ */
102
+ export declare function buildSearch(f: OpenfdaFilters): string;
103
+ /**
104
+ * A SINGLE classified `fetch` to api.fda.gov (NOT the shared getJson — we must
105
+ * READ a 404 body to distinguish a NOT_FOUND no-match from a real outage; see the
106
+ * ★P2 crux in the caller). Builds the URL on the FIXED host from `params`, asserts
107
+ * the constructed URL cannot have been steered off-host (belt-and-suspenders), and
108
+ * sets `redirect:"error"` (fail closed on any off-host 3xx — a redirect could
109
+ * carry the api_key away). Returns the raw Response for the caller to classify. A
110
+ * timeout/abort ⇒ non-retryable upstream_unavailable; a network TypeError ⇒
111
+ * retryable upstream_unavailable; a redirect TypeError ⇒ schema_drift (never a
112
+ * fake-empty). `label` is host+path only.
113
+ */
114
+ export declare function fetchOpenfda(url: string, label: string): Promise<Response>;
115
+ /** Best-effort read of an openFDA error body `{error:{code,message}}`. null on any failure. */
116
+ export declare function readOpenfdaError(res: Response): Promise<{
117
+ code: string | null;
118
+ message: string | null;
119
+ }>;
120
+ export type OpenfdaEnforcementArgs = OpenfdaFilters & {
121
+ category?: string;
122
+ limit?: number;
123
+ skip?: number;
124
+ };
125
+ /**
126
+ * Search openFDA recall/enforcement records for a category (drug/device/food) with
127
+ * structured filters → curated recall rows + honest `_meta`. KEYLESS (an OPTIONAL
128
+ * OPENFDA_API_KEY only raises the rate limit). totalAvailable = meta.results.total
129
+ * (EXACT); skip/limit offset pagination. ★A no-match query (openFDA HTTP 404
130
+ * NOT_FOUND) ⇒ an honest empty, never a throw.
131
+ */
132
+ export declare function enforcement(args: OpenfdaEnforcementArgs): Promise<MetaBundle>;
133
+ //# sourceMappingURL=openfda.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfda.d.ts","sourceRoot":"","sources":["../src/openfda.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAKH,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAGzE,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAG1C,eAAO,MAAM,kBAAkB,qCAAsC,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIlE,eAAO,MAAM,uBAAuB,+CAAgD,CAAC;AA2BrF,wFAAwF;AACxF,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAIlD;AAGD,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC;AAqBF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAarD;AAGD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyChF;AAED,+FAA+F;AAC/F,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAY1D;AAGD,MAAM,MAAM,sBAAsB,GAAG,cAAc,GAAG;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,UAAU,CAAC,CA8KrB"}