@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,144 @@
1
+ import type { UsDataAdapter } from './types.js';
2
+ /** One month of mean sea level at a tide gauge. */
3
+ export interface NoaaSeaLevelMonth {
4
+ /** Calendar year the month belongs to. */
5
+ year: number;
6
+ /** Calendar month, 1 for January. */
7
+ month: number;
8
+ /**
9
+ * Mean sea level for the month, in metres against the station's tidal
10
+ * datum: the 1983-2001 National Tidal Datum Epoch.
11
+ */
12
+ meanSeaLevelMeters: number;
13
+ }
14
+ /** One calendar year, averaged from the months the gauge published. */
15
+ export interface NoaaSeaLevelYear {
16
+ /** Calendar year the average covers. */
17
+ year: number;
18
+ /** Mean of the monthly values published that year. */
19
+ meanSeaLevelMeters: number;
20
+ /** Months that went into the average. A complete year holds twelve. */
21
+ monthCount: number;
22
+ }
23
+ /** The gauge a series came from. */
24
+ export interface NoaaSeaLevelStation {
25
+ /** Station id as the agency publishes it, e.g. "8518750". */
26
+ id: string;
27
+ /** Station name as the agency publishes it, e.g. "The Battery". */
28
+ name: string;
29
+ }
30
+ /** One response, as the monthly rows plus the station they came from. */
31
+ export interface NoaaSeaLevelPayload {
32
+ station: NoaaSeaLevelStation;
33
+ /** Months with a published value, in the order the agency sent them. */
34
+ months: NoaaSeaLevelMonth[];
35
+ }
36
+ /** One gauge's record, folded down to the points a story leans on. */
37
+ export interface NoaaSeaLevelSeries {
38
+ station: NoaaSeaLevelStation;
39
+ /** One entry per calendar year that has a value, oldest first. */
40
+ years: NoaaSeaLevelYear[];
41
+ yearCount: number;
42
+ firstYear: NoaaSeaLevelYear;
43
+ lastYear: NoaaSeaLevelYear;
44
+ highest: NoaaSeaLevelYear;
45
+ lowest: NoaaSeaLevelYear;
46
+ /** Least-squares trend through the annual means, in millimetres per year. */
47
+ trendMillimetresPerYear: number;
48
+ /** Change from the first year to the last, in metres. */
49
+ changeFirstToLastMeters: number;
50
+ }
51
+ /** The station and the window one request asks for. */
52
+ export interface NoaaSeaLevelQuery {
53
+ /** Station id, e.g. "8518750" for The Battery. */
54
+ stationId: string;
55
+ /** First calendar year in the window. */
56
+ startYear: number;
57
+ /** Last calendar year in the window. */
58
+ endYear: number;
59
+ }
60
+ /** Base URL for the NOAA CO-OPS data retrieval API. */
61
+ export declare const NOAA_COOPS_API_BASE = "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter";
62
+ /** CO-OPS product id for one row of monthly statistics per month. */
63
+ export declare const NOAA_SEA_LEVEL_PRODUCT = "monthly_mean";
64
+ /**
65
+ * Datum the values are read against.
66
+ *
67
+ * MSL is the station's mean sea level datum, the average of hourly heights
68
+ * over the 1983-2001 National Tidal Datum Epoch. Reading against one fixed
69
+ * epoch is what makes two years in the record comparable.
70
+ */
71
+ export declare const NOAA_SEA_LEVEL_DATUM = "MSL";
72
+ /** Units the adapter asks for: metres. */
73
+ export declare const NOAA_SEA_LEVEL_UNITS = "metric";
74
+ /** Station id for The Battery, New York, the longest sea level record in the country. */
75
+ export declare const NOAA_BATTERY_STATION_ID = "8518750";
76
+ /** Name the agency publishes for that gauge. */
77
+ export declare const NOAA_BATTERY_STATION_NAME = "The Battery";
78
+ /** First year the gauge published a monthly mean. */
79
+ export declare const NOAA_SEA_LEVEL_FIRST_YEAR = 1856;
80
+ /** Application id sent with each request, so the agency can see the caller. */
81
+ export declare const NOAA_SEA_LEVEL_APPLICATION_ID = "nzlab-usa-sources";
82
+ /**
83
+ * Builds the download URL for one gauge and window.
84
+ *
85
+ * One request covers the whole window, because the product returns one row
86
+ * per month rather than one row per reading. The endpoint is keyless.
87
+ *
88
+ * @param query - the station and the first and last year to read
89
+ * @returns the full request URL
90
+ */
91
+ export declare function buildNoaaSeaLevelUrl(query: NoaaSeaLevelQuery): string;
92
+ /**
93
+ * Parses one response into the months that carry a value.
94
+ *
95
+ * The gauge answers with a row for every month in the window. A month it
96
+ * could not compute comes back with a blank value, which is how the early
97
+ * gaps in the record arrive, so those rows are left out rather than counted
98
+ * as zero. A row missing its year or month, or a non-blank value that is not
99
+ * a number, is a changed shape and stops the parse.
100
+ *
101
+ * @param payload - the JSON body from the CO-OPS data retrieval API
102
+ * @returns the station and the months that carry a value
103
+ */
104
+ export declare function parseNoaaSeaLevelPayload(payload: unknown): NoaaSeaLevelPayload;
105
+ /**
106
+ * Fits a straight line through the annual means.
107
+ *
108
+ * The slope of that line is the average rise per year over the window. A
109
+ * positive number means the record climbed; the size of it is how fast, in
110
+ * millimetres per year.
111
+ *
112
+ * @param years - the annual means, in any order
113
+ * @returns the fitted slope in millimetres per year
114
+ */
115
+ export declare function noaaSeaLevelTrendMillimetresPerYear(years: NoaaSeaLevelYear[]): number;
116
+ /**
117
+ * Folds the parsed months into one series of calendar-year averages.
118
+ *
119
+ * A year's average is taken over the months the gauge published, so a year
120
+ * with part of its months missing is still counted, and its monthCount says
121
+ * how much of the year it covers.
122
+ *
123
+ * @param payload - the parsed response
124
+ * @returns the annual means, oldest first, plus the highest, the lowest, and the trend
125
+ */
126
+ export declare function buildNoaaSeaLevelSeries(payload: NoaaSeaLevelPayload): NoaaSeaLevelSeries;
127
+ /**
128
+ * Reads a sea level record from NOAA CO-OPS.
129
+ *
130
+ * The window defaults to the whole record at The Battery, which the gauge
131
+ * has been publishing since 1856.
132
+ *
133
+ * @param options - an optional station, window, and fetch stub
134
+ * @returns the annual means, the headline years, and the trend
135
+ */
136
+ export declare function fetchNoaaSeaLevel(options?: {
137
+ stationId?: string;
138
+ startYear?: number;
139
+ endYear?: number;
140
+ fetchImpl?: typeof globalThis.fetch;
141
+ }): Promise<NoaaSeaLevelSeries>;
142
+ /** NOAA CO-OPS monthly mean sea level, keyless. */
143
+ export declare const noaaSeaLevelAdapter: UsDataAdapter<NoaaSeaLevelSeries>;
144
+ //# sourceMappingURL=noaaSeaLevel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noaaSeaLevel.d.ts","sourceRoot":"","sources":["../src/noaaSeaLevel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,252 @@
1
+ import { z } from 'zod';
2
+ import { UsSourceApiError, UsSourceParseError } from './errors.js';
3
+ import { readFixtureJson } from './fixtures.js';
4
+ /** Base URL for the NOAA CO-OPS data retrieval API. */
5
+ export const NOAA_COOPS_API_BASE = 'https://api.tidesandcurrents.noaa.gov/api/prod/datagetter';
6
+ /** CO-OPS product id for one row of monthly statistics per month. */
7
+ export const NOAA_SEA_LEVEL_PRODUCT = 'monthly_mean';
8
+ /**
9
+ * Datum the values are read against.
10
+ *
11
+ * MSL is the station's mean sea level datum, the average of hourly heights
12
+ * over the 1983-2001 National Tidal Datum Epoch. Reading against one fixed
13
+ * epoch is what makes two years in the record comparable.
14
+ */
15
+ export const NOAA_SEA_LEVEL_DATUM = 'MSL';
16
+ /** Units the adapter asks for: metres. */
17
+ export const NOAA_SEA_LEVEL_UNITS = 'metric';
18
+ /** Station id for The Battery, New York, the longest sea level record in the country. */
19
+ export const NOAA_BATTERY_STATION_ID = '8518750';
20
+ /** Name the agency publishes for that gauge. */
21
+ export const NOAA_BATTERY_STATION_NAME = 'The Battery';
22
+ /** First year the gauge published a monthly mean. */
23
+ export const NOAA_SEA_LEVEL_FIRST_YEAR = 1856;
24
+ /** Application id sent with each request, so the agency can see the caller. */
25
+ export const NOAA_SEA_LEVEL_APPLICATION_ID = 'nzlab-usa-sources';
26
+ /** Millimetres in a metre, for the trend's units. */
27
+ const MILLIMETRES_PER_METRE = 1000;
28
+ /** First day of January, at the start of the month the window opens on. */
29
+ const WINDOW_START_DAY = '0101';
30
+ /** Last day of December, at the end of the month the window closes on. */
31
+ const WINDOW_END_DAY = '1231';
32
+ /** Committed snapshot, so a build works when the agency host is unreachable. */
33
+ const NOAA_SEA_LEVEL_FIXTURE_FILENAME = 'noaa-sea-level-2026-09-27.json';
34
+ // The API sends every field as a string and leaves a blank string where it
35
+ // has no value, so each field is optional and read defensively. A JSON level
36
+ // error arrives in place of the data when the request is not understood.
37
+ const NOAA_MONTH_ROW_SCHEMA = z.object({
38
+ year: z.string().optional(),
39
+ month: z.string().optional(),
40
+ MSL: z.string().optional(),
41
+ });
42
+ const NOAA_PAYLOAD_SCHEMA = z.object({
43
+ metadata: z
44
+ .object({
45
+ id: z.string().optional(),
46
+ name: z.string().optional(),
47
+ })
48
+ .optional(),
49
+ data: z.array(NOAA_MONTH_ROW_SCHEMA).optional(),
50
+ error: z
51
+ .object({
52
+ message: z.string().optional(),
53
+ })
54
+ .optional(),
55
+ });
56
+ /** Reads a whole number the API sends as a string, or undefined. */
57
+ function readNoaaWholeNumber(value) {
58
+ if (value === undefined || value.trim() === '') {
59
+ return undefined;
60
+ }
61
+ const parsed = Number(value);
62
+ return Number.isInteger(parsed) ? parsed : undefined;
63
+ }
64
+ /**
65
+ * Builds the download URL for one gauge and window.
66
+ *
67
+ * One request covers the whole window, because the product returns one row
68
+ * per month rather than one row per reading. The endpoint is keyless.
69
+ *
70
+ * @param query - the station and the first and last year to read
71
+ * @returns the full request URL
72
+ */
73
+ export function buildNoaaSeaLevelUrl(query) {
74
+ const params = new URLSearchParams({
75
+ product: NOAA_SEA_LEVEL_PRODUCT,
76
+ application: NOAA_SEA_LEVEL_APPLICATION_ID,
77
+ begin_date: `${query.startYear}${WINDOW_START_DAY}`,
78
+ end_date: `${query.endYear}${WINDOW_END_DAY}`,
79
+ datum: NOAA_SEA_LEVEL_DATUM,
80
+ station: query.stationId,
81
+ time_zone: 'gmt',
82
+ units: NOAA_SEA_LEVEL_UNITS,
83
+ format: 'json',
84
+ });
85
+ return `${NOAA_COOPS_API_BASE}?${params.toString()}`;
86
+ }
87
+ /**
88
+ * Parses one response into the months that carry a value.
89
+ *
90
+ * The gauge answers with a row for every month in the window. A month it
91
+ * could not compute comes back with a blank value, which is how the early
92
+ * gaps in the record arrive, so those rows are left out rather than counted
93
+ * as zero. A row missing its year or month, or a non-blank value that is not
94
+ * a number, is a changed shape and stops the parse.
95
+ *
96
+ * @param payload - the JSON body from the CO-OPS data retrieval API
97
+ * @returns the station and the months that carry a value
98
+ */
99
+ export function parseNoaaSeaLevelPayload(payload) {
100
+ const parsed = NOAA_PAYLOAD_SCHEMA.safeParse(payload);
101
+ if (!parsed.success) {
102
+ throw new UsSourceParseError('noaa', parsed.error.message);
103
+ }
104
+ if (parsed.data.error !== undefined) {
105
+ throw new UsSourceApiError('noaa', parsed.data.error.message ?? 'The API rejected the request');
106
+ }
107
+ const stationId = parsed.data.metadata?.id;
108
+ const stationName = parsed.data.metadata?.name;
109
+ if (stationId === undefined || stationName === undefined) {
110
+ throw new UsSourceParseError('noaa', 'The response carried no station metadata');
111
+ }
112
+ const months = [];
113
+ for (const row of parsed.data.data ?? []) {
114
+ const year = readNoaaWholeNumber(row.year);
115
+ const month = readNoaaWholeNumber(row.month);
116
+ if (year === undefined || month === undefined) {
117
+ throw new UsSourceParseError('noaa', `A monthly row has no year or month: ${JSON.stringify(row)}`);
118
+ }
119
+ if (row.MSL === undefined || row.MSL.trim() === '') {
120
+ continue;
121
+ }
122
+ const meanSeaLevelMeters = Number(row.MSL);
123
+ if (!Number.isFinite(meanSeaLevelMeters)) {
124
+ throw new UsSourceParseError('noaa', `Unreadable mean sea level: ${row.MSL}`);
125
+ }
126
+ months.push({ year, month, meanSeaLevelMeters });
127
+ }
128
+ if (months.length === 0) {
129
+ throw new UsSourceParseError('noaa', 'No monthly values in the response');
130
+ }
131
+ return { station: { id: stationId, name: stationName }, months };
132
+ }
133
+ /**
134
+ * Fits a straight line through the annual means.
135
+ *
136
+ * The slope of that line is the average rise per year over the window. A
137
+ * positive number means the record climbed; the size of it is how fast, in
138
+ * millimetres per year.
139
+ *
140
+ * @param years - the annual means, in any order
141
+ * @returns the fitted slope in millimetres per year
142
+ */
143
+ export function noaaSeaLevelTrendMillimetresPerYear(years) {
144
+ if (years.length < 2) {
145
+ throw new UsSourceParseError('noaa', 'A trend needs at least two years');
146
+ }
147
+ let sumYears = 0;
148
+ let sumLevels = 0;
149
+ let sumProducts = 0;
150
+ let sumSquares = 0;
151
+ for (const year of years) {
152
+ sumYears += year.year;
153
+ sumLevels += year.meanSeaLevelMeters;
154
+ sumProducts += year.year * year.meanSeaLevelMeters;
155
+ sumSquares += year.year * year.year;
156
+ }
157
+ const count = years.length;
158
+ const denominator = count * sumSquares - sumYears * sumYears;
159
+ if (denominator === 0) {
160
+ throw new UsSourceParseError('noaa', 'Every year in the window is the same year');
161
+ }
162
+ const slope = (count * sumProducts - sumYears * sumLevels) / denominator;
163
+ return slope * MILLIMETRES_PER_METRE;
164
+ }
165
+ /**
166
+ * Folds the parsed months into one series of calendar-year averages.
167
+ *
168
+ * A year's average is taken over the months the gauge published, so a year
169
+ * with part of its months missing is still counted, and its monthCount says
170
+ * how much of the year it covers.
171
+ *
172
+ * @param payload - the parsed response
173
+ * @returns the annual means, oldest first, plus the highest, the lowest, and the trend
174
+ */
175
+ export function buildNoaaSeaLevelSeries(payload) {
176
+ const monthsByYear = new Map();
177
+ for (const month of payload.months) {
178
+ const values = monthsByYear.get(month.year);
179
+ if (values === undefined) {
180
+ monthsByYear.set(month.year, [month.meanSeaLevelMeters]);
181
+ }
182
+ else {
183
+ values.push(month.meanSeaLevelMeters);
184
+ }
185
+ }
186
+ const years = [...monthsByYear.entries()]
187
+ .sort(([left], [right]) => left - right)
188
+ .map(([year, values]) => ({
189
+ year,
190
+ meanSeaLevelMeters: values.reduce((total, value) => total + value, 0) / values.length,
191
+ monthCount: values.length,
192
+ }));
193
+ const first = years[0];
194
+ const last = years[years.length - 1];
195
+ if (first === undefined || last === undefined) {
196
+ throw new UsSourceParseError('noaa', 'No years to summarise');
197
+ }
198
+ let highest = first;
199
+ let lowest = first;
200
+ for (const year of years) {
201
+ if (year.meanSeaLevelMeters > highest.meanSeaLevelMeters) {
202
+ highest = year;
203
+ }
204
+ if (year.meanSeaLevelMeters < lowest.meanSeaLevelMeters) {
205
+ lowest = year;
206
+ }
207
+ }
208
+ return {
209
+ station: payload.station,
210
+ years,
211
+ yearCount: years.length,
212
+ firstYear: first,
213
+ lastYear: last,
214
+ highest,
215
+ lowest,
216
+ trendMillimetresPerYear: noaaSeaLevelTrendMillimetresPerYear(years),
217
+ changeFirstToLastMeters: last.meanSeaLevelMeters - first.meanSeaLevelMeters,
218
+ };
219
+ }
220
+ /**
221
+ * Reads a sea level record from NOAA CO-OPS.
222
+ *
223
+ * The window defaults to the whole record at The Battery, which the gauge
224
+ * has been publishing since 1856.
225
+ *
226
+ * @param options - an optional station, window, and fetch stub
227
+ * @returns the annual means, the headline years, and the trend
228
+ */
229
+ export async function fetchNoaaSeaLevel(options) {
230
+ const fetchImpl = options?.fetchImpl ?? globalThis.fetch;
231
+ const url = buildNoaaSeaLevelUrl({
232
+ stationId: options?.stationId ?? NOAA_BATTERY_STATION_ID,
233
+ startYear: options?.startYear ?? NOAA_SEA_LEVEL_FIRST_YEAR,
234
+ endYear: options?.endYear ?? new Date().getFullYear(),
235
+ });
236
+ const response = await fetchImpl(url);
237
+ if (!response.ok) {
238
+ throw new UsSourceApiError('noaa', `HTTP ${response.status} reading the sea level record`);
239
+ }
240
+ return buildNoaaSeaLevelSeries(parseNoaaSeaLevelPayload(await response.json()));
241
+ }
242
+ /** NOAA CO-OPS monthly mean sea level, keyless. */
243
+ export const noaaSeaLevelAdapter = {
244
+ id: 'noaa-sea-level',
245
+ name: 'NOAA CO-OPS sea level',
246
+ auth: 'none',
247
+ description: 'Monthly mean sea level at a NOAA tide gauge, folded into calendar-year averages with a fitted trend.',
248
+ fetchLive: async (options) => fetchNoaaSeaLevel(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
249
+ parse: (payload) => buildNoaaSeaLevelSeries(parseNoaaSeaLevelPayload(payload)),
250
+ loadFixture: () => buildNoaaSeaLevelSeries(parseNoaaSeaLevelPayload(readFixtureJson(NOAA_SEA_LEVEL_FIXTURE_FILENAME))),
251
+ };
252
+ //# sourceMappingURL=noaaSeaLevel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noaaSeaLevel.js","sourceRoot":"","sources":["../src/noaaSeaLevel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ import type { UsDataAdapter } from './types.js';
2
+ /** One value of a counted openFDA field, with how many records carry it. */
3
+ export interface OpenFdaRecallCount {
4
+ /** The value as the agency publishes it, e.g. "Class I". */
5
+ name: string;
6
+ count: number;
7
+ }
8
+ /** One publication date and the recalls the agency published on it. */
9
+ export interface OpenFdaRecallDateCount {
10
+ /** The agency's date stamp, e.g. "20120620". */
11
+ date: string;
12
+ count: number;
13
+ }
14
+ /**
15
+ * Every response one summary is built from.
16
+ *
17
+ * The live endpoint answers one question per request, so the adapter folds
18
+ * four responses into this shape before it counts anything.
19
+ */
20
+ export interface OpenFdaFoodRecallSnapshot {
21
+ /** Enforcement reports in the food endpoint, from the response metadata. */
22
+ recallCount: number;
23
+ /** Recalls published on each date, every classification. */
24
+ reportDates: OpenFdaRecallDateCount[];
25
+ /** Recalls published on each date whose classification is Class I. */
26
+ classOneDates: OpenFdaRecallDateCount[];
27
+ /** Recalls per classification, most first. */
28
+ classifications: OpenFdaRecallCount[];
29
+ /** Recalls per initiator, most first. */
30
+ voluntary: OpenFdaRecallCount[];
31
+ }
32
+ /** One calendar year of food enforcement reports. */
33
+ export interface OpenFdaFoodRecallYear {
34
+ year: number;
35
+ total: number;
36
+ /** Class I recalls: a reasonable chance of serious harm or death. */
37
+ classOne: number;
38
+ /** Every other classification, including Class II and Class III. */
39
+ other: number;
40
+ }
41
+ /** The food recall file folded into the shape a story reads. */
42
+ export interface OpenFdaFoodRecallSummary {
43
+ recallCount: number;
44
+ /** Distinct publication dates in the file. */
45
+ reportDateCount: number;
46
+ /** Oldest publication date, e.g. "2012-06-20". */
47
+ firstReportDate: string;
48
+ /** Newest publication date, e.g. "2026-09-23". */
49
+ newestReportDate: string;
50
+ firstYear: number;
51
+ /** Newest year in the file, which is still open. */
52
+ newestYear: number;
53
+ /** Recalls published against the newest year so far. */
54
+ newestYearCount: number;
55
+ /** One entry per calendar year in the file, oldest first. */
56
+ years: OpenFdaFoodRecallYear[];
57
+ busiestYear: OpenFdaFoodRecallYear;
58
+ classOneCount: number;
59
+ /** Class I recalls as a share of the file, in percent. */
60
+ classOneSharePercent: number;
61
+ classifications: OpenFdaRecallCount[];
62
+ voluntaryCount: number;
63
+ mandatedCount: number;
64
+ }
65
+ /** Base URL for the openFDA food enforcement endpoint. */
66
+ export declare const OPENFDA_FOOD_ENFORCEMENT_BASE = "https://api.fda.gov/food/enforcement.json";
67
+ /**
68
+ * Classification FDA gives a recall that can seriously harm or kill.
69
+ *
70
+ * The endpoint carries Class I, Class II, Class III, and a handful of
71
+ * unclassified records; Class I is the one the story separates out.
72
+ */
73
+ export declare const OPENFDA_CLASS_ONE = "Class I";
74
+ /** Initiator terms the agency uses for a firm's own recall and a mandated one. */
75
+ export declare const OPENFDA_VOLUNTARY_TERM = "Voluntary: Firm initiated";
76
+ export declare const OPENFDA_MANDATED_TERM = "FDA Mandated";
77
+ /** Fields the adapter counts on. The endpoint carries exact-term variants. */
78
+ export declare const OPENFDA_REPORT_DATE_FIELD = "report_date";
79
+ export declare const OPENFDA_CLASSIFICATION_COUNT_FIELD = "classification.exact";
80
+ export declare const OPENFDA_VOLUNTARY_COUNT_FIELD = "voluntary_mandated.exact";
81
+ /**
82
+ * Builds the URL that reports how many records the endpoint holds.
83
+ *
84
+ * @returns the request URL
85
+ */
86
+ export declare function buildOpenFdaFoodRecallTotalUrl(): string;
87
+ /**
88
+ * Builds the URL for one counted field, optionally inside a search.
89
+ *
90
+ * @param options - the field to count, and an optional openFDA search clause
91
+ * @returns the request URL
92
+ */
93
+ export declare function buildOpenFdaFoodRecallCountUrl(options?: {
94
+ field?: string;
95
+ search?: string;
96
+ }): string;
97
+ /**
98
+ * Reads the record count out of the metadata block.
99
+ *
100
+ * @param payload - the JSON body from the limit=1 request
101
+ * @returns how many enforcement reports the endpoint holds
102
+ */
103
+ export declare function parseOpenFdaRecallTotal(payload: unknown): number;
104
+ /**
105
+ * Reads one counted field into value and count pairs.
106
+ *
107
+ * @param payload - the JSON body from a count request
108
+ * @returns the values with their counts, in the order the agency sent them
109
+ */
110
+ export declare function parseOpenFdaRecallCounts(payload: unknown): OpenFdaRecallCount[];
111
+ /**
112
+ * Reads a count of the date field into dated counts.
113
+ *
114
+ * @param payload - the JSON body from a count=report_date request
115
+ * @returns one entry per publication date, in the order the agency sent them
116
+ */
117
+ export declare function parseOpenFdaRecallDateCounts(payload: unknown): OpenFdaRecallDateCount[];
118
+ /**
119
+ * Reads the committed snapshot: four responses under the keys that name them.
120
+ *
121
+ * @param payload - the parsed contents of the snapshot file
122
+ * @returns the snapshot in the shape the summary builder reads
123
+ */
124
+ export declare function parseOpenFdaFoodRecallSnapshot(payload: unknown): OpenFdaFoodRecallSnapshot;
125
+ /** Turns the agency's date stamp into the ISO date a reader recognises. */
126
+ export declare function openFdaReportDateLabel(date: string): string;
127
+ /**
128
+ * Folds the four responses into the per-year counts a story reads.
129
+ *
130
+ * Every recall in the file carries a publication date and a classification,
131
+ * so the two counts have to agree: a snapshot whose dates cover fewer recalls
132
+ * than the metadata reports is refused rather than charted short.
133
+ *
134
+ * @param snapshot - the counted responses, all from the same day
135
+ * @returns the per-year counts and the figures the copy quotes
136
+ */
137
+ export declare function buildOpenFdaFoodRecallSummary(snapshot: OpenFdaFoodRecallSnapshot): OpenFdaFoodRecallSummary;
138
+ /**
139
+ * Reads the food enforcement file as four counted responses.
140
+ *
141
+ * Keyless. The endpoint answers one question per request and cannot group by
142
+ * two fields at once, so the per-year Class I counts come from a second date
143
+ * count under a search clause. Without an API key the endpoint allows 240
144
+ * requests a minute and 1,000 a day, and five requests cover the whole file.
145
+ *
146
+ * @param options - an optional fetch stub
147
+ * @returns the per-year counts and the figures the copy quotes
148
+ */
149
+ export declare function fetchOpenFdaFoodRecalls(options?: {
150
+ fetchImpl?: typeof globalThis.fetch;
151
+ }): Promise<OpenFdaFoodRecallSummary>;
152
+ /** openFDA food enforcement reports, keyless. */
153
+ export declare const openFdaFoodRecallsAdapter: UsDataAdapter<OpenFdaFoodRecallSummary>;
154
+ //# sourceMappingURL=openFdaFoodRecalls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openFdaFoodRecalls.d.ts","sourceRoot":"","sources":["../src/openFdaFoodRecalls.ts"],"names":[],"mappings":"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"}