@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.
Files changed (66) hide show
  1. package/README.md +339 -0
  2. package/dist/blsSeries.d.ts +73 -0
  3. package/dist/blsSeries.d.ts.map +1 -0
  4. package/dist/blsSeries.js +173 -0
  5. package/dist/blsSeries.js.map +1 -0
  6. package/dist/cdcCountyObesity.d.ts +116 -0
  7. package/dist/cdcCountyObesity.d.ts.map +1 -0
  8. package/dist/cdcCountyObesity.js +180 -0
  9. package/dist/cdcCountyObesity.js.map +1 -0
  10. package/dist/cfpbConsumerComplaints.d.ts +152 -0
  11. package/dist/cfpbConsumerComplaints.d.ts.map +1 -0
  12. package/dist/cfpbConsumerComplaints.js +273 -0
  13. package/dist/cfpbConsumerComplaints.js.map +1 -0
  14. package/dist/cpscProductRecalls.d.ts +146 -0
  15. package/dist/cpscProductRecalls.d.ts.map +1 -0
  16. package/dist/cpscProductRecalls.js +298 -0
  17. package/dist/cpscProductRecalls.js.map +1 -0
  18. package/dist/errors.d.ts +15 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +26 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/femaDisasterDeclarations.d.ts +123 -0
  23. package/dist/femaDisasterDeclarations.d.ts.map +1 -0
  24. package/dist/femaDisasterDeclarations.js +214 -0
  25. package/dist/femaDisasterDeclarations.js.map +1 -0
  26. package/dist/fixtures.d.ts +5 -0
  27. package/dist/fixtures.d.ts.map +1 -0
  28. package/dist/fixtures.js +11 -0
  29. package/dist/fixtures.js.map +1 -0
  30. package/dist/index.d.ts +51 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +27 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/nceiAnnualTemperature.d.ts +92 -0
  35. package/dist/nceiAnnualTemperature.d.ts.map +1 -0
  36. package/dist/nceiAnnualTemperature.js +158 -0
  37. package/dist/nceiAnnualTemperature.js.map +1 -0
  38. package/dist/noaaSeaLevel.d.ts +144 -0
  39. package/dist/noaaSeaLevel.d.ts.map +1 -0
  40. package/dist/noaaSeaLevel.js +252 -0
  41. package/dist/noaaSeaLevel.js.map +1 -0
  42. package/dist/openFdaFoodRecalls.d.ts +154 -0
  43. package/dist/openFdaFoodRecalls.d.ts.map +1 -0
  44. package/dist/openFdaFoodRecalls.js +293 -0
  45. package/dist/openFdaFoodRecalls.js.map +1 -0
  46. package/dist/registry.d.ts +28 -0
  47. package/dist/registry.d.ts.map +1 -0
  48. package/dist/registry.js +79 -0
  49. package/dist/registry.js.map +1 -0
  50. package/dist/treasuryAvgInterestRate.d.ts +107 -0
  51. package/dist/treasuryAvgInterestRate.d.ts.map +1 -0
  52. package/dist/treasuryAvgInterestRate.js +191 -0
  53. package/dist/treasuryAvgInterestRate.js.map +1 -0
  54. package/dist/types.d.ts +30 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/dist/usgsEarthquakes.d.ts +108 -0
  59. package/dist/usgsEarthquakes.d.ts.map +1 -0
  60. package/dist/usgsEarthquakes.js +199 -0
  61. package/dist/usgsEarthquakes.js.map +1 -0
  62. package/dist/usgsPeakStreamflow.d.ts +116 -0
  63. package/dist/usgsPeakStreamflow.d.ts.map +1 -0
  64. package/dist/usgsPeakStreamflow.js +211 -0
  65. package/dist/usgsPeakStreamflow.js.map +1 -0
  66. 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"}