@usa-open-data-connectors/usa-sources 0.1.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.md +339 -0
- package/dist/blsSeries.d.ts +73 -0
- package/dist/blsSeries.d.ts.map +1 -0
- package/dist/blsSeries.js +173 -0
- package/dist/blsSeries.js.map +1 -0
- package/dist/cdcCountyObesity.d.ts +116 -0
- package/dist/cdcCountyObesity.d.ts.map +1 -0
- package/dist/cdcCountyObesity.js +180 -0
- package/dist/cdcCountyObesity.js.map +1 -0
- package/dist/cfpbConsumerComplaints.d.ts +152 -0
- package/dist/cfpbConsumerComplaints.d.ts.map +1 -0
- package/dist/cfpbConsumerComplaints.js +273 -0
- package/dist/cfpbConsumerComplaints.js.map +1 -0
- package/dist/cpscProductRecalls.d.ts +146 -0
- package/dist/cpscProductRecalls.d.ts.map +1 -0
- package/dist/cpscProductRecalls.js +298 -0
- package/dist/cpscProductRecalls.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -0
- package/dist/femaDisasterDeclarations.d.ts +123 -0
- package/dist/femaDisasterDeclarations.d.ts.map +1 -0
- package/dist/femaDisasterDeclarations.js +214 -0
- package/dist/femaDisasterDeclarations.js.map +1 -0
- package/dist/fixtures.d.ts +5 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +11 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/nceiAnnualTemperature.d.ts +92 -0
- package/dist/nceiAnnualTemperature.d.ts.map +1 -0
- package/dist/nceiAnnualTemperature.js +158 -0
- package/dist/nceiAnnualTemperature.js.map +1 -0
- package/dist/noaaSeaLevel.d.ts +144 -0
- package/dist/noaaSeaLevel.d.ts.map +1 -0
- package/dist/noaaSeaLevel.js +252 -0
- package/dist/noaaSeaLevel.js.map +1 -0
- package/dist/openFdaFoodRecalls.d.ts +154 -0
- package/dist/openFdaFoodRecalls.d.ts.map +1 -0
- package/dist/openFdaFoodRecalls.js +293 -0
- package/dist/openFdaFoodRecalls.js.map +1 -0
- package/dist/registry.d.ts +28 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +79 -0
- package/dist/registry.js.map +1 -0
- package/dist/treasuryAvgInterestRate.d.ts +107 -0
- package/dist/treasuryAvgInterestRate.d.ts.map +1 -0
- package/dist/treasuryAvgInterestRate.js +191 -0
- package/dist/treasuryAvgInterestRate.js.map +1 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/usgsEarthquakes.d.ts +108 -0
- package/dist/usgsEarthquakes.d.ts.map +1 -0
- package/dist/usgsEarthquakes.js +199 -0
- package/dist/usgsEarthquakes.js.map +1 -0
- package/dist/usgsPeakStreamflow.d.ts +116 -0
- package/dist/usgsPeakStreamflow.d.ts.map +1 -0
- package/dist/usgsPeakStreamflow.js +211 -0
- package/dist/usgsPeakStreamflow.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UsSourceApiError, UsSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/** Base URL for the Consumer Complaint Database search API. */
|
|
5
|
+
export const CFPB_COMPLAINT_SEARCH_API_BASE = 'https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/';
|
|
6
|
+
/**
|
|
7
|
+
* First calendar year the adapter reads.
|
|
8
|
+
*
|
|
9
|
+
* The bureau published its first complaints on 1 December 2011, so that year
|
|
10
|
+
* holds one month. The window keeps it rather than dropping it, and the data
|
|
11
|
+
* note on the page says the first bar covers December only.
|
|
12
|
+
*/
|
|
13
|
+
export const CFPB_COMPLAINT_FIRST_YEAR = 2011;
|
|
14
|
+
/**
|
|
15
|
+
* How many days back the newest-complaint probe walks.
|
|
16
|
+
*
|
|
17
|
+
* The bureau adds complaints every day, but the newest one is normally filed
|
|
18
|
+
* within the last day or two. The probe walks back from today and stops at
|
|
19
|
+
* the first day with a complaint, so a quiet weekend costs a few requests.
|
|
20
|
+
*/
|
|
21
|
+
export const CFPB_NEWEST_DATE_PROBE_DAYS = 14;
|
|
22
|
+
/** How many products and companies one fetch keeps. */
|
|
23
|
+
export const CFPB_TOP_LIST_LIMIT = 10;
|
|
24
|
+
/** Committed snapshot, so a build works when consumerfinance.gov is unreachable. */
|
|
25
|
+
const CFPB_COMPLAINT_FIXTURE_FILENAME = 'cfpb-consumer-complaints-2026-10-03.json';
|
|
26
|
+
/** Shape of the totals-only response, `size=0&no_aggs=true`. */
|
|
27
|
+
const CFPB_TOTAL_SCHEMA = z.object({
|
|
28
|
+
hits: z.object({ total: z.object({ value: z.number() }) }),
|
|
29
|
+
});
|
|
30
|
+
const CFPB_BUCKET_SCHEMA = z.object({ key: z.string(), doc_count: z.number() });
|
|
31
|
+
// The terms aggregations are nested twice: once for the aggregation name and
|
|
32
|
+
// once for the field group, so `product.buckets` sits under `aggregations.product.product`.
|
|
33
|
+
const CFPB_AGGREGATIONS_SCHEMA = z.object({
|
|
34
|
+
aggregations: z.object({
|
|
35
|
+
product: z.object({ product: z.object({ buckets: z.array(CFPB_BUCKET_SCHEMA) }) }),
|
|
36
|
+
company: z.object({ company: z.object({ buckets: z.array(CFPB_BUCKET_SCHEMA) }) }),
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
// The committed snapshot holds the folded counts rather than the raw rows,
|
|
40
|
+
// because the full file is more than eighteen million records.
|
|
41
|
+
const CFPB_SNAPSHOT_SCHEMA = z.object({
|
|
42
|
+
exportedAt: z.string(),
|
|
43
|
+
firstYear: z.number(),
|
|
44
|
+
newestYear: z.number(),
|
|
45
|
+
newestReceivedDate: z.string(),
|
|
46
|
+
totalComplaints: z.number(),
|
|
47
|
+
years: z.array(z.object({ year: z.number(), complaintCount: z.number() })).min(1),
|
|
48
|
+
topProducts: z.array(z.object({ product: z.string(), complaintCount: z.number() })),
|
|
49
|
+
topCompanies: z.array(z.object({ company: z.string(), complaintCount: z.number() })),
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Builds the request URL for one calendar year of complaint totals.
|
|
53
|
+
*
|
|
54
|
+
* `no_aggs=true` drops the term aggregations the search API computes by
|
|
55
|
+
* default, which takes the response from several hundred kilobytes to about
|
|
56
|
+
* fifteen, and `size=0` drops the rows.
|
|
57
|
+
*
|
|
58
|
+
* @param year - the calendar year to read
|
|
59
|
+
* @returns the full request URL
|
|
60
|
+
*/
|
|
61
|
+
export function buildCfpComplaintCountUrl(year) {
|
|
62
|
+
const params = new URLSearchParams({
|
|
63
|
+
size: '0',
|
|
64
|
+
no_aggs: 'true',
|
|
65
|
+
date_received_min: `${year}-01-01`,
|
|
66
|
+
date_received_max: `${year}-12-31`,
|
|
67
|
+
});
|
|
68
|
+
return `${CFPB_COMPLAINT_SEARCH_API_BASE}?${params.toString()}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Builds the request URL for one day of complaint totals.
|
|
72
|
+
*
|
|
73
|
+
* @param isoDate - the day to read, e.g. "2026-10-02"
|
|
74
|
+
* @returns the full request URL
|
|
75
|
+
*/
|
|
76
|
+
export function buildCfpComplaintDayUrl(isoDate) {
|
|
77
|
+
const params = new URLSearchParams({
|
|
78
|
+
size: '0',
|
|
79
|
+
no_aggs: 'true',
|
|
80
|
+
date_received_min: isoDate,
|
|
81
|
+
date_received_max: isoDate,
|
|
82
|
+
});
|
|
83
|
+
return `${CFPB_COMPLAINT_SEARCH_API_BASE}?${params.toString()}`;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Builds the request URL for the product and company tallies.
|
|
87
|
+
*
|
|
88
|
+
* @returns the full request URL
|
|
89
|
+
*/
|
|
90
|
+
export function buildCfpComplaintAggregationUrl() {
|
|
91
|
+
return `${CFPB_COMPLAINT_SEARCH_API_BASE}?size=0`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Reads the total complaint count out of a totals-only response.
|
|
95
|
+
*
|
|
96
|
+
* @param payload - the JSON body from the search API
|
|
97
|
+
* @returns the number of complaints the query matched
|
|
98
|
+
*/
|
|
99
|
+
export function parseCfpComplaintTotal(payload) {
|
|
100
|
+
const parsed = CFPB_TOTAL_SCHEMA.safeParse(payload);
|
|
101
|
+
if (!parsed.success) {
|
|
102
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', parsed.error.message);
|
|
103
|
+
}
|
|
104
|
+
return parsed.data.hits.total.value;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Reads the product and company tallies out of an aggregation response.
|
|
108
|
+
*
|
|
109
|
+
* @param payload - the JSON body from the search API with aggregations
|
|
110
|
+
* @returns products and companies, most complaints first
|
|
111
|
+
*/
|
|
112
|
+
export function parseCfpComplaintAggregations(payload) {
|
|
113
|
+
const parsed = CFPB_AGGREGATIONS_SCHEMA.safeParse(payload);
|
|
114
|
+
if (!parsed.success) {
|
|
115
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', parsed.error.message);
|
|
116
|
+
}
|
|
117
|
+
const topProducts = parsed.data.aggregations.product.product.buckets
|
|
118
|
+
.filter((bucket) => bucket.doc_count > 0)
|
|
119
|
+
.map((bucket) => ({ product: bucket.key, complaintCount: bucket.doc_count }))
|
|
120
|
+
.slice(0, CFPB_TOP_LIST_LIMIT);
|
|
121
|
+
const topCompanies = parsed.data.aggregations.company.company.buckets
|
|
122
|
+
.filter((bucket) => bucket.doc_count > 0)
|
|
123
|
+
.map((bucket) => ({ company: bucket.key, complaintCount: bucket.doc_count }))
|
|
124
|
+
.slice(0, CFPB_TOP_LIST_LIMIT);
|
|
125
|
+
return { topProducts, topCompanies };
|
|
126
|
+
}
|
|
127
|
+
/** Picks the year with the most, or fewest, complaints. Ties go to the earlier year. */
|
|
128
|
+
function extremeYear(years, direction) {
|
|
129
|
+
const first = years[0];
|
|
130
|
+
if (first === undefined) {
|
|
131
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', 'No years to summarise');
|
|
132
|
+
}
|
|
133
|
+
return years.reduce((best, year) => {
|
|
134
|
+
if (direction === 'most') {
|
|
135
|
+
return year.complaintCount > best.complaintCount ? year : best;
|
|
136
|
+
}
|
|
137
|
+
return year.complaintCount < best.complaintCount ? year : best;
|
|
138
|
+
}, first);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Folds one window of year counts into the series a story reads.
|
|
142
|
+
*
|
|
143
|
+
* The newest year in the window is the year the bureau is still filling, so
|
|
144
|
+
* the busiest and quietest years are picked from the years before it.
|
|
145
|
+
*
|
|
146
|
+
* @param input - the year rows and the tallies read alongside them
|
|
147
|
+
* @returns the counts and the named years
|
|
148
|
+
*/
|
|
149
|
+
export function buildCfpConsumerComplaintSeries(input) {
|
|
150
|
+
if (input.newestYear <= input.firstYear) {
|
|
151
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', 'The window needs at least two years');
|
|
152
|
+
}
|
|
153
|
+
const first = input.years[0];
|
|
154
|
+
const newest = input.years[input.years.length - 1];
|
|
155
|
+
if (first === undefined || newest === undefined || first.year !== input.firstYear) {
|
|
156
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', 'The year rows do not cover the window asked for');
|
|
157
|
+
}
|
|
158
|
+
if (newest.year !== input.newestYear) {
|
|
159
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', 'The year rows do not reach the newest year');
|
|
160
|
+
}
|
|
161
|
+
const totalComplaints = input.years.reduce((sum, year) => sum + year.complaintCount, 0);
|
|
162
|
+
if (totalComplaints === 0) {
|
|
163
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', 'No complaints to summarise');
|
|
164
|
+
}
|
|
165
|
+
const completeYears = input.years.filter((year) => year.year !== input.newestYear);
|
|
166
|
+
if (completeYears.length === 0) {
|
|
167
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', 'No complete year to compare against');
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
firstYear: input.firstYear,
|
|
171
|
+
newestYear: input.newestYear,
|
|
172
|
+
newestReceivedDate: input.newestReceivedDate,
|
|
173
|
+
totalComplaints,
|
|
174
|
+
years: input.years,
|
|
175
|
+
completeYearCount: completeYears.length,
|
|
176
|
+
busiestCompleteYear: extremeYear(completeYears, 'most'),
|
|
177
|
+
quietestCompleteYear: extremeYear(completeYears, 'fewest'),
|
|
178
|
+
topProducts: input.topProducts,
|
|
179
|
+
topCompanies: input.topCompanies,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Parses the folded counts a committed snapshot holds into the story shape.
|
|
184
|
+
*
|
|
185
|
+
* @param payload - the JSON body of a committed snapshot
|
|
186
|
+
* @returns the counts and the named years
|
|
187
|
+
*/
|
|
188
|
+
export function parseCfpComplaintSnapshot(payload) {
|
|
189
|
+
const parsed = CFPB_SNAPSHOT_SCHEMA.safeParse(payload);
|
|
190
|
+
if (!parsed.success) {
|
|
191
|
+
throw new UsSourceParseError('cfpb-consumer-complaints', parsed.error.message);
|
|
192
|
+
}
|
|
193
|
+
return buildCfpConsumerComplaintSeries({
|
|
194
|
+
firstYear: parsed.data.firstYear,
|
|
195
|
+
newestYear: parsed.data.newestYear,
|
|
196
|
+
newestReceivedDate: parsed.data.newestReceivedDate,
|
|
197
|
+
years: parsed.data.years,
|
|
198
|
+
topProducts: parsed.data.topProducts,
|
|
199
|
+
topCompanies: parsed.data.topCompanies,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Walks back from today to the newest day with a published complaint.
|
|
204
|
+
*
|
|
205
|
+
* The search API has no date sort, so the adapter asks a day at a time and
|
|
206
|
+
* stops at the first non-empty one. That is normally one or two requests.
|
|
207
|
+
*
|
|
208
|
+
* @param fetchImpl - the fetch implementation to use
|
|
209
|
+
* @param today - the day to start from, defaulting to now
|
|
210
|
+
* @returns the newest day with a complaint, e.g. "2026-10-02"
|
|
211
|
+
*/
|
|
212
|
+
export async function findNewestCfpComplaintDate(fetchImpl, today = new Date()) {
|
|
213
|
+
for (let daysBack = 0; daysBack < CFPB_NEWEST_DATE_PROBE_DAYS; daysBack += 1) {
|
|
214
|
+
const day = new Date(today.getTime() - daysBack * 24 * 60 * 60 * 1000);
|
|
215
|
+
const isoDate = day.toISOString().slice(0, 10);
|
|
216
|
+
const response = await fetchImpl(buildCfpComplaintDayUrl(isoDate));
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
throw new UsSourceApiError('cfpb-consumer-complaints', `HTTP ${response.status} reading the complaint count for ${isoDate}`);
|
|
219
|
+
}
|
|
220
|
+
if (parseCfpComplaintTotal(await response.json()) > 0) {
|
|
221
|
+
return isoDate;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
throw new UsSourceApiError('cfpb-consumer-complaints', `No complaint found in the last ${String(CFPB_NEWEST_DATE_PROBE_DAYS)} days`);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Reads the complaint database at build time.
|
|
228
|
+
*
|
|
229
|
+
* The search API answers one question per request, so the window costs one
|
|
230
|
+
* request per year plus one for the product and company tallies and one or
|
|
231
|
+
* two to find the newest day. The endpoint is keyless.
|
|
232
|
+
*
|
|
233
|
+
* @param options - an optional window and fetch stub
|
|
234
|
+
* @returns the counts and the named years
|
|
235
|
+
*/
|
|
236
|
+
export async function fetchCfpConsumerComplaints(options) {
|
|
237
|
+
const fetchImpl = options?.fetchImpl ?? globalThis.fetch;
|
|
238
|
+
const firstYear = options?.firstYear ?? CFPB_COMPLAINT_FIRST_YEAR;
|
|
239
|
+
const newestYear = options?.newestYear ?? new Date().getFullYear();
|
|
240
|
+
const years = [];
|
|
241
|
+
for (let year = firstYear; year <= newestYear; year += 1) {
|
|
242
|
+
const response = await fetchImpl(buildCfpComplaintCountUrl(year));
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
throw new UsSourceApiError('cfpb-consumer-complaints', `HTTP ${response.status} reading the ${String(year)} complaint count`);
|
|
245
|
+
}
|
|
246
|
+
years.push({ year, complaintCount: parseCfpComplaintTotal(await response.json()) });
|
|
247
|
+
}
|
|
248
|
+
const aggregationResponse = await fetchImpl(buildCfpComplaintAggregationUrl());
|
|
249
|
+
if (!aggregationResponse.ok) {
|
|
250
|
+
throw new UsSourceApiError('cfpb-consumer-complaints', `HTTP ${aggregationResponse.status} reading the product and company tallies`);
|
|
251
|
+
}
|
|
252
|
+
const { topProducts, topCompanies } = parseCfpComplaintAggregations(await aggregationResponse.json());
|
|
253
|
+
const newestReceivedDate = await findNewestCfpComplaintDate(fetchImpl);
|
|
254
|
+
return buildCfpConsumerComplaintSeries({
|
|
255
|
+
firstYear,
|
|
256
|
+
newestYear,
|
|
257
|
+
newestReceivedDate,
|
|
258
|
+
years,
|
|
259
|
+
topProducts,
|
|
260
|
+
topCompanies,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/** US Consumer Financial Protection Bureau Consumer Complaint Database, keyless. */
|
|
264
|
+
export const cfpbConsumerComplaintsAdapter = {
|
|
265
|
+
id: 'cfpb-consumer-complaints',
|
|
266
|
+
name: 'CFPB consumer complaints',
|
|
267
|
+
auth: 'none',
|
|
268
|
+
description: 'Every consumer complaint the Consumer Financial Protection Bureau has sent to a company, counted by year, with the products and companies named most often.',
|
|
269
|
+
fetchLive: async (options) => fetchCfpConsumerComplaints(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
270
|
+
parse: (payload) => parseCfpComplaintSnapshot(payload),
|
|
271
|
+
loadFixture: () => parseCfpComplaintSnapshot(readFixtureJson(CFPB_COMPLAINT_FIXTURE_FILENAME)),
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=cfpbConsumerComplaints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cfpbConsumerComplaints.js","sourceRoot":"","sources":["../src/cfpbConsumerComplaints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { UsDataAdapter } from './types.js';
|
|
2
|
+
/** One consumer product recall, trimmed to the fields a story counts. */
|
|
3
|
+
export interface CpscProductRecall {
|
|
4
|
+
/** The agency's recall number, e.g. "26156". */
|
|
5
|
+
recallNumber: string;
|
|
6
|
+
/** Date the recall was published, e.g. "2025-12-18". */
|
|
7
|
+
recallDate: string;
|
|
8
|
+
/** Title of the recall notice. */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Names of the products the recall covers. */
|
|
11
|
+
productNames: string[];
|
|
12
|
+
/** Remedies offered, e.g. "Refund" or "Repair". Empty when none are listed. */
|
|
13
|
+
remedyOptions: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Countries that made the recalled product, e.g. "China". A recall names
|
|
16
|
+
* every country involved, so one recall can carry several.
|
|
17
|
+
*/
|
|
18
|
+
manufacturerCountries: string[];
|
|
19
|
+
}
|
|
20
|
+
/** One calendar year of recalls. */
|
|
21
|
+
export interface CpscRecallYearCount {
|
|
22
|
+
year: number;
|
|
23
|
+
recallCount: number;
|
|
24
|
+
}
|
|
25
|
+
/** One remedy option and how many recalls offer it. */
|
|
26
|
+
export interface CpscRemedyOptionCount {
|
|
27
|
+
option: string;
|
|
28
|
+
recallCount: number;
|
|
29
|
+
}
|
|
30
|
+
/** One manufacturer country and how many recalls name it. */
|
|
31
|
+
export interface CpscManufacturerCountryCount {
|
|
32
|
+
country: string;
|
|
33
|
+
recallCount: number;
|
|
34
|
+
}
|
|
35
|
+
/** The recall file folded into the shape a story reads. */
|
|
36
|
+
export interface CpscProductRecallSeries {
|
|
37
|
+
firstYear: number;
|
|
38
|
+
/** Newest year in the window. The agency is still filling it. */
|
|
39
|
+
newestYear: number;
|
|
40
|
+
/** Date of the newest recall in the file, e.g. "2026-09-24". */
|
|
41
|
+
newestRecallDate: string;
|
|
42
|
+
totalRecalls: number;
|
|
43
|
+
/** One entry per year in the window, oldest first. A year with no recall keeps its zero row. */
|
|
44
|
+
years: CpscRecallYearCount[];
|
|
45
|
+
/** Years in the window before the newest one, which is complete. */
|
|
46
|
+
completeYearCount: number;
|
|
47
|
+
/** Busiest of the complete years. */
|
|
48
|
+
busiestCompleteYear: CpscRecallYearCount;
|
|
49
|
+
/** Quietest of the complete years. */
|
|
50
|
+
quietestCompleteYear: CpscRecallYearCount;
|
|
51
|
+
/** Remedy options, most recalls first. */
|
|
52
|
+
remedyOptions: CpscRemedyOptionCount[];
|
|
53
|
+
/** Manufacturer countries, most recalls first. */
|
|
54
|
+
manufacturerCountries: CpscManufacturerCountryCount[];
|
|
55
|
+
}
|
|
56
|
+
/** Window of calendar years one fetch reads. */
|
|
57
|
+
export interface CpscRecallWindow {
|
|
58
|
+
firstYear: number;
|
|
59
|
+
newestYear: number;
|
|
60
|
+
}
|
|
61
|
+
/** Base URL for the SaferProducts.gov recall service. */
|
|
62
|
+
export declare const CPSC_RECALL_API_BASE = "https://www.saferproducts.gov/RestWebServices/Recall";
|
|
63
|
+
/**
|
|
64
|
+
* First calendar year the adapter reads.
|
|
65
|
+
*
|
|
66
|
+
* The service holds recalls from the 1970s, but a story that leans on recent
|
|
67
|
+
* years does not need a fifty-year fetch: every request costs a year of
|
|
68
|
+
* data, and the counts before 2014 are thin enough that the year-to-year
|
|
69
|
+
* shape is noise.
|
|
70
|
+
*/
|
|
71
|
+
export declare const CPSC_RECALL_FIRST_YEAR = 2014;
|
|
72
|
+
/**
|
|
73
|
+
* Recall id the service uses for an error row.
|
|
74
|
+
*
|
|
75
|
+
* A failed query does not answer with an HTTP error. It answers 200 with a
|
|
76
|
+
* single row whose id is 0 and whose title begins "Error retrieving Recalls",
|
|
77
|
+
* which is why the parser checks the row rather than the status code.
|
|
78
|
+
*/
|
|
79
|
+
export declare const CPSC_ERROR_RECALL_ID = 0;
|
|
80
|
+
/**
|
|
81
|
+
* Builds the request URL for one calendar year of recalls.
|
|
82
|
+
*
|
|
83
|
+
* The service refuses a range wider than a year with a body reading
|
|
84
|
+
* "The underlying provider failed on Open", so a caller reads one year per
|
|
85
|
+
* request. The endpoint is keyless.
|
|
86
|
+
*
|
|
87
|
+
* @param year - the calendar year to read
|
|
88
|
+
* @returns the full request URL
|
|
89
|
+
*/
|
|
90
|
+
export declare function buildCpscRecallUrl(year: number): string;
|
|
91
|
+
/**
|
|
92
|
+
* Parses one year's response into the recalls dated inside that year.
|
|
93
|
+
*
|
|
94
|
+
* A failed query answers 200 with a single error row, which stops the parse
|
|
95
|
+
* rather than counting as a recall. A row dated outside the year asked for is
|
|
96
|
+
* dropped, so a response that ignored the date filter cannot widen the year.
|
|
97
|
+
* A row with no recall number or no readable date is a changed shape and also
|
|
98
|
+
* stops the parse.
|
|
99
|
+
*
|
|
100
|
+
* @param payload - the JSON body from the recall service
|
|
101
|
+
* @param year - the calendar year the request asked for
|
|
102
|
+
* @returns the recalls dated inside that year, in the order the agency sent them
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseCpscRecallPayload(payload: unknown, year: number): CpscProductRecall[];
|
|
105
|
+
/**
|
|
106
|
+
* Folds one window of recalls into the counts, the busiest and quietest
|
|
107
|
+
* complete years, and the remedy and country tallies.
|
|
108
|
+
*
|
|
109
|
+
* The newest year in the window is the year the agency is still filling, so
|
|
110
|
+
* the busiest and quietest years are picked from the years before it.
|
|
111
|
+
*
|
|
112
|
+
* @param recalls - the recalls read across the window
|
|
113
|
+
* @param window - the first and newest year the fetch covered
|
|
114
|
+
* @returns the counts and the named years
|
|
115
|
+
*/
|
|
116
|
+
export declare function buildCpscProductRecallSeries(recalls: CpscProductRecall[], window: CpscRecallWindow): CpscProductRecallSeries;
|
|
117
|
+
/**
|
|
118
|
+
* Parses the folded counts a snapshot holds into the story shape.
|
|
119
|
+
*
|
|
120
|
+
* A snapshot is written by the daily loop and keeps the counts, not the year
|
|
121
|
+
* of raw rows they came from. The year counts must add to the file total, so
|
|
122
|
+
* a snapshot from a half-finished fetch cannot be read as a smaller file.
|
|
123
|
+
*
|
|
124
|
+
* @param payload - the JSON body of a committed snapshot
|
|
125
|
+
* @returns the counts and the named years
|
|
126
|
+
*/
|
|
127
|
+
export declare function parseCpscRecallSnapshot(payload: unknown): CpscProductRecallSeries;
|
|
128
|
+
/**
|
|
129
|
+
* Reads the recall file one calendar year at a time.
|
|
130
|
+
*
|
|
131
|
+
* The window runs from 2014 to the current year, so a build makes one
|
|
132
|
+
* request per year and reads about thirteen. Each request carries a whole
|
|
133
|
+
* year as JSON: 2025 is 420 rows in about 1.3 MB. A failed query answers
|
|
134
|
+
* 200 with an error row, which the parser turns into a thrown error.
|
|
135
|
+
*
|
|
136
|
+
* @param options - an optional window and fetch stub
|
|
137
|
+
* @returns the counts and the named years
|
|
138
|
+
*/
|
|
139
|
+
export declare function fetchCpscProductRecalls(options?: {
|
|
140
|
+
firstYear?: number;
|
|
141
|
+
newestYear?: number;
|
|
142
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
143
|
+
}): Promise<CpscProductRecallSeries>;
|
|
144
|
+
/** US Consumer Product Safety Commission product recalls, keyless. */
|
|
145
|
+
export declare const cpscProductRecallsAdapter: UsDataAdapter<CpscProductRecallSeries>;
|
|
146
|
+
//# sourceMappingURL=cpscProductRecalls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cpscProductRecalls.d.ts","sourceRoot":"","sources":["../src/cpscProductRecalls.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB;;;OAGG;IACH,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,oCAAoC;AACpC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6DAA6D;AAC7D,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,sCAAsC;IACtC,oBAAoB,EAAE,mBAAmB,CAAC;IAC1C,0CAA0C;IAC1C,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,kDAAkD;IAClD,qBAAqB,EAAE,4BAA4B,EAAE,CAAC;CACvD;AAED,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yDAAyD;AACzD,eAAO,MAAM,oBAAoB,yDAAyD,CAAC;AAE3F;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAkEtC;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAmD1F;AAoFD;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,iBAAiB,EAAE,EAC5B,MAAM,EAAE,gBAAgB,GACvB,uBAAuB,CA4CzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,CAcjF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,CAAC,EAAE;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAmBnC;AAED,sEAAsE;AACtE,eAAO,MAAM,yBAAyB,EAAE,aAAa,CAAC,uBAAuB,CAY5E,CAAC"}
|