@cliwant/mcp-sam-gov 1.2.0 → 1.3.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/README.ja.md +12 -7
- package/README.ko.md +12 -7
- package/README.md +28 -10
- package/dist/bea.d.ts +105 -0
- package/dist/bea.d.ts.map +1 -0
- package/dist/bea.js +303 -0
- package/dist/bea.js.map +1 -0
- package/dist/dol.d.ts +118 -0
- package/dist/dol.d.ts.map +1 -0
- package/dist/dol.js +421 -0
- package/dist/dol.js.map +1 -0
- package/dist/keys.d.ts +10 -8
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +36 -8
- package/dist/keys.js.map +1 -1
- package/dist/lda.d.ts +105 -0
- package/dist/lda.d.ts.map +1 -0
- package/dist/lda.js +317 -0
- package/dist/lda.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +210 -3
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/bea.ts +372 -0
- package/src/dol.ts +515 -0
- package/src/keys.ts +39 -8
- package/src/lda.ts +385 -0
- package/src/server.ts +234 -3
package/dist/bea.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bea.ts — BEA (Bureau of Economic Analysis) Regional Economic Accounts — the
|
|
3
|
+
* REGIONAL / SUB-NATIONAL economic lane (ADR-0051). County / state / MSA GDP by
|
|
4
|
+
* industry (CAGDP2 / SAGDP2N) and personal income (CAINC1 / SAINC1) — the
|
|
5
|
+
* place-of-performance market context that neither the national FRED macro series
|
|
6
|
+
* nor the Census establishment counts carry.
|
|
7
|
+
*
|
|
8
|
+
* ★ THIS IS THE SERVER'S THIRD KEY-REQUIRED SOURCE (Census CBP #1, FRED #2). The
|
|
9
|
+
* BEA Data API has NO keyless tier: every request needs `UserID=`. So, honestly:
|
|
10
|
+
* with NO `BEA_API_KEY` this tool THROWS an `invalid_input` config error BEFORE
|
|
11
|
+
* any fetch (never a fake-empty, never a keyless-pretend). The other 116 tools
|
|
12
|
+
* stay keyless — this key is scoped to this one source. (Contrast the OPTIONAL
|
|
13
|
+
* keys of datagov/bls/nvd, which lift a tier but are not required.)
|
|
14
|
+
*
|
|
15
|
+
* This module MIRRORS the census-economic.ts / fred.ts key-required precedent: a
|
|
16
|
+
* `beaApiKey()` env seam, a pre-fetch `invalid_input` THROW when unset, the
|
|
17
|
+
* fixed-host SSRF assert + `redirect:"error"`, and a data-absence-sentinel→null
|
|
18
|
+
* idiom (Census's negative floor / FRED's `"."` — here BEA's string suppression
|
|
19
|
+
* codes `(NA) (D) (NM) (L) *`). It REUSES `getJson` (the shared fetch envelope) /
|
|
20
|
+
* `driftError` / `num`·`str` (coerce.ts, null-never-0/empty) / `withMeta`·`buildMeta`.
|
|
21
|
+
* The key rides ONLY in the `UserID=` query param, NOWHERE else (never the label,
|
|
22
|
+
* `_meta.source`, notes, or a log — the K-test).
|
|
23
|
+
*
|
|
24
|
+
* GET https://apps.bea.gov/api/data
|
|
25
|
+
* ?UserID=<BEA_API_KEY> (REQUIRED)
|
|
26
|
+
* &method=GetData&datasetname=Regional&ResultFormat=json (fixed)
|
|
27
|
+
* &TableName=<tableName> (e.g. CAGDP2, SAGDP2N, CAINC1)
|
|
28
|
+
* &GeoFips=<geoFips> (STATE | county FIPS | MSA)
|
|
29
|
+
* &LineCode=<lineCode> (industry line, or ALL)
|
|
30
|
+
* &Year=<year> (YYYY | LAST5 | ALL)
|
|
31
|
+
* &Frequency=<frequency> (A | Q)
|
|
32
|
+
* → { BEAAPI:{ Results:{ Statistic, UnitOfMeasure, Dimensions:[…],
|
|
33
|
+
* Data:[{ Code, GeoFips, GeoName, TimePeriod, CL_UNIT, UNIT_MULT,
|
|
34
|
+
* DataValue, NoteRef }], Notes:[{ NoteRef, NoteText }] } } }
|
|
35
|
+
*
|
|
36
|
+
* ★ HONESTY (ADR-0051 P1–P5):
|
|
37
|
+
* [KEY] no key ⇒ invalid_input THROW pre-fetch (0 fetch); the message names
|
|
38
|
+
* BEA_API_KEY + the free-signup URL.
|
|
39
|
+
* [P1] BEA GetData returns the COMPLETE set for the filter (no server
|
|
40
|
+
* pagination) ⇒ totalAvailable = the row count, complete:true. NEVER
|
|
41
|
+
* fabricated.
|
|
42
|
+
* [★P2] ★the crux: a missing/invalid key (and any bad-parameter request) returns
|
|
43
|
+
* HTTP **200** carrying `BEAAPI.Results.Error` — NOT an HTTP error status.
|
|
44
|
+
* The catch-ladder checks `Results.Error` FIRST (BEFORE the Data-array
|
|
45
|
+
* drift check) and throws invalid_input SURFACING `APIErrorDescription`
|
|
46
|
+
* (+ code) — NEVER read as an empty result. `Data:[]` (a genuine empty
|
|
47
|
+
* array) ⇒ honest empty (returned:0, complete:true). A 5xx/timeout ⇒
|
|
48
|
+
* upstream_unavailable THROW. A 200 non-JSON ⇒ schema_drift.
|
|
49
|
+
* [★P3] `DataValue` is a STRING WITH COMMAS ("1,234,567") — strip commas then
|
|
50
|
+
* `num()`. The suppression/not-available sentinels `(NA) (D) (NM) (L) *`
|
|
51
|
+
* (and any non-numeric after the comma-strip) map to **null** (withheld),
|
|
52
|
+
* NEVER 0 — a real "0" stays 0. `UNIT_MULT` (power-of-10 multiplier) is
|
|
53
|
+
* reported as `unitMult` and `CL_UNIT` as `unitOfMeasure`; the raw value is
|
|
54
|
+
* surfaced WITH the multiplier — it is NEVER multiplied in (that would lose
|
|
55
|
+
* precision and double-count against the disclosed multiplier).
|
|
56
|
+
* [P4] `BEAAPI` / `Results` / `Data` absent or non-array ⇒ driftError — BUT
|
|
57
|
+
* ONLY after the Results.Error check (an Error response is P2, not drift).
|
|
58
|
+
* [SSRF] fixed host `apps.bea.gov`; `tableName` ^[A-Za-z0-9]{2,20}$; `geoFips`
|
|
59
|
+
* ^[A-Za-z0-9]{2,10}$; `lineCode` ^([0-9]{1,4}|ALL)$; `year`
|
|
60
|
+
* ^\d{4}$|LAST5|ALL; `frequency` {A,Q}. All VALUES ride URLSearchParams;
|
|
61
|
+
* the key rides `UserID=` ONLY.
|
|
62
|
+
*/
|
|
63
|
+
import { ToolErrorCarrier } from "./errors.js";
|
|
64
|
+
import { getJson, driftError } from "./datasource.js";
|
|
65
|
+
import { num, str } from "./coerce.js";
|
|
66
|
+
import { withMeta } from "./meta.js";
|
|
67
|
+
// Re-export the shared honesty coercion (single audited copy in ./coerce.js —
|
|
68
|
+
// ADR-0005 v2 FIX-C) so a `num` regression fails together across sources. NO local
|
|
69
|
+
// num/str; the sentinel/comma-strip map is a WRAPPER around num, not a fork.
|
|
70
|
+
export { num };
|
|
71
|
+
// ─── SSRF core: the single fixed host + base path ─────────────────
|
|
72
|
+
export const BEA_HOST = "apps.bea.gov";
|
|
73
|
+
const BEA_PATH = "/api/data";
|
|
74
|
+
// HOST+path label — surfaces in ToolError.upstreamEndpoint; the key rides ONLY in
|
|
75
|
+
// the UserID= query param, so no token can ever appear here.
|
|
76
|
+
const BEA_LABEL = "bea:/api/data";
|
|
77
|
+
// ─── Validation charclasses (SSRF + "verify the input" honesty) ───
|
|
78
|
+
const TABLE_RE = /^[A-Za-z0-9]{2,20}$/; // e.g. CAGDP2, SAGDP2N, CAINC1
|
|
79
|
+
const GEOFIPS_RE = /^[A-Za-z0-9]{2,10}$/; // STATE | county FIPS | MSA code
|
|
80
|
+
const LINECODE_RE = /^([0-9]{1,4}|ALL)$/; // industry line, or ALL
|
|
81
|
+
const YEAR_RE = /^\d{4}$/; // a single 4-digit year
|
|
82
|
+
const YEAR_KEYWORDS = new Set(["LAST5", "ALL"]);
|
|
83
|
+
const FREQUENCIES = new Set(["A", "Q"]);
|
|
84
|
+
const DEFAULT_YEAR = "LAST5";
|
|
85
|
+
const DEFAULT_FREQUENCY = "A";
|
|
86
|
+
// BEA encodes a suppressed / not-available cell as one of these string codes in
|
|
87
|
+
// DataValue: (NA)=not available, (D)=disclosure-suppressed, (NM)=not meaningful,
|
|
88
|
+
// (L)=less than half the unit, *=statistically insignificant. Any of these — and
|
|
89
|
+
// any non-numeric value after the comma-strip — is a data-absence marker, NEVER a
|
|
90
|
+
// number and NEVER 0.
|
|
91
|
+
const BEA_SUPPRESSION = new Set(["(NA)", "(D)", "(NM)", "(L)", "*"]);
|
|
92
|
+
// ─── Honesty notes (ADR-0051 required set) ────────────────────────
|
|
93
|
+
const KEY_REQUIRED_NOTE = "This source REQUIRES a free BEA_API_KEY (the BEA Data API has no keyless tier). The key is sent ONLY as the UserID= query parameter to apps.bea.gov and is NEVER logged, echoed, or placed in this response.";
|
|
94
|
+
const DATAVALUE_NOTE = "dataValue is parsed from BEA's comma-formatted DataValue string ('1,234,567' → 1234567). BEA suppression/not-available codes ((NA)/(D)/(NM)/(L)/*) map to null (withheld) — NEVER 0 (a genuine 0 is preserved as 0).";
|
|
95
|
+
const UNIT_MULT_NOTE = "unitMult is BEA's UNIT_MULT (a power-of-10 multiplier) and unitOfMeasure is CL_UNIT (the unit label). The raw dataValue is surfaced ALONGSIDE unitMult and is NOT multiplied by it — apply unitMult yourself if a scaled figure is needed (multiplying here would lose precision and double-count).";
|
|
96
|
+
const NO_PAGINATION_NOTE = "BEA GetData returns the COMPLETE set of rows matching the filter (no server-side pagination); totalAvailable equals the number of rows returned. Narrow with geoFips / lineCode / year to reduce the row count.";
|
|
97
|
+
// ─── The key seam (REQUIRED; value NEVER leaked past the UserID= param) ──
|
|
98
|
+
/** Read BEA_API_KEY from env; trim; return the value or undefined (unset/blank). */
|
|
99
|
+
export function beaApiKey() {
|
|
100
|
+
const raw = process.env.BEA_API_KEY;
|
|
101
|
+
const trimmed = typeof raw === "string" ? raw.trim() : "";
|
|
102
|
+
return trimmed ? trimmed : undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* num(), but for BEA's comma-formatted DataValue string. Strips a suppression code
|
|
106
|
+
* ((NA)/(D)/(NM)/(L)/*) → null, otherwise removes thousands commas and defers to
|
|
107
|
+
* num (so "1,234,567" ⇒ 1234567, a genuine "0" ⇒ 0, and any residual non-numeric
|
|
108
|
+
* ⇒ null — NEVER a fabricated 0).
|
|
109
|
+
*/
|
|
110
|
+
export function beaDataValue(v) {
|
|
111
|
+
if (typeof v === "string") {
|
|
112
|
+
const s = v.trim();
|
|
113
|
+
if (s === "" || BEA_SUPPRESSION.has(s))
|
|
114
|
+
return null;
|
|
115
|
+
return num(s.replace(/,/g, ""));
|
|
116
|
+
}
|
|
117
|
+
return num(v);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Fetch BEA Regional Economic Accounts rows for a table × geography × line filter
|
|
121
|
+
* → normalized rows + summarized BEA Notes + honest `_meta`. REQUIRES BEA_API_KEY
|
|
122
|
+
* (throws invalid_input pre-fetch when unset). ★A missing/invalid key (and any bad
|
|
123
|
+
* parameter) surfaces as an HTTP-200 `BEAAPI.Results.Error` carrier which is
|
|
124
|
+
* detected and thrown as invalid_input BEFORE the Data-array shape check.
|
|
125
|
+
*/
|
|
126
|
+
export async function regionalData(args) {
|
|
127
|
+
// ── [KEY] REQUIRED key — throw an honest config error BEFORE any fetch. ──
|
|
128
|
+
const key = beaApiKey();
|
|
129
|
+
if (key === undefined) {
|
|
130
|
+
throw new ToolErrorCarrier({
|
|
131
|
+
kind: "invalid_input",
|
|
132
|
+
retryable: false,
|
|
133
|
+
message: "BEA Regional Economic Accounts requires a free API key. Get one at https://apps.bea.gov/API/signup/ and set BEA_API_KEY.",
|
|
134
|
+
upstreamEndpoint: BEA_LABEL,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// ── Validate + default the inputs (belt-and-suspenders behind the server Zod;
|
|
138
|
+
// a DIRECT handler call bypasses Zod). All ride in the query string. ──
|
|
139
|
+
const tableName = args.tableName ?? "";
|
|
140
|
+
if (!TABLE_RE.test(tableName)) {
|
|
141
|
+
throw new ToolErrorCarrier({
|
|
142
|
+
kind: "invalid_input",
|
|
143
|
+
retryable: false,
|
|
144
|
+
message: `Invalid tableName ${JSON.stringify(tableName)} — expected a BEA Regional table code (^[A-Za-z0-9]{2,20}$), e.g. "CAGDP2" (county GDP by industry), "SAGDP2N" (state GDP), "CAINC1" (personal income).`,
|
|
145
|
+
upstreamEndpoint: BEA_LABEL,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const geoFips = args.geoFips ?? "";
|
|
149
|
+
if (!GEOFIPS_RE.test(geoFips)) {
|
|
150
|
+
throw new ToolErrorCarrier({
|
|
151
|
+
kind: "invalid_input",
|
|
152
|
+
retryable: false,
|
|
153
|
+
message: `Invalid geoFips ${JSON.stringify(geoFips)} — expected a BEA GeoFips selector (^[A-Za-z0-9]{2,10}$), e.g. "STATE" (all states), a county FIPS like "06075", or an MSA code.`,
|
|
154
|
+
upstreamEndpoint: BEA_LABEL,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const lineCode = args.lineCode ?? "";
|
|
158
|
+
if (!LINECODE_RE.test(lineCode)) {
|
|
159
|
+
throw new ToolErrorCarrier({
|
|
160
|
+
kind: "invalid_input",
|
|
161
|
+
retryable: false,
|
|
162
|
+
message: `Invalid lineCode ${JSON.stringify(lineCode)} — expected an integer industry line (^[0-9]{1,4}$), e.g. "1", or "ALL" for every line.`,
|
|
163
|
+
upstreamEndpoint: BEA_LABEL,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const year = args.year ?? DEFAULT_YEAR;
|
|
167
|
+
if (!YEAR_KEYWORDS.has(year) && !YEAR_RE.test(year)) {
|
|
168
|
+
throw new ToolErrorCarrier({
|
|
169
|
+
kind: "invalid_input",
|
|
170
|
+
retryable: false,
|
|
171
|
+
message: `Invalid year ${JSON.stringify(year)} — expected a 4-digit year (^\\d{4}$), "LAST5", or "ALL".`,
|
|
172
|
+
upstreamEndpoint: BEA_LABEL,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
const frequency = args.frequency ?? DEFAULT_FREQUENCY;
|
|
176
|
+
if (!FREQUENCIES.has(frequency)) {
|
|
177
|
+
throw new ToolErrorCarrier({
|
|
178
|
+
kind: "invalid_input",
|
|
179
|
+
retryable: false,
|
|
180
|
+
message: `Invalid frequency ${JSON.stringify(frequency)} — expected one of A (annual), Q (quarterly).`,
|
|
181
|
+
upstreamEndpoint: BEA_LABEL,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// ── Build the query (all VALUES via URLSearchParams — no host/path steer; the
|
|
185
|
+
// REQUIRED key rides ONLY here in UserID=). ──
|
|
186
|
+
const params = new URLSearchParams();
|
|
187
|
+
params.set("UserID", key);
|
|
188
|
+
params.set("method", "GetData");
|
|
189
|
+
params.set("datasetname", "Regional");
|
|
190
|
+
params.set("ResultFormat", "json");
|
|
191
|
+
params.set("TableName", tableName);
|
|
192
|
+
params.set("GeoFips", geoFips);
|
|
193
|
+
params.set("LineCode", lineCode);
|
|
194
|
+
params.set("Year", year);
|
|
195
|
+
params.set("Frequency", frequency);
|
|
196
|
+
const url = `https://${BEA_HOST}${BEA_PATH}?${params.toString()}`;
|
|
197
|
+
// Belt-and-suspenders: the fixed host + strictly-validated query leave nothing to
|
|
198
|
+
// steer the authority; assert the built URL cannot have been moved off-host.
|
|
199
|
+
const built = new URL(url);
|
|
200
|
+
if (built.hostname !== BEA_HOST || built.protocol !== "https:") {
|
|
201
|
+
throw new ToolErrorCarrier({
|
|
202
|
+
kind: "invalid_input",
|
|
203
|
+
retryable: false,
|
|
204
|
+
message: `Constructed BEA URL host ${JSON.stringify(built.hostname)} (${built.protocol}) is not ${BEA_HOST} over https — refusing to fetch (SSRF safety).`,
|
|
205
|
+
upstreamEndpoint: BEA_LABEL,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
// ── Fetch through the shared envelope. The key rides UserID= ONLY (never the
|
|
209
|
+
// label/_meta); redirect:"error" fails closed on any off-host 3xx (it could
|
|
210
|
+
// carry the key away). A 5xx/timeout ⇒ upstream_unavailable THROW; a 200
|
|
211
|
+
// non-JSON body ⇒ getJson's r.json() throws a SyntaxError ⇒ we reclassify to
|
|
212
|
+
// schema_drift. ★A missing/invalid key returns HTTP 200 with an Error carrier,
|
|
213
|
+
// so it does NOT surface here — it is detected in the parse ladder below. ──
|
|
214
|
+
let body;
|
|
215
|
+
try {
|
|
216
|
+
body = await getJson(url, { label: BEA_LABEL, redirect: "error" });
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
if (e instanceof SyntaxError) {
|
|
220
|
+
throw driftError(BEA_LABEL, "BEA /api/data returned a non-JSON body at HTTP 200 — treating as schema drift (never read as an empty result).");
|
|
221
|
+
}
|
|
222
|
+
throw e; // 5xx → upstream_unavailable, 404 → not_found, 429 → rate_limited …
|
|
223
|
+
}
|
|
224
|
+
// ── [P4] Navigate BEAAPI.Results. An absent BEAAPI/Results is drift — BUT the
|
|
225
|
+
// Results.Error check (P2) comes FIRST below, since an error RESPONSE also
|
|
226
|
+
// carries BEAAPI.Results (with an Error member, not a Data array). ──
|
|
227
|
+
const beaapi = body?.BEAAPI;
|
|
228
|
+
if (beaapi === null || typeof beaapi !== "object") {
|
|
229
|
+
throw driftError(BEA_LABEL, "BEA response is missing the `BEAAPI` envelope — treating as schema drift (never a fabricated empty).");
|
|
230
|
+
}
|
|
231
|
+
const results = beaapi.Results;
|
|
232
|
+
if (results === null || typeof results !== "object") {
|
|
233
|
+
throw driftError(BEA_LABEL, "BEA response is missing `BEAAPI.Results` — treating as schema drift (never a fabricated empty).");
|
|
234
|
+
}
|
|
235
|
+
// ── [★P2] The CRUX: a missing/invalid key (or any bad parameter) returns HTTP
|
|
236
|
+
// 200 carrying `BEAAPI.Results.Error` — checked HERE, BEFORE the Data-array
|
|
237
|
+
// drift check, so an error response is surfaced as invalid_input CARRYING the
|
|
238
|
+
// APIErrorDescription (+ code), NEVER read as an empty result. ──
|
|
239
|
+
const errNode = results.Error;
|
|
240
|
+
if (errNode !== undefined && errNode !== null) {
|
|
241
|
+
const errObj = (Array.isArray(errNode) ? errNode[0] : errNode);
|
|
242
|
+
const code = str(errObj?.APIErrorCode);
|
|
243
|
+
const desc = str(errObj?.APIErrorDescription);
|
|
244
|
+
throw new ToolErrorCarrier({
|
|
245
|
+
kind: "invalid_input",
|
|
246
|
+
retryable: false,
|
|
247
|
+
message: desc
|
|
248
|
+
? `BEA rejected the request${code ? ` (APIErrorCode ${code})` : ""}: ${desc}. Check BEA_API_KEY and the tableName / geoFips / lineCode / year parameters.`
|
|
249
|
+
: `BEA rejected the request${code ? ` (APIErrorCode ${code})` : ""} — check BEA_API_KEY and the tableName / geoFips / lineCode / year parameters.`,
|
|
250
|
+
upstreamEndpoint: BEA_LABEL,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// ── [P4] `Data` MUST be an array (a missing/non-array is drift, never a
|
|
254
|
+
// fabricated empty). An EMPTY array is a genuine honest-empty (below). ──
|
|
255
|
+
const data = results.Data;
|
|
256
|
+
if (!Array.isArray(data)) {
|
|
257
|
+
throw driftError(BEA_LABEL, "BEA `BEAAPI.Results.Data` is missing or not an array — treating as schema drift (never a fabricated empty).");
|
|
258
|
+
}
|
|
259
|
+
// ── [P3] Map each Data row (comma-stripped/sentinel→null DataValue; UNIT_MULT /
|
|
260
|
+
// CL_UNIT reported, NOT applied). ──
|
|
261
|
+
const rows = data.map((raw) => {
|
|
262
|
+
const row = (raw ?? {});
|
|
263
|
+
return {
|
|
264
|
+
geoFips: str(row.GeoFips),
|
|
265
|
+
geoName: str(row.GeoName),
|
|
266
|
+
timePeriod: str(row.TimePeriod),
|
|
267
|
+
lineCode: str(row.Code),
|
|
268
|
+
dataValue: beaDataValue(row.DataValue),
|
|
269
|
+
unitOfMeasure: str(row.CL_UNIT),
|
|
270
|
+
unitMult: num(row.UNIT_MULT),
|
|
271
|
+
noteRef: str(row.NoteRef),
|
|
272
|
+
};
|
|
273
|
+
});
|
|
274
|
+
// ── Summarize the BEA Notes (footnotes). A missing/non-array Notes ⇒ []. ──
|
|
275
|
+
const notesNode = results.Notes;
|
|
276
|
+
const notes = Array.isArray(notesNode)
|
|
277
|
+
? notesNode.map((raw) => {
|
|
278
|
+
const n = (raw ?? {});
|
|
279
|
+
return { noteRef: str(n.NoteRef), noteText: str(n.NoteText) };
|
|
280
|
+
})
|
|
281
|
+
: [];
|
|
282
|
+
// ── [P1] The COMPLETE set for the filter (no server pagination). ──
|
|
283
|
+
const totalAvailable = rows.length;
|
|
284
|
+
const meta = {
|
|
285
|
+
// MODE only — never the key value (K-test).
|
|
286
|
+
source: "apps.bea.gov /api/data (BEA Regional Economic Accounts; BEA_API_KEY)",
|
|
287
|
+
keylessMode: false, // ★KEYED — the third key-required source
|
|
288
|
+
returned: rows.length,
|
|
289
|
+
totalAvailable,
|
|
290
|
+
filtersApplied: [
|
|
291
|
+
`tableName:${tableName}`,
|
|
292
|
+
`geoFips:${geoFips}`,
|
|
293
|
+
`lineCode:${lineCode}`,
|
|
294
|
+
`year:${year}`,
|
|
295
|
+
`frequency:${frequency}`,
|
|
296
|
+
],
|
|
297
|
+
filtersDropped: [],
|
|
298
|
+
fieldsUnavailable: [],
|
|
299
|
+
notes: [KEY_REQUIRED_NOTE, DATAVALUE_NOTE, UNIT_MULT_NOTE, NO_PAGINATION_NOTE],
|
|
300
|
+
};
|
|
301
|
+
return withMeta({ rows, notes }, meta);
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=bea.js.map
|
package/dist/bea.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bea.js","sourceRoot":"","sources":["../src/bea.ts"],"names":[],"mappings":"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"}
|
package/dist/dol.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dol.ts — US Department of Labor Data API v4 (apiprod.dol.gov) — the LABOR
|
|
3
|
+
* ENFORCEMENT lane (ADR-0053). WHD wage/hour violations, OSHA inspections, ILAB
|
|
4
|
+
* child/forced-labor reports, MSHA mine safety … — the compliance/enforcement
|
|
5
|
+
* signal no contract/spending/market source carries.
|
|
6
|
+
*
|
|
7
|
+
* TWO tools with a DELIBERATE key split (the honest reflection of the live API):
|
|
8
|
+
* • `dol_list_datasets` (KEYLESS) — GET /v4/datasets. The dataset CATALOG (and
|
|
9
|
+
* /v4/agencies) is keyless; this tool needs NO key. It returns the machine
|
|
10
|
+
* inventory (name / tablename / api_url / agency / …) you page + filter to find
|
|
11
|
+
* an endpoint, then feed its `apiUrl` into dol_get_dataset.
|
|
12
|
+
* • `dol_get_dataset` (KEY-REQUIRED, DOL_API_KEY) — GET
|
|
13
|
+
* /v4/get/{agency}/{endpoint}/json?… . The DATA endpoint has NO keyless tier, so
|
|
14
|
+
* with NO `DOL_API_KEY` this tool THROWS an invalid_input config error BEFORE any
|
|
15
|
+
* fetch (0 network call; the message names DOL_API_KEY + dol.gov/developer).
|
|
16
|
+
* So DOL is the 4th REQUIRED key — but ONLY for the data tool; the catalog tool
|
|
17
|
+
* (and every other tool on the server) stays keyless.
|
|
18
|
+
*
|
|
19
|
+
* ★LIVE-VERIFIED WIRE FACTS (2026-07-15):
|
|
20
|
+
* - Host `apiprod.dol.gov` (AWS API Gateway) + base `/v4`.
|
|
21
|
+
* - /v4/datasets is KEYLESS 200 → `{ datasets:[…], meta:{ current_page, next_page,
|
|
22
|
+
* prev_page, total_pages, total_count } }`. `limit` is the PER-PAGE size (limit=1000
|
|
23
|
+
* returns the whole 42-row catalog in one page, total_pages:1); server-side agency
|
|
24
|
+
* filtering is NOT honored (verified: ?agency=ILAB still returned the full 42) — so
|
|
25
|
+
* agency/query filtering is CLIENT-SIDE over the fetched catalog.
|
|
26
|
+
* - The DATA route is the QUERY-STYLE form `/v4/get/{agency}/{endpoint}/{format}?…`
|
|
27
|
+
* (NOT the path-style `/…/limit/N/offset/O/format/json`): the query-style URL
|
|
28
|
+
* reached the DOL app and returned a proper `401 {"…key…missing…"}` on a bad key,
|
|
29
|
+
* whereas the path-style URL only ever hit the AWS gateway's generic
|
|
30
|
+
* `403 {"message":"Missing Authentication Token"}` (an unmatched route). The DOL
|
|
31
|
+
* API User Guide (dataportal.dol.gov/pdf/dol-api-user-guide.pdf) confirms the
|
|
32
|
+
* `/get/<agency>/<api_url>/<format>?limit=&offset=&filter_object=&…` template and
|
|
33
|
+
* that `<endpoint>` is the dataset's **api_url** (NOT its tablename).
|
|
34
|
+
*
|
|
35
|
+
* ★UNVERIFIED (key-gated — coded DEFENSIVELY, honestly disclosed): the DATA response
|
|
36
|
+
* body shape could not be observed live (every /v4/get call is 401 without a real
|
|
37
|
+
* key, and the User Guide shows no body example). So `dol_get_dataset` accepts EITHER
|
|
38
|
+
* a bare row array `[…]` OR `{ data:[…] }` OR `{ results:[…] }`; a top-level count
|
|
39
|
+
* (`total_count`/`total`/`count`, or `meta.total_count`) is used for totalAvailable
|
|
40
|
+
* ONLY if actually present, else `totalAvailable = null` (an HONEST unknown — never
|
|
41
|
+
* `returned` passed off as the total). Records are surfaced VERBATIM (each dataset
|
|
42
|
+
* has its own enforcement schema; blind coercion would distort compliance data — a
|
|
43
|
+
* JSON `0` stays `0`, a JSON `null` stays `null`, field names are preserved as-is).
|
|
44
|
+
*
|
|
45
|
+
* ★HONESTY (ADR-0053 P1–P4 + KEY + SSRF):
|
|
46
|
+
* [KEY] dol_get_dataset with NO DOL_API_KEY ⇒ invalid_input THROW pre-fetch (0
|
|
47
|
+
* fetch); the message names DOL_API_KEY + dol.gov/developer. The key rides the
|
|
48
|
+
* `X-API-KEY` HEADER ONLY — NEVER the URL / label / _meta / notes / a log (the
|
|
49
|
+
* K-test). dol_list_datasets is keyless (no key read, no header).
|
|
50
|
+
* [P1] catalog: totalAvailable = meta.total_count (the API's real catalog total)
|
|
51
|
+
* for an unfiltered scan; when a CLIENT-SIDE filter is applied it is the
|
|
52
|
+
* filtered-set size (exact — the whole catalog is fetched in one page). data:
|
|
53
|
+
* totalAvailable = a real count field when present, else null; offset/limit
|
|
54
|
+
* pagination in both.
|
|
55
|
+
* [P2] data: 401/403 (missing/invalid key) ⇒ invalid_input reclassified with the
|
|
56
|
+
* DOL_API_KEY guidance (the AWS "Missing Authentication Token" is a key problem
|
|
57
|
+
* here) — never empty. 400 ⇒ invalid_input. empty rows ⇒ honest empty
|
|
58
|
+
* (returned:0). 429 ⇒ rate_limited THROW (Retry-After honored). 5xx/timeout ⇒
|
|
59
|
+
* upstream_unavailable THROW. 200 non-JSON ⇒ schema_drift.
|
|
60
|
+
* [P3] records verbatim (null-never-0 comes for free — JSON preserves 0/null and
|
|
61
|
+
* every original field name; the tool never introduces a fabricated 0).
|
|
62
|
+
* [P4] the expected array (catalog `datasets`; data's row array) absent/non-array
|
|
63
|
+
* ⇒ driftError. A ToolErrorCarrier (401/5xx/…) is a P2 outcome, rethrown
|
|
64
|
+
* BEFORE the drift check.
|
|
65
|
+
* [SSRF] fixed host `apiprod.dol.gov` + a post-construction hostname/https assert +
|
|
66
|
+
* `redirect:"error"`; agency/endpoint charclass `^[A-Za-z0-9_]+$` (they ride
|
|
67
|
+
* in the PATH); limit/offset integers; filterValue/fields ride URLSearchParams;
|
|
68
|
+
* the key rides the X-API-KEY header only.
|
|
69
|
+
*/
|
|
70
|
+
import { num } from "./coerce.js";
|
|
71
|
+
import { type MetaBundle } from "./meta.js";
|
|
72
|
+
export { num };
|
|
73
|
+
export declare const DOL_HOST = "apiprod.dol.gov";
|
|
74
|
+
/** Read DOL_API_KEY from env; trim; return the value or undefined (unset/blank). */
|
|
75
|
+
export declare function dolApiKey(): string | undefined;
|
|
76
|
+
export type DolListDatasetsArgs = {
|
|
77
|
+
agency?: string;
|
|
78
|
+
query?: string;
|
|
79
|
+
limit?: number;
|
|
80
|
+
offset?: number;
|
|
81
|
+
};
|
|
82
|
+
export type DolDataset = {
|
|
83
|
+
name: string | null;
|
|
84
|
+
tablename: string | null;
|
|
85
|
+
apiUrl: string | null;
|
|
86
|
+
agency: string | null;
|
|
87
|
+
agencyAbbr: string | null;
|
|
88
|
+
description: string | null;
|
|
89
|
+
frequency: string | null;
|
|
90
|
+
datasetType: number | null;
|
|
91
|
+
category: string | null;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* List the DOL Data API v4 dataset catalog (KEYLESS). Fetches the WHOLE catalog in
|
|
95
|
+
* one page, applies optional CLIENT-SIDE `agency` (abbr/name) + `query` (substring over
|
|
96
|
+
* name/description/tags) filters, then offset/limit-slices the filtered set. Honest
|
|
97
|
+
* `_meta`: totalAvailable = the catalog's real total (unfiltered) or the filtered-set
|
|
98
|
+
* size (exact — the whole catalog is in hand); offset pagination over the filtered set.
|
|
99
|
+
*/
|
|
100
|
+
export declare function listDatasets(args: DolListDatasetsArgs): Promise<MetaBundle>;
|
|
101
|
+
export type DolGetDatasetArgs = {
|
|
102
|
+
agency?: string;
|
|
103
|
+
table?: string;
|
|
104
|
+
limit?: number;
|
|
105
|
+
offset?: number;
|
|
106
|
+
filterField?: string;
|
|
107
|
+
filterValue?: string;
|
|
108
|
+
fields?: string[];
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Fetch records from one DOL dataset (KEY-REQUIRED). `agency` (abbr) + `table` (the
|
|
112
|
+
* dataset's api_url endpoint) ride the request PATH; limit/offset/filter ride the query;
|
|
113
|
+
* the DOL_API_KEY rides the X-API-KEY header ONLY. Records are surfaced VERBATIM. Honest
|
|
114
|
+
* `_meta`: totalAvailable is a real count field when present, else null (offset
|
|
115
|
+
* pagination). Unset key ⇒ invalid_input THROW pre-fetch (0 fetch).
|
|
116
|
+
*/
|
|
117
|
+
export declare function getDataset(args: DolGetDatasetArgs): Promise<MetaBundle>;
|
|
118
|
+
//# sourceMappingURL=dol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dol.d.ts","sourceRoot":"","sources":["../src/dol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAIH,OAAO,EAAE,GAAG,EAAO,MAAM,aAAa,CAAC;AACvC,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAIzE,OAAO,EAAE,GAAG,EAAE,CAAC;AAGf,eAAO,MAAM,QAAQ,oBAAoB,CAAC;AAkC1C,oFAAoF;AACpF,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAI9C;AAyBD,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAoBF;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiGjF;AAGD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAyK7E"}
|