@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.
Files changed (228) hide show
  1. package/LICENSE +21 -21
  2. package/README.ja.md +48 -16
  3. package/README.ko.md +48 -16
  4. package/README.md +226 -64
  5. package/dist/attachments.d.ts +63 -0
  6. package/dist/attachments.d.ts.map +1 -0
  7. package/dist/attachments.js +576 -0
  8. package/dist/attachments.js.map +1 -0
  9. package/dist/bls.d.ts +267 -0
  10. package/dist/bls.d.ts.map +1 -0
  11. package/dist/bls.js +1549 -0
  12. package/dist/bls.js.map +1 -0
  13. package/dist/cache.d.ts +2 -0
  14. package/dist/cache.d.ts.map +1 -1
  15. package/dist/cache.js +4 -0
  16. package/dist/cache.js.map +1 -1
  17. package/dist/census.d.ts +182 -0
  18. package/dist/census.d.ts.map +1 -0
  19. package/dist/census.js +576 -0
  20. package/dist/census.js.map +1 -0
  21. package/dist/ckan.d.ts +141 -0
  22. package/dist/ckan.d.ts.map +1 -0
  23. package/dist/ckan.js +393 -0
  24. package/dist/ckan.js.map +1 -0
  25. package/dist/clinicaltrials.d.ts +180 -0
  26. package/dist/clinicaltrials.d.ts.map +1 -0
  27. package/dist/clinicaltrials.js +730 -0
  28. package/dist/clinicaltrials.js.map +1 -0
  29. package/dist/cms.d.ts +140 -0
  30. package/dist/cms.d.ts.map +1 -0
  31. package/dist/cms.js +482 -0
  32. package/dist/cms.js.map +1 -0
  33. package/dist/coerce.d.ts +32 -0
  34. package/dist/coerce.d.ts.map +1 -0
  35. package/dist/coerce.js +50 -0
  36. package/dist/coerce.js.map +1 -0
  37. package/dist/datagov-catalog.d.ts +84 -0
  38. package/dist/datagov-catalog.d.ts.map +1 -0
  39. package/dist/datagov-catalog.js +233 -0
  40. package/dist/datagov-catalog.js.map +1 -0
  41. package/dist/datagov.d.ts +146 -0
  42. package/dist/datagov.d.ts.map +1 -0
  43. package/dist/datagov.js +689 -0
  44. package/dist/datagov.js.map +1 -0
  45. package/dist/datagovKey.d.ts +36 -0
  46. package/dist/datagovKey.d.ts.map +1 -0
  47. package/dist/datagovKey.js +60 -0
  48. package/dist/datagovKey.js.map +1 -0
  49. package/dist/datasource.d.ts +313 -0
  50. package/dist/datasource.d.ts.map +1 -0
  51. package/dist/datasource.js +551 -0
  52. package/dist/datasource.js.map +1 -0
  53. package/dist/disclosure.d.ts +55 -0
  54. package/dist/disclosure.d.ts.map +1 -0
  55. package/dist/disclosure.js +57 -0
  56. package/dist/disclosure.js.map +1 -0
  57. package/dist/ecfr.d.ts +4 -2
  58. package/dist/ecfr.d.ts.map +1 -1
  59. package/dist/ecfr.js +74 -1
  60. package/dist/ecfr.js.map +1 -1
  61. package/dist/echo.d.ts +143 -0
  62. package/dist/echo.d.ts.map +1 -0
  63. package/dist/echo.js +424 -0
  64. package/dist/echo.js.map +1 -0
  65. package/dist/edgar.d.ts +377 -0
  66. package/dist/edgar.d.ts.map +1 -0
  67. package/dist/edgar.js +2418 -0
  68. package/dist/edgar.js.map +1 -0
  69. package/dist/errors.d.ts +26 -0
  70. package/dist/errors.d.ts.map +1 -1
  71. package/dist/errors.js +69 -1
  72. package/dist/errors.js.map +1 -1
  73. package/dist/fac.d.ts +180 -0
  74. package/dist/fac.d.ts.map +1 -0
  75. package/dist/fac.js +416 -0
  76. package/dist/fac.js.map +1 -0
  77. package/dist/far.d.ts +170 -0
  78. package/dist/far.d.ts.map +1 -0
  79. package/dist/far.js +804 -0
  80. package/dist/far.js.map +1 -0
  81. package/dist/fdic.d.ts +599 -0
  82. package/dist/fdic.d.ts.map +1 -0
  83. package/dist/fdic.js +1624 -0
  84. package/dist/fdic.js.map +1 -0
  85. package/dist/federal-register.d.ts +139 -2
  86. package/dist/federal-register.d.ts.map +1 -1
  87. package/dist/federal-register.js +419 -3
  88. package/dist/federal-register.js.map +1 -1
  89. package/dist/fema.d.ts +181 -0
  90. package/dist/fema.d.ts.map +1 -0
  91. package/dist/fema.js +436 -0
  92. package/dist/fema.js.map +1 -0
  93. package/dist/fpds.d.ts +108 -0
  94. package/dist/fpds.d.ts.map +1 -0
  95. package/dist/fpds.js +519 -0
  96. package/dist/fpds.js.map +1 -0
  97. package/dist/gao.d.ts +64 -0
  98. package/dist/gao.d.ts.map +1 -0
  99. package/dist/gao.js +640 -0
  100. package/dist/gao.js.map +1 -0
  101. package/dist/govinfo.d.ts +111 -0
  102. package/dist/govinfo.d.ts.map +1 -0
  103. package/dist/govinfo.js +422 -0
  104. package/dist/govinfo.js.map +1 -0
  105. package/dist/grants.d.ts +27 -4
  106. package/dist/grants.d.ts.map +1 -1
  107. package/dist/grants.js +111 -6
  108. package/dist/grants.js.map +1 -1
  109. package/dist/gsa-csv.d.ts +249 -0
  110. package/dist/gsa-csv.d.ts.map +1 -0
  111. package/dist/gsa-csv.js +784 -0
  112. package/dist/gsa-csv.js.map +1 -0
  113. package/dist/integrity.d.ts +212 -0
  114. package/dist/integrity.d.ts.map +1 -0
  115. package/dist/integrity.js +707 -0
  116. package/dist/integrity.js.map +1 -0
  117. package/dist/meta.d.ts +165 -0
  118. package/dist/meta.d.ts.map +1 -0
  119. package/dist/meta.js +162 -0
  120. package/dist/meta.js.map +1 -0
  121. package/dist/nih.d.ts +117 -0
  122. package/dist/nih.d.ts.map +1 -0
  123. package/dist/nih.js +291 -0
  124. package/dist/nih.js.map +1 -0
  125. package/dist/nppes.d.ts +157 -0
  126. package/dist/nppes.d.ts.map +1 -0
  127. package/dist/nppes.js +648 -0
  128. package/dist/nppes.js.map +1 -0
  129. package/dist/nsf.d.ts +176 -0
  130. package/dist/nsf.d.ts.map +1 -0
  131. package/dist/nsf.js +554 -0
  132. package/dist/nsf.js.map +1 -0
  133. package/dist/nvd.d.ts +176 -0
  134. package/dist/nvd.d.ts.map +1 -0
  135. package/dist/nvd.js +912 -0
  136. package/dist/nvd.js.map +1 -0
  137. package/dist/ofac.d.ts +205 -0
  138. package/dist/ofac.d.ts.map +1 -0
  139. package/dist/ofac.js +919 -0
  140. package/dist/ofac.js.map +1 -0
  141. package/dist/pricing.d.ts +110 -0
  142. package/dist/pricing.d.ts.map +1 -0
  143. package/dist/pricing.js +843 -0
  144. package/dist/pricing.js.map +1 -0
  145. package/dist/sam-gov/client.d.ts +60 -2
  146. package/dist/sam-gov/client.d.ts.map +1 -1
  147. package/dist/sam-gov/client.js +320 -54
  148. package/dist/sam-gov/client.js.map +1 -1
  149. package/dist/sam-gov/index.d.ts +1 -1
  150. package/dist/sam-gov/index.d.ts.map +1 -1
  151. package/dist/sam-gov/index.js +1 -1
  152. package/dist/sam-gov/index.js.map +1 -1
  153. package/dist/sam-gov/types.d.ts +24 -0
  154. package/dist/sam-gov/types.d.ts.map +1 -1
  155. package/dist/sba.d.ts +72 -0
  156. package/dist/sba.d.ts.map +1 -0
  157. package/dist/sba.js +281 -0
  158. package/dist/sba.js.map +1 -0
  159. package/dist/server.d.ts +14 -2
  160. package/dist/server.d.ts.map +1 -1
  161. package/dist/server.js +3889 -296
  162. package/dist/server.js.map +1 -1
  163. package/dist/snapshot.d.ts +98 -0
  164. package/dist/snapshot.d.ts.map +1 -0
  165. package/dist/snapshot.js +146 -0
  166. package/dist/snapshot.js.map +1 -0
  167. package/dist/socrata.d.ts +157 -0
  168. package/dist/socrata.d.ts.map +1 -0
  169. package/dist/socrata.js +448 -0
  170. package/dist/socrata.js.map +1 -0
  171. package/dist/treasury.d.ts +143 -0
  172. package/dist/treasury.d.ts.map +1 -0
  173. package/dist/treasury.js +436 -0
  174. package/dist/treasury.js.map +1 -0
  175. package/dist/usaspending.d.ts +260 -65
  176. package/dist/usaspending.d.ts.map +1 -1
  177. package/dist/usaspending.js +1629 -201
  178. package/dist/usaspending.js.map +1 -1
  179. package/dist/usitc.d.ts +142 -0
  180. package/dist/usitc.d.ts.map +1 -0
  181. package/dist/usitc.js +339 -0
  182. package/dist/usitc.js.map +1 -0
  183. package/package.json +24 -2
  184. package/src/attachments.ts +652 -0
  185. package/src/bls.ts +1943 -0
  186. package/src/cache.ts +5 -0
  187. package/src/census.ts +735 -0
  188. package/src/ckan.ts +495 -0
  189. package/src/clinicaltrials.ts +923 -0
  190. package/src/cms.ts +634 -0
  191. package/src/coerce.ts +47 -0
  192. package/src/datagov-catalog.ts +296 -0
  193. package/src/datagov.ts +907 -0
  194. package/src/datagovKey.ts +68 -0
  195. package/src/datasource.ts +721 -0
  196. package/src/disclosure.ts +61 -0
  197. package/src/ecfr.ts +98 -1
  198. package/src/echo.ts +496 -0
  199. package/src/edgar.ts +3014 -0
  200. package/src/errors.ts +84 -1
  201. package/src/fac.ts +529 -0
  202. package/src/far.ts +1007 -0
  203. package/src/fdic.ts +2052 -0
  204. package/src/federal-register.ts +512 -3
  205. package/src/fema.ts +541 -0
  206. package/src/fpds.ts +620 -0
  207. package/src/gao.ts +744 -0
  208. package/src/govinfo.ts +497 -0
  209. package/src/grants.ts +139 -7
  210. package/src/gsa-csv.ts +992 -0
  211. package/src/integrity.ts +928 -0
  212. package/src/meta.ts +292 -0
  213. package/src/nih.ts +375 -0
  214. package/src/nppes.ts +834 -0
  215. package/src/nsf.ts +706 -0
  216. package/src/nvd.ts +1124 -0
  217. package/src/ofac.ts +1166 -0
  218. package/src/pricing.ts +1075 -0
  219. package/src/sam-gov/client.ts +345 -63
  220. package/src/sam-gov/index.ts +5 -1
  221. package/src/sam-gov/types.ts +22 -0
  222. package/src/sba.ts +357 -0
  223. package/src/server.ts +4551 -328
  224. package/src/snapshot.ts +192 -0
  225. package/src/socrata.ts +532 -0
  226. package/src/treasury.ts +575 -0
  227. package/src/usaspending.ts +1946 -214
  228. package/src/usitc.ts +420 -0
package/dist/fema.d.ts ADDED
@@ -0,0 +1,181 @@
1
+ /**
2
+ * OpenFEMA — keyless federal disaster declarations + emergency-assistance spend
3
+ * (ADR-0016). A NEW DOMAIN: federal disaster declarations + FEMA Public Assistance
4
+ * grants to state/local/tribal applicants (the SLED emergency-spend / B2G-adjacent
5
+ * angle). Fully PUBLIC, KEYLESS (no key, no token, no header — OpenFEMA is
6
+ * unauthenticated). Nth consumer of the fetch/map/meta shape after treasury /
7
+ * edgar / socrata / ckan.
8
+ * Row query: https://www.fema.gov/api/open/{version}/{EntityName}?{OData params}
9
+ *
10
+ * REUSE (writes ZERO fetch/coercion/error code): `getJson`+`redirect:"error"`
11
+ * (datasource.ts), `num`/`str` (coerce.ts), `driftError` (datasource.ts),
12
+ * `withMeta`/`buildMeta` (meta.ts), the errors.ts taxonomy. COPIES (does NOT
13
+ * import) the socrata/ckan fixed-host SSRF + honesty PATTERN. NEW (small): the
14
+ * OData `$filter` builder (structured args → escaped filter string) + the
15
+ * `FEMA_DATASETS` registry (entityName+version+per-tool field-whitelist pins).
16
+ *
17
+ * ★ THE TOTAL-HONESTY CRUX — `metadata.count` (`$inlinecount=allpages`).
18
+ * OpenFEMA reports the exact FILTERED total in `metadata.count` — a real JSON
19
+ * NUMBER — but ONLY when the query carries `$inlinecount=allpages`. WITHOUT it,
20
+ * `metadata.count` is `0` — a SENTINEL, not the real total (live-verified
21
+ * 2026-07-12: `?$top=1` → count:0; `?$top=1&$inlinecount=allpages` → count:70049).
22
+ * So `getOpenFema` ALWAYS sets `$inlinecount=allpages` (inside the fetch fn, not
23
+ * the caller — no code path can omit it) and reads `metadata.count` as
24
+ * `totalAvailable`. Reading count off a query that omitted inlinecount would
25
+ * report `totalAvailable:0` on a full page — a data-absence-as-zero lie. The count
26
+ * is EXACT (respects the filter), so `hasMore`/`complete` are exact — NO
27
+ * totalIsLowerBound / totalIsEstimated hedge.
28
+ *
29
+ * ★ Entity-keyed envelope (the shape quirk). Results live under `body[EntityName]`
30
+ * (e.g. `body.DisasterDeclarationsSummaries`), NOT a fixed `results`/`data`. The
31
+ * dataset registry carries the entity name, known ahead of the fetch. `metadata`
32
+ * carries the exact filtered `count`.
33
+ *
34
+ * ★ SSRF guard (policy① — mirror socrata.ts / ckan.ts fixed-host). Fixed host
35
+ * `www.fema.gov` + a CURATED dataset registry pinning `{entityName, version}` (no
36
+ * free host, no free path, no free version — HMA's live v2→v4 drift is exactly why
37
+ * version is pinned, never a caller param):
38
+ * 1. `datasetKey` ∈ the frozen `FEMA_DATASETS` registry (belt-and-suspenders
39
+ * recheck in the fetch fn → invalid_input before any fetch);
40
+ * 2. construct `https://www.fema.gov/api/open/${version}/${entityName}?${params}`
41
+ * (host + path segments come ONLY from the pinned registry entry — nothing
42
+ * caller-supplied interpolates into the path), then ASSERT the built URL's
43
+ * hostname === "www.fema.gov" and protocol === "https:" (else invalid_input);
44
+ * 3. OData params via URLSearchParams — `$inlinecount`/`$top`/`$skip` set by the
45
+ * module; `$filter` is MODULE-BUILT from structured args against a per-tool
46
+ * field whitelist (NO raw caller `$filter` — no tool exposes one). String
47
+ * values escape a single-quote by DOUBLING it (`'`→`''`, e.g. `O'Brien` →
48
+ * `'O''Brien'`) then wrap in `'...'`; numbers are unquoted; booleans render
49
+ * `true`/`false`. An un-whitelisted field is rejected (invalid_input) — zero
50
+ * filter-injection surface AND guaranteed-valid, live-verified field names;
51
+ * 4. `redirect:"error"` on every fetch — a direct-JSON endpoint 3xx is anomalous
52
+ * → fail closed, never follow off-host.
53
+ *
54
+ * ★ Silent-filter trap AVOIDED (unlike ECHO). A bad `$filter`/`$orderby` field is
55
+ * NOT silently ignored — OpenFEMA returns HTTP 400 (live-verified). We never
56
+ * present unfiltered-as-filtered. We still module-build `$filter` from a per-tool
57
+ * whitelist (belt-and-suspenders: no 400 in normal use + no injection surface).
58
+ *
59
+ * ★ 200-HTML maintenance-page guard (ADR-0016 OQ1). FEMA's Drupal can serve an
60
+ * "experiencing technical difficulties" HTML page. Observed failures were HTTP 404
61
+ * (→ not_found, thrown before r.json()); but IF an outage returns that page at
62
+ * HTTP 200, getJson's r.json() throws a SyntaxError. `getOpenFema` catches it →
63
+ * `driftError` (never a fake-empty, and a clean classification). Shared getJson
64
+ * stays byte-identical (the shape-drift check stays at the call site).
65
+ *
66
+ * ★ PER-DATASET FIELD NAMES DIFFER — live-verified (ADR-0016 M1). `state eq 'CA'`
67
+ * works on DisasterDeclarationsSummaries (1689) but `stateAbbreviation` → HTTP 400
68
+ * there; conversely `stateAbbreviation eq 'LA'` works on
69
+ * PublicAssistanceFundedProjectsDetails (39444) but `state` → HTTP 400 there. Each
70
+ * tool maps its user-facing `state` arg to ITS dataset's real OData field. Every
71
+ * shipped filter below NARROWS its dataset (live-verified 2026-07-12); a field
72
+ * that 400s or is silently ignored is NOT shipped.
73
+ *
74
+ * HMA v4 (HazardMitigationAssistanceProjects) is a 3rd-tool backlog item (per
75
+ * ADR-0016 §3) — slice 1 ships the 2 core tools.
76
+ */
77
+ import { num } from "./coerce.js";
78
+ import { type MetaBundle } from "./meta.js";
79
+ export { num };
80
+ export declare const FEMA_HOST = "www.fema.gov";
81
+ /**
82
+ * The frozen dataset registry (SSRF core — no free host, no free path, no free
83
+ * version). Each entry's count + fields are LIVE-VERIFIED keyless (2026-07-12).
84
+ * Adding a dataset later = a registry edit + a live
85
+ * `$top=1&$inlinecount=allpages` verify (entity key, version, count) + a test
86
+ * note — NEVER a free runtime param. Re-verify periodically against
87
+ * `/api/open/v1/DataSets` (versions drift: HMA already moved v2→v4).
88
+ */
89
+ export declare const FEMA_DATASETS: {
90
+ public_assistance: {
91
+ entityName: string;
92
+ version: string;
93
+ filterFields: Set<string>;
94
+ amountFields: string[];
95
+ };
96
+ disaster_declarations: {
97
+ entityName: string;
98
+ version: string;
99
+ filterFields: Set<string>;
100
+ amountFields: never[];
101
+ };
102
+ };
103
+ export type FemaDatasetKey = keyof typeof FEMA_DATASETS;
104
+ type FilterOp = "eq" | "ge" | "le";
105
+ export type FilterClause = {
106
+ field: string;
107
+ op: FilterOp;
108
+ type: "string";
109
+ value: string;
110
+ } | {
111
+ field: string;
112
+ op: FilterOp;
113
+ type: "number";
114
+ value: number;
115
+ } | {
116
+ field: string;
117
+ op: FilterOp;
118
+ type: "boolean";
119
+ value: boolean;
120
+ };
121
+ /** OData string-literal escaping: double every single-quote (`'` → `''`). This is
122
+ * the ONLY escaping OData needs for a `'...'` literal — zero injection surface. */
123
+ export declare function escapeODataString(v: string): string;
124
+ /**
125
+ * Build the `$filter` string from structured clauses against the dataset's
126
+ * whitelist. Each clause's field MUST be in `FEMA_DATASETS[datasetKey].filterFields`
127
+ * (belt-and-suspenders — the tool functions only ever pass whitelisted fields, but
128
+ * an un-whitelisted field is a hard invalid_input, never silently dropped). Returns
129
+ * null when there are no clauses (⇒ no `$filter` param).
130
+ */
131
+ export declare function buildFilter(datasetKey: FemaDatasetKey, clauses: FilterClause[]): string | null;
132
+ export type FemaRow = Record<string, unknown>;
133
+ /**
134
+ * Search FEMA Public Assistance funded projects (SLED emergency spend to state /
135
+ * local / tribal applicants). Dataset PublicAssistanceFundedProjectsDetails v2.
136
+ * Structured filters → module-built `$filter` (each field LIVE-VERIFIED to narrow):
137
+ * state → stateAbbreviation eq · disasterNumber eq · applicantId eq ·
138
+ * damageCategoryCode eq (e.g. "B" = Emergency Protective Measures) ·
139
+ * incidentType eq · minProjectAmount → projectAmount ge · maxProjectAmount →
140
+ * projectAmount le · declaredDateFrom/To → declarationDate ge/le.
141
+ * Rows carry projectAmount / federalShareObligated / totalObligated /
142
+ * mitigationAmount as number|null. Honest `_meta` (totalAvailable = exact filtered
143
+ * metadata.count).
144
+ */
145
+ export declare function searchPublicAssistance(args: {
146
+ state?: string;
147
+ disasterNumber?: number;
148
+ applicantId?: string;
149
+ damageCategoryCode?: string;
150
+ incidentType?: string;
151
+ minProjectAmount?: number;
152
+ maxProjectAmount?: number;
153
+ declaredDateFrom?: string;
154
+ declaredDateTo?: string;
155
+ limit?: number;
156
+ offset?: number;
157
+ }): Promise<MetaBundle>;
158
+ /**
159
+ * Look up FEMA disaster / emergency declarations. Dataset
160
+ * DisasterDeclarationsSummaries v2. Structured filters → module-built `$filter`
161
+ * (each field LIVE-VERIFIED to narrow):
162
+ * state → state eq · incidentType eq (e.g. "Flood") · declarationType eq
163
+ * (DR/EM/FM) · fyDeclared eq · disasterNumber eq · declaredDateFrom/To →
164
+ * declarationDate ge/le · paProgramDeclared / iaProgramDeclared → eq true/false
165
+ * (BOOLEAN — `eq 1` 400s upstream).
166
+ * Honest `_meta` (totalAvailable = exact filtered metadata.count).
167
+ */
168
+ export declare function disasterDeclarations(args: {
169
+ state?: string;
170
+ incidentType?: string;
171
+ declarationType?: string;
172
+ fyDeclared?: number;
173
+ disasterNumber?: number;
174
+ declaredDateFrom?: string;
175
+ declaredDateTo?: string;
176
+ paProgramDeclared?: boolean;
177
+ iaProgramDeclared?: boolean;
178
+ limit?: number;
179
+ offset?: number;
180
+ }): Promise<MetaBundle>;
181
+ //# sourceMappingURL=fema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fema.d.ts","sourceRoot":"","sources":["../src/fema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AAIH,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAKzE,OAAO,EAAE,GAAG,EAAE,CAAC;AAGf,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAkBxC;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;CAyCgB,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,aAAa,CAAC;AAKxD,KAAK,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACnC,MAAM,MAAM,YAAY,GACpB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAErE;oFACoF;AACpF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAeD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,YAAY,EAAE,GACtB,MAAM,GAAG,IAAI,CAef;AAGD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA8K9C;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmDtB;AAGD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmDtB"}
package/dist/fema.js ADDED
@@ -0,0 +1,436 @@
1
+ /**
2
+ * OpenFEMA — keyless federal disaster declarations + emergency-assistance spend
3
+ * (ADR-0016). A NEW DOMAIN: federal disaster declarations + FEMA Public Assistance
4
+ * grants to state/local/tribal applicants (the SLED emergency-spend / B2G-adjacent
5
+ * angle). Fully PUBLIC, KEYLESS (no key, no token, no header — OpenFEMA is
6
+ * unauthenticated). Nth consumer of the fetch/map/meta shape after treasury /
7
+ * edgar / socrata / ckan.
8
+ * Row query: https://www.fema.gov/api/open/{version}/{EntityName}?{OData params}
9
+ *
10
+ * REUSE (writes ZERO fetch/coercion/error code): `getJson`+`redirect:"error"`
11
+ * (datasource.ts), `num`/`str` (coerce.ts), `driftError` (datasource.ts),
12
+ * `withMeta`/`buildMeta` (meta.ts), the errors.ts taxonomy. COPIES (does NOT
13
+ * import) the socrata/ckan fixed-host SSRF + honesty PATTERN. NEW (small): the
14
+ * OData `$filter` builder (structured args → escaped filter string) + the
15
+ * `FEMA_DATASETS` registry (entityName+version+per-tool field-whitelist pins).
16
+ *
17
+ * ★ THE TOTAL-HONESTY CRUX — `metadata.count` (`$inlinecount=allpages`).
18
+ * OpenFEMA reports the exact FILTERED total in `metadata.count` — a real JSON
19
+ * NUMBER — but ONLY when the query carries `$inlinecount=allpages`. WITHOUT it,
20
+ * `metadata.count` is `0` — a SENTINEL, not the real total (live-verified
21
+ * 2026-07-12: `?$top=1` → count:0; `?$top=1&$inlinecount=allpages` → count:70049).
22
+ * So `getOpenFema` ALWAYS sets `$inlinecount=allpages` (inside the fetch fn, not
23
+ * the caller — no code path can omit it) and reads `metadata.count` as
24
+ * `totalAvailable`. Reading count off a query that omitted inlinecount would
25
+ * report `totalAvailable:0` on a full page — a data-absence-as-zero lie. The count
26
+ * is EXACT (respects the filter), so `hasMore`/`complete` are exact — NO
27
+ * totalIsLowerBound / totalIsEstimated hedge.
28
+ *
29
+ * ★ Entity-keyed envelope (the shape quirk). Results live under `body[EntityName]`
30
+ * (e.g. `body.DisasterDeclarationsSummaries`), NOT a fixed `results`/`data`. The
31
+ * dataset registry carries the entity name, known ahead of the fetch. `metadata`
32
+ * carries the exact filtered `count`.
33
+ *
34
+ * ★ SSRF guard (policy① — mirror socrata.ts / ckan.ts fixed-host). Fixed host
35
+ * `www.fema.gov` + a CURATED dataset registry pinning `{entityName, version}` (no
36
+ * free host, no free path, no free version — HMA's live v2→v4 drift is exactly why
37
+ * version is pinned, never a caller param):
38
+ * 1. `datasetKey` ∈ the frozen `FEMA_DATASETS` registry (belt-and-suspenders
39
+ * recheck in the fetch fn → invalid_input before any fetch);
40
+ * 2. construct `https://www.fema.gov/api/open/${version}/${entityName}?${params}`
41
+ * (host + path segments come ONLY from the pinned registry entry — nothing
42
+ * caller-supplied interpolates into the path), then ASSERT the built URL's
43
+ * hostname === "www.fema.gov" and protocol === "https:" (else invalid_input);
44
+ * 3. OData params via URLSearchParams — `$inlinecount`/`$top`/`$skip` set by the
45
+ * module; `$filter` is MODULE-BUILT from structured args against a per-tool
46
+ * field whitelist (NO raw caller `$filter` — no tool exposes one). String
47
+ * values escape a single-quote by DOUBLING it (`'`→`''`, e.g. `O'Brien` →
48
+ * `'O''Brien'`) then wrap in `'...'`; numbers are unquoted; booleans render
49
+ * `true`/`false`. An un-whitelisted field is rejected (invalid_input) — zero
50
+ * filter-injection surface AND guaranteed-valid, live-verified field names;
51
+ * 4. `redirect:"error"` on every fetch — a direct-JSON endpoint 3xx is anomalous
52
+ * → fail closed, never follow off-host.
53
+ *
54
+ * ★ Silent-filter trap AVOIDED (unlike ECHO). A bad `$filter`/`$orderby` field is
55
+ * NOT silently ignored — OpenFEMA returns HTTP 400 (live-verified). We never
56
+ * present unfiltered-as-filtered. We still module-build `$filter` from a per-tool
57
+ * whitelist (belt-and-suspenders: no 400 in normal use + no injection surface).
58
+ *
59
+ * ★ 200-HTML maintenance-page guard (ADR-0016 OQ1). FEMA's Drupal can serve an
60
+ * "experiencing technical difficulties" HTML page. Observed failures were HTTP 404
61
+ * (→ not_found, thrown before r.json()); but IF an outage returns that page at
62
+ * HTTP 200, getJson's r.json() throws a SyntaxError. `getOpenFema` catches it →
63
+ * `driftError` (never a fake-empty, and a clean classification). Shared getJson
64
+ * stays byte-identical (the shape-drift check stays at the call site).
65
+ *
66
+ * ★ PER-DATASET FIELD NAMES DIFFER — live-verified (ADR-0016 M1). `state eq 'CA'`
67
+ * works on DisasterDeclarationsSummaries (1689) but `stateAbbreviation` → HTTP 400
68
+ * there; conversely `stateAbbreviation eq 'LA'` works on
69
+ * PublicAssistanceFundedProjectsDetails (39444) but `state` → HTTP 400 there. Each
70
+ * tool maps its user-facing `state` arg to ITS dataset's real OData field. Every
71
+ * shipped filter below NARROWS its dataset (live-verified 2026-07-12); a field
72
+ * that 400s or is silently ignored is NOT shipped.
73
+ *
74
+ * HMA v4 (HazardMitigationAssistanceProjects) is a 3rd-tool backlog item (per
75
+ * ADR-0016 §3) — slice 1 ships the 2 core tools.
76
+ */
77
+ import { ToolErrorCarrier } from "./errors.js";
78
+ import { getJson, driftError } from "./datasource.js";
79
+ import { num } from "./coerce.js";
80
+ import { withMeta } from "./meta.js";
81
+ // Re-export the shared honesty coercion (single audited copy in ./coerce.js —
82
+ // ADR-0005 v2 FIX-C) so the fault suite's num-parity guard resolves the SAME
83
+ // `num` from this module (fema.num === coerce.num === socrata.num === ckan.num).
84
+ export { num };
85
+ // ─── Fixed host + curated dataset registry (SSRF core) ────────────
86
+ export const FEMA_HOST = "www.fema.gov";
87
+ /**
88
+ * The frozen dataset registry (SSRF core — no free host, no free path, no free
89
+ * version). Each entry's count + fields are LIVE-VERIFIED keyless (2026-07-12).
90
+ * Adding a dataset later = a registry edit + a live
91
+ * `$top=1&$inlinecount=allpages` verify (entity key, version, count) + a test
92
+ * note — NEVER a free runtime param. Re-verify periodically against
93
+ * `/api/open/v1/DataSets` (versions drift: HMA already moved v2→v4).
94
+ */
95
+ export const FEMA_DATASETS = {
96
+ // SLED emergency spend — 803,904 rows (2026-07-12). stateAbbreviation is THIS
97
+ // dataset's state field (`state` → HTTP 400 here). NO `applicantName` field
98
+ // exists (only applicantId + applicationTitle).
99
+ public_assistance: {
100
+ entityName: "PublicAssistanceFundedProjectsDetails",
101
+ version: "v2",
102
+ filterFields: new Set([
103
+ "stateAbbreviation", // state → 39444 for 'LA'
104
+ "disasterNumber", // → 15 for 3638
105
+ "applicantId", // → 8 for '015-UF5E0-00'
106
+ "damageCategoryCode", // → 232398 for 'B'
107
+ "incidentType", // → 80407 for 'Flood'
108
+ "projectAmount", // ge 1e6 → 25284; le 10000 → 354953
109
+ "declarationDate", // ge '2024-01-01' → 30082
110
+ ]),
111
+ amountFields: [
112
+ "projectAmount",
113
+ "federalShareObligated",
114
+ "totalObligated",
115
+ "mitigationAmount",
116
+ ],
117
+ },
118
+ // Declared disasters — 70,049 rows (2026-07-12). `state` is THIS dataset's state
119
+ // field (`stateAbbreviation` → HTTP 400 here). Program flags are BOOLEAN
120
+ // (`eq true`; `eq 1` → HTTP 400).
121
+ disaster_declarations: {
122
+ entityName: "DisasterDeclarationsSummaries",
123
+ version: "v2",
124
+ filterFields: new Set([
125
+ "state", // → 1689 for 'CA'
126
+ "incidentType", // → 11346 for 'Flood'
127
+ "declarationType", // DR 46462 / EM 21471 / FM 2116
128
+ "fyDeclared", // → 2147 for 2024
129
+ "disasterNumber", // → 64 for 4611
130
+ "declarationDate", // ge '2024-01-01' → 5067
131
+ "paProgramDeclared", // eq true → 65534
132
+ "iaProgramDeclared", // eq true → 17187
133
+ ]),
134
+ amountFields: [],
135
+ },
136
+ };
137
+ const SOURCE_LABEL = "openfema:" + FEMA_HOST;
138
+ /** OData string-literal escaping: double every single-quote (`'` → `''`). This is
139
+ * the ONLY escaping OData needs for a `'...'` literal — zero injection surface. */
140
+ export function escapeODataString(v) {
141
+ return v.replace(/'/g, "''");
142
+ }
143
+ /** Render ONE clause's value: strings quoted+escaped, numbers bare, booleans
144
+ * `true`/`false`. */
145
+ function renderValue(c) {
146
+ switch (c.type) {
147
+ case "string":
148
+ return `'${escapeODataString(c.value)}'`;
149
+ case "number":
150
+ return String(c.value);
151
+ case "boolean":
152
+ return c.value ? "true" : "false";
153
+ }
154
+ }
155
+ /**
156
+ * Build the `$filter` string from structured clauses against the dataset's
157
+ * whitelist. Each clause's field MUST be in `FEMA_DATASETS[datasetKey].filterFields`
158
+ * (belt-and-suspenders — the tool functions only ever pass whitelisted fields, but
159
+ * an un-whitelisted field is a hard invalid_input, never silently dropped). Returns
160
+ * null when there are no clauses (⇒ no `$filter` param).
161
+ */
162
+ export function buildFilter(datasetKey, clauses) {
163
+ if (clauses.length === 0)
164
+ return null;
165
+ const allowed = FEMA_DATASETS[datasetKey].filterFields;
166
+ return clauses
167
+ .map((c) => {
168
+ if (!allowed.has(c.field)) {
169
+ throw new ToolErrorCarrier({
170
+ kind: "invalid_input",
171
+ message: `OpenFEMA $filter field ${JSON.stringify(c.field)} is not in the ${datasetKey} whitelist — refusing to build an un-whitelisted filter (SSRF/injection safety).`,
172
+ retryable: false,
173
+ });
174
+ }
175
+ return `${c.field} ${c.op} ${renderValue(c)}`;
176
+ })
177
+ .join(" and ");
178
+ }
179
+ /**
180
+ * GET one OpenFEMA dataset page. SSRF guard (belt-and-suspenders behind the pinned
181
+ * registry): datasetKey ∈ registry, and the CONSTRUCTED URL's hostname ===
182
+ * www.fema.gov (https). ALWAYS sets `$inlinecount=allpages` (the total-honesty
183
+ * crux — no code path can omit it) + `redirect:"error"` (B1); NO headers (keyless).
184
+ * Reuses errors.ts retry/timeout/taxonomy (429 → rate_limited; 5xx →
185
+ * upstream_unavailable; 404 → not_found; 400 → invalid_input). A 200-non-JSON
186
+ * (Drupal maintenance page) → SyntaxError → driftError (ADR-0016 OQ1). Returns the
187
+ * parsed body (unknown; the caller validates the entity-keyed shape).
188
+ */
189
+ async function getOpenFema(datasetKey, params) {
190
+ const def = FEMA_DATASETS[datasetKey];
191
+ if (!def) {
192
+ throw new ToolErrorCarrier({
193
+ kind: "invalid_input",
194
+ message: `OpenFEMA dataset ${JSON.stringify(datasetKey)} is not in the curated registry. Allowed: ${Object.keys(FEMA_DATASETS).join(", ")}.`,
195
+ retryable: false,
196
+ });
197
+ }
198
+ // ★ ALWAYS send $inlinecount=allpages — WITHOUT it metadata.count is a 0
199
+ // sentinel, not the real total (a data-absence-as-zero lie). Set HERE (not the
200
+ // caller) so no code path can omit it. Dropping this line turns a fault test RED.
201
+ params.set("$inlinecount", "allpages");
202
+ const url = `https://${FEMA_HOST}/api/open/${def.version}/${def.entityName}?${params.toString()}`;
203
+ // Belt-and-suspenders: host + path come ONLY from the pinned registry entry;
204
+ // assert the built URL cannot have been steered off-host.
205
+ const built = new URL(url);
206
+ if (built.hostname !== FEMA_HOST || built.protocol !== "https:") {
207
+ throw new ToolErrorCarrier({
208
+ kind: "invalid_input",
209
+ message: `Constructed OpenFEMA URL host ${JSON.stringify(built.hostname)} (${built.protocol}) is not ${FEMA_HOST} over https — refusing to fetch (SSRF safety).`,
210
+ retryable: false,
211
+ });
212
+ }
213
+ let body;
214
+ try {
215
+ // Keyless ⇒ NO headers key (byte-clean init); B1 redirect:"error"; host-only
216
+ // label → ToolError.upstreamEndpoint.
217
+ body = await getJson(url, { label: SOURCE_LABEL, redirect: "error" });
218
+ }
219
+ catch (e) {
220
+ // Preserve the structured taxonomy (404/429/5xx/400/timeout) unchanged.
221
+ if (e instanceof ToolErrorCarrier)
222
+ throw e;
223
+ // ★ OQ1 — a 200 non-JSON body (Drupal "technical difficulties" HTML) makes
224
+ // getJson's r.json() throw a SyntaxError. Classify it as schema_drift (an
225
+ // honest THROW + a clean classification), never a fake-empty. Shared getJson
226
+ // stays byte-identical (the check is at THIS call site).
227
+ if (e instanceof SyntaxError) {
228
+ throw driftError(SOURCE_LABEL, "OpenFEMA returned non-JSON at HTTP 200 — possible maintenance page");
229
+ }
230
+ throw e;
231
+ }
232
+ return { body, entityName: def.entityName };
233
+ }
234
+ // ─── map + meta ───────────────────────────────────────────────────
235
+ const SHAPE_NOTE = "OpenFEMA OData envelope: rows live under the entity key (e.g. body.DisasterDeclarationsSummaries) and totalAvailable is the EXACT filtered total from metadata.count (we always send $inlinecount=allpages). Never inferred from the page length.";
236
+ const AMOUNT_NOTE = "Amount fields (projectAmount / federalShareObligated / totalObligated / mitigationAmount) are number|null — a real 0 stays 0; absent/empty is null (never 0).";
237
+ const DATE_NOTE = "Date filters use OData ISO compares on declarationDate; a bare 'YYYY-MM-DD' means midnight-UTC start of that day (pass a full ISO datetime for finer bounds; a 'to' bound is exclusive of intra-day times after midnight).";
238
+ /**
239
+ * Coerce the declared amount fields through `num` (null-never-0): a real 0 stays
240
+ * 0; absent/""/"null" → null. Every declared amount field is always present as
241
+ * number|null in the output (honest "unknown", never a fabricated 0). Datasets
242
+ * with no amount fields pass rows through verbatim.
243
+ */
244
+ function coerceAmounts(rows, amountFields) {
245
+ if (amountFields.length === 0)
246
+ return rows;
247
+ return rows.map((row) => {
248
+ const out = { ...row };
249
+ for (const f of amountFields)
250
+ out[f] = num(out[f]);
251
+ return out;
252
+ });
253
+ }
254
+ /**
255
+ * Validate the entity-keyed envelope + build the honest `_meta`. PRIMARY response:
256
+ * - `body[entityName]` MUST be an array → else driftError (nothing valid to return).
257
+ * - `metadata.count` MUST be a number (with $inlinecount it is the exact filtered
258
+ * total) → else driftError. `totalAvailable = num(metadata.count)` — the EXACT
259
+ * total, NEVER the page length (a wide page byte-truncates below $top).
260
+ * - pagination: offset=$skip, limit=$top, hasMore = offset+returned <
261
+ * totalAvailable (exact — no hedge), nextOffset. genuine-empty (count:0, []) →
262
+ * complete:true/0 (via buildMeta). outage/5xx/timeout/400/404 already THREW in
263
+ * getOpenFema (never a fake empty).
264
+ */
265
+ function shapeResponse(args) {
266
+ const def = FEMA_DATASETS[args.datasetKey];
267
+ const b = (args.body ?? {});
268
+ const rawRows = b[def.entityName];
269
+ if (!Array.isArray(rawRows)) {
270
+ throw driftError(SOURCE_LABEL, `OpenFEMA ${def.entityName} returned an unexpected shape (body[${JSON.stringify(def.entityName)}] must be an array of rows).`);
271
+ }
272
+ // ★ The single most important honesty line: totalAvailable is the EXACT filtered
273
+ // metadata.count, NEVER rows.length. typeof-check BEFORE num() (num cannot tell a
274
+ // non-number from an absent one). Mutating this to rows.length must turn a test RED.
275
+ if (typeof b.metadata?.count !== "number") {
276
+ throw driftError(SOURCE_LABEL, `OpenFEMA ${def.entityName} returned a non-number metadata.count — with $inlinecount=allpages it must be the exact filtered total; treating as schema drift.`);
277
+ }
278
+ const totalAvailable = num(b.metadata.count);
279
+ const rows = coerceAmounts(rawRows, def.amountFields);
280
+ const returned = rows.length;
281
+ const hasMore = totalAvailable !== null && args.offset + returned < totalAvailable;
282
+ const nextOffset = hasMore ? args.offset + returned : null;
283
+ const notes = [SHAPE_NOTE];
284
+ if (def.amountFields.length > 0)
285
+ notes.push(AMOUNT_NOTE);
286
+ if (args.filtersApplied.some((f) => /date/i.test(f)))
287
+ notes.push(DATE_NOTE);
288
+ // Byte-cap disclosure: a wide page can byte-truncate below $top while more rows
289
+ // remain — metadata.count is authoritative, so page via $skip.
290
+ if (returned < args.limit && hasMore) {
291
+ notes.push("This page returned fewer rows than the requested limit while more remain (OpenFEMA byte-truncates a wide page below $top); metadata.count is authoritative — page with a larger offset ($skip).");
292
+ }
293
+ // Deep-offset caveat on the ~800k PA set (ADR-0016 OQ2).
294
+ if (args.offset > 100000) {
295
+ notes.push("Deep offset (>100000): very deep $skip into a large dataset may degrade upstream; prefer narrowing the filter over paging deep.");
296
+ }
297
+ return withMeta({
298
+ dataset: def.entityName,
299
+ rows,
300
+ }, {
301
+ source: `OpenFEMA ${def.entityName} (keyless)`,
302
+ keylessMode: true,
303
+ returned,
304
+ totalAvailable,
305
+ filtersApplied: args.filtersApplied,
306
+ filtersDropped: [],
307
+ fieldsUnavailable: [],
308
+ pagination: { offset: args.offset, limit: args.limit, hasMore, nextOffset },
309
+ notes,
310
+ });
311
+ }
312
+ // ─── Tool 1: fema_search_public_assistance ────────────────────────
313
+ /**
314
+ * Search FEMA Public Assistance funded projects (SLED emergency spend to state /
315
+ * local / tribal applicants). Dataset PublicAssistanceFundedProjectsDetails v2.
316
+ * Structured filters → module-built `$filter` (each field LIVE-VERIFIED to narrow):
317
+ * state → stateAbbreviation eq · disasterNumber eq · applicantId eq ·
318
+ * damageCategoryCode eq (e.g. "B" = Emergency Protective Measures) ·
319
+ * incidentType eq · minProjectAmount → projectAmount ge · maxProjectAmount →
320
+ * projectAmount le · declaredDateFrom/To → declarationDate ge/le.
321
+ * Rows carry projectAmount / federalShareObligated / totalObligated /
322
+ * mitigationAmount as number|null. Honest `_meta` (totalAvailable = exact filtered
323
+ * metadata.count).
324
+ */
325
+ export async function searchPublicAssistance(args) {
326
+ const limit = args.limit ?? 100;
327
+ const offset = args.offset ?? 0;
328
+ const clauses = [];
329
+ const filtersApplied = [];
330
+ if (args.state !== undefined) {
331
+ clauses.push({ field: "stateAbbreviation", op: "eq", type: "string", value: args.state });
332
+ filtersApplied.push("state");
333
+ }
334
+ if (args.disasterNumber !== undefined) {
335
+ clauses.push({ field: "disasterNumber", op: "eq", type: "number", value: args.disasterNumber });
336
+ filtersApplied.push("disasterNumber");
337
+ }
338
+ if (args.applicantId !== undefined) {
339
+ clauses.push({ field: "applicantId", op: "eq", type: "string", value: args.applicantId });
340
+ filtersApplied.push("applicantId");
341
+ }
342
+ if (args.damageCategoryCode !== undefined) {
343
+ clauses.push({ field: "damageCategoryCode", op: "eq", type: "string", value: args.damageCategoryCode });
344
+ filtersApplied.push("damageCategoryCode");
345
+ }
346
+ if (args.incidentType !== undefined) {
347
+ clauses.push({ field: "incidentType", op: "eq", type: "string", value: args.incidentType });
348
+ filtersApplied.push("incidentType");
349
+ }
350
+ if (args.minProjectAmount !== undefined) {
351
+ clauses.push({ field: "projectAmount", op: "ge", type: "number", value: args.minProjectAmount });
352
+ filtersApplied.push("minProjectAmount");
353
+ }
354
+ if (args.maxProjectAmount !== undefined) {
355
+ clauses.push({ field: "projectAmount", op: "le", type: "number", value: args.maxProjectAmount });
356
+ filtersApplied.push("maxProjectAmount");
357
+ }
358
+ if (args.declaredDateFrom !== undefined) {
359
+ clauses.push({ field: "declarationDate", op: "ge", type: "string", value: args.declaredDateFrom });
360
+ filtersApplied.push("declaredDateFrom");
361
+ }
362
+ if (args.declaredDateTo !== undefined) {
363
+ clauses.push({ field: "declarationDate", op: "le", type: "string", value: args.declaredDateTo });
364
+ filtersApplied.push("declaredDateTo");
365
+ }
366
+ const params = new URLSearchParams();
367
+ params.set("$top", String(limit));
368
+ params.set("$skip", String(offset));
369
+ const filter = buildFilter("public_assistance", clauses);
370
+ if (filter)
371
+ params.set("$filter", filter);
372
+ const { body } = await getOpenFema("public_assistance", params);
373
+ return shapeResponse({ body, datasetKey: "public_assistance", offset, limit, filtersApplied });
374
+ }
375
+ // ─── Tool 2: fema_disaster_declarations ───────────────────────────
376
+ /**
377
+ * Look up FEMA disaster / emergency declarations. Dataset
378
+ * DisasterDeclarationsSummaries v2. Structured filters → module-built `$filter`
379
+ * (each field LIVE-VERIFIED to narrow):
380
+ * state → state eq · incidentType eq (e.g. "Flood") · declarationType eq
381
+ * (DR/EM/FM) · fyDeclared eq · disasterNumber eq · declaredDateFrom/To →
382
+ * declarationDate ge/le · paProgramDeclared / iaProgramDeclared → eq true/false
383
+ * (BOOLEAN — `eq 1` 400s upstream).
384
+ * Honest `_meta` (totalAvailable = exact filtered metadata.count).
385
+ */
386
+ export async function disasterDeclarations(args) {
387
+ const limit = args.limit ?? 100;
388
+ const offset = args.offset ?? 0;
389
+ const clauses = [];
390
+ const filtersApplied = [];
391
+ if (args.state !== undefined) {
392
+ clauses.push({ field: "state", op: "eq", type: "string", value: args.state });
393
+ filtersApplied.push("state");
394
+ }
395
+ if (args.incidentType !== undefined) {
396
+ clauses.push({ field: "incidentType", op: "eq", type: "string", value: args.incidentType });
397
+ filtersApplied.push("incidentType");
398
+ }
399
+ if (args.declarationType !== undefined) {
400
+ clauses.push({ field: "declarationType", op: "eq", type: "string", value: args.declarationType });
401
+ filtersApplied.push("declarationType");
402
+ }
403
+ if (args.fyDeclared !== undefined) {
404
+ clauses.push({ field: "fyDeclared", op: "eq", type: "number", value: args.fyDeclared });
405
+ filtersApplied.push("fyDeclared");
406
+ }
407
+ if (args.disasterNumber !== undefined) {
408
+ clauses.push({ field: "disasterNumber", op: "eq", type: "number", value: args.disasterNumber });
409
+ filtersApplied.push("disasterNumber");
410
+ }
411
+ if (args.declaredDateFrom !== undefined) {
412
+ clauses.push({ field: "declarationDate", op: "ge", type: "string", value: args.declaredDateFrom });
413
+ filtersApplied.push("declaredDateFrom");
414
+ }
415
+ if (args.declaredDateTo !== undefined) {
416
+ clauses.push({ field: "declarationDate", op: "le", type: "string", value: args.declaredDateTo });
417
+ filtersApplied.push("declaredDateTo");
418
+ }
419
+ if (args.paProgramDeclared !== undefined) {
420
+ clauses.push({ field: "paProgramDeclared", op: "eq", type: "boolean", value: args.paProgramDeclared });
421
+ filtersApplied.push("paProgramDeclared");
422
+ }
423
+ if (args.iaProgramDeclared !== undefined) {
424
+ clauses.push({ field: "iaProgramDeclared", op: "eq", type: "boolean", value: args.iaProgramDeclared });
425
+ filtersApplied.push("iaProgramDeclared");
426
+ }
427
+ const params = new URLSearchParams();
428
+ params.set("$top", String(limit));
429
+ params.set("$skip", String(offset));
430
+ const filter = buildFilter("disaster_declarations", clauses);
431
+ if (filter)
432
+ params.set("$filter", filter);
433
+ const { body } = await getOpenFema("disaster_declarations", params);
434
+ return shapeResponse({ body, datasetKey: "disaster_declarations", offset, limit, filtersApplied });
435
+ }
436
+ //# sourceMappingURL=fema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fema.js","sourceRoot":"","sources":["../src/fema.ts"],"names":[],"mappings":"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"}