@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
package/README.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# @usa-open-data-connectors/usa-sources
|
|
2
|
+
|
|
3
|
+
Uniform TypeScript adapters for US public data sources. Keyless first, with a
|
|
4
|
+
strict parser and a committed fixture behind every adapter so a build never
|
|
5
|
+
depends on a live API.
|
|
6
|
+
|
|
7
|
+
The HTTP API and the CLI read this package's registry (`US_DATA_SOURCES`), so a
|
|
8
|
+
new adapter is exposed on `/api/sources`, `/api/sources/{id}/probe`, and
|
|
9
|
+
`/api/sources/{id}/data` as soon as it is registered.
|
|
10
|
+
|
|
11
|
+
## Sources
|
|
12
|
+
|
|
13
|
+
| id | publisher | auth | what it reads |
|
|
14
|
+
| ---------------------------- | --------------------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| `bls-unemployment-rate` | Bureau of Labor Statistics | none | The national unemployment rate, monthly, seasonally adjusted |
|
|
16
|
+
| `usgs-hawaii-earthquakes` | US Geological Survey | none | Earthquakes of magnitude 2.5 and above near the Hawaiian islands |
|
|
17
|
+
| `cdc-county-obesity` | Centers for Disease Control (CDC) | none | The share of adults with obesity in each US county, from CDC PLACES |
|
|
18
|
+
| `ncei-annual-temperature` | NOAA National Centers for Environmental Information | none | Calendar-year average temperature for the contiguous United States, since 1895 |
|
|
19
|
+
| `noaa-sea-level` | NOAA Center for Operational Oceanographic Products and Services | none | Monthly mean sea level at a tide gauge, folded into calendar-year averages, since 1856 at The Battery |
|
|
20
|
+
| `treasury-avg-interest-rate` | US Department of the Treasury | none | Average interest rate on the interest-bearing federal debt outstanding, monthly since 2001 |
|
|
21
|
+
| `fema-disaster-declarations` | Federal Emergency Management Agency | none | Every disaster declaration FEMA has published, one row per declaration, from 1953 to the newest one |
|
|
22
|
+
| `openfda-food-recalls` | US Food and Drug Administration | none | Every food recall FDA has published as an enforcement report, from June 2012 to the newest publication date |
|
|
23
|
+
| `usgs-peak-streamflow` | US Geological Survey | none | The annual peak-flow record at a USGS stream gauge, one row per water year, since 1844 at the Mississippi River at St. Louis |
|
|
24
|
+
| `cpsc-product-recalls` | US Consumer Product Safety Commission | none | Every consumer product recall CPSC has published, one row per recall, from 2014 to the newest one |
|
|
25
|
+
| `cfpb-consumer-complaints` | Consumer Financial Protection Bureau | none | Every consumer complaint sent to a company since December 2011, counted by year, with the products and companies named most |
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { fetchBlsSeries, US_UNEMPLOYMENT_SERIES_ID } from '@usa-open-data-connectors/usa-sources';
|
|
31
|
+
|
|
32
|
+
const rate = await fetchBlsSeries(US_UNEMPLOYMENT_SERIES_ID, {
|
|
33
|
+
startYear: 2006,
|
|
34
|
+
endYear: 2025,
|
|
35
|
+
});
|
|
36
|
+
console.log(rate.latest.year, rate.latest.period, rate.latest.value);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import {
|
|
41
|
+
buildUsgsEarthquakeCatalogue,
|
|
42
|
+
fetchUsgsEarthquakes,
|
|
43
|
+
USGS_HAWAII_BOUNDS,
|
|
44
|
+
USGS_HAWAII_MIN_MAGNITUDE,
|
|
45
|
+
} from '@usa-open-data-connectors/usa-sources';
|
|
46
|
+
|
|
47
|
+
const earthquakes = await fetchUsgsEarthquakes({
|
|
48
|
+
startDate: '2025-01-01',
|
|
49
|
+
endDate: '2026-01-01',
|
|
50
|
+
minMagnitude: USGS_HAWAII_MIN_MAGNITUDE,
|
|
51
|
+
bounds: USGS_HAWAII_BOUNDS,
|
|
52
|
+
});
|
|
53
|
+
const catalogue = buildUsgsEarthquakeCatalogue(earthquakes);
|
|
54
|
+
console.log(catalogue.count, catalogue.strongest.place, catalogue.strongest.magnitude);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import {
|
|
59
|
+
buildCdcCountyObesitySet,
|
|
60
|
+
fetchCdcCountyObesity,
|
|
61
|
+
} from '@usa-open-data-connectors/usa-sources';
|
|
62
|
+
|
|
63
|
+
const set = buildCdcCountyObesitySet(await fetchCdcCountyObesity());
|
|
64
|
+
console.log(set.countyCount, set.lowest.countyName, set.highest.percent, set.national.percent);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import {
|
|
69
|
+
buildNceiAnnualTemperatureSeries,
|
|
70
|
+
fetchNceiAnnualTemperature,
|
|
71
|
+
} from '@usa-open-data-connectors/usa-sources';
|
|
72
|
+
|
|
73
|
+
const series = await fetchNceiAnnualTemperature({
|
|
74
|
+
startYear: 1895,
|
|
75
|
+
endYear: new Date().getFullYear(),
|
|
76
|
+
});
|
|
77
|
+
console.log(series.yearCount, series.warmest.year, series.coldest.valueFahrenheit);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { buildNoaaSeaLevelSeries, fetchNoaaSeaLevel } from '@usa-open-data-connectors/usa-sources';
|
|
82
|
+
|
|
83
|
+
const seaLevel = buildNoaaSeaLevelSeries(await fetchNoaaSeaLevel());
|
|
84
|
+
console.log(
|
|
85
|
+
seaLevel.station.name,
|
|
86
|
+
seaLevel.yearCount,
|
|
87
|
+
seaLevel.highest.year,
|
|
88
|
+
seaLevel.trendMillimetresPerYear
|
|
89
|
+
);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { fetchTreasuryAvgInterestRates } from '@usa-open-data-connectors/usa-sources';
|
|
94
|
+
|
|
95
|
+
const rates = await fetchTreasuryAvgInterestRates();
|
|
96
|
+
console.log(rates.monthCount, rates.lastMonth.averageInterestRatePercent, rates.lowest.recordDate);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { fetchFemaDeclarations } from '@usa-open-data-connectors/usa-sources';
|
|
101
|
+
|
|
102
|
+
const declarations = await fetchFemaDeclarations();
|
|
103
|
+
console.log(
|
|
104
|
+
declarations.declarationCount,
|
|
105
|
+
declarations.busiestYear.year,
|
|
106
|
+
declarations.incidentTypes[0]?.name
|
|
107
|
+
);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import {
|
|
112
|
+
buildUsgsPeakStreamflowSeries,
|
|
113
|
+
fetchUsgsPeakStreamflow,
|
|
114
|
+
} from '@usa-open-data-connectors/usa-sources';
|
|
115
|
+
|
|
116
|
+
const record = await fetchUsgsPeakStreamflow();
|
|
117
|
+
console.log(
|
|
118
|
+
record.yearCount,
|
|
119
|
+
record.highest.waterYear,
|
|
120
|
+
record.highest.peakDischargeCubicFeetPerSecond,
|
|
121
|
+
record.highestToLowestRatio
|
|
122
|
+
);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { fetchCpscProductRecalls } from '@usa-open-data-connectors/usa-sources';
|
|
127
|
+
|
|
128
|
+
const recalls = await fetchCpscProductRecalls();
|
|
129
|
+
console.log(
|
|
130
|
+
recalls.totalRecalls,
|
|
131
|
+
recalls.busiestCompleteYear.year,
|
|
132
|
+
recalls.remedyOptions[0]?.option,
|
|
133
|
+
recalls.manufacturerCountries[0]?.country
|
|
134
|
+
);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { fetchCfpConsumerComplaints } from '@usa-open-data-connectors/usa-sources';
|
|
139
|
+
|
|
140
|
+
const complaints = await fetchCfpConsumerComplaints();
|
|
141
|
+
console.log(
|
|
142
|
+
complaints.totalComplaints,
|
|
143
|
+
complaints.busiestCompleteYear.year,
|
|
144
|
+
complaints.newestReceivedDate,
|
|
145
|
+
complaints.topCompanies[0]?.company
|
|
146
|
+
);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Notes on the BLS API
|
|
150
|
+
|
|
151
|
+
- The public API refuses a request spanning more than ten years, so
|
|
152
|
+
`fetchBlsSeries` splits a longer range into windows: two decades cost three
|
|
153
|
+
calls once the range runs into the current year.
|
|
154
|
+
- Keyless access allows 25 series queries a day. A registered key raises that
|
|
155
|
+
to 500, and the adapter takes `apiKey` for the day that matters.
|
|
156
|
+
- The agency uses `-` for a month it could not publish, and `M13` for an
|
|
157
|
+
annual average. Both are dropped, so one year holds twelve monthly values.
|
|
158
|
+
|
|
159
|
+
## Notes on the USGS earthquake catalogue
|
|
160
|
+
|
|
161
|
+
- The event query answers a window of any length, one request per window. The
|
|
162
|
+
box, the magnitude floor, and the dates come from the caller.
|
|
163
|
+
- The feed mixes tectonic earthquakes with quarry blasts, explosions, and ice
|
|
164
|
+
quakes, and a row can arrive without a magnitude. The parser keeps only
|
|
165
|
+
tectonic earthquakes that carry a magnitude and a depth.
|
|
166
|
+
- The catalogue answers newest first. `parseUsgsEarthquakes` returns oldest
|
|
167
|
+
first, so a caller reading index 0 gets the start of the window.
|
|
168
|
+
|
|
169
|
+
## Notes on the CDC PLACES county release
|
|
170
|
+
|
|
171
|
+
- The resource id is per release. `cdc-county-obesity` reads the 2025
|
|
172
|
+
release, whose estimates come from the 2023 Behavioral Risk Factor
|
|
173
|
+
Surveillance System (BRFSS) and the Census Bureau's 2023 county population
|
|
174
|
+
estimates. A new release gets a new id, so a caller pinned to this one
|
|
175
|
+
keeps reading the numbers it was written against.
|
|
176
|
+
- Every row is a model-based estimate, not a count. The release publishes
|
|
177
|
+
each measure twice, as crude and as age-adjusted prevalence; the adapter
|
|
178
|
+
reads the crude rows, because age-adjusted ones answer a different question.
|
|
179
|
+
- A county the model could not estimate arrives with no value, and the
|
|
180
|
+
release carries no obesity rows at all for Kentucky and Pennsylvania. Rows
|
|
181
|
+
without a value are dropped, and the national row is kept apart from the
|
|
182
|
+
counties rather than counted as one.
|
|
183
|
+
|
|
184
|
+
## Notes on the Climate at a Glance download
|
|
185
|
+
|
|
186
|
+
- The download is a CSV, not JSON: two comment lines, a header, then one row
|
|
187
|
+
per year as `YYYYMM,value` in degrees Fahrenheit. `parseNceiAnnualTemperatureCsv`
|
|
188
|
+
skips the comments and the header, and stops on any other line shape rather
|
|
189
|
+
than dropping it.
|
|
190
|
+
- The URL carries a window length and a window end month. Twelve months ending
|
|
191
|
+
in December is the calendar year, which is the number a story about a year
|
|
192
|
+
can compare. A shorter window ending in December would be that month alone,
|
|
193
|
+
and the parser refuses a file with more than one row per year.
|
|
194
|
+
- The current year joins the file only once December has closed it, so the
|
|
195
|
+
newest row is the last complete calendar year. The 2026 download is dated
|
|
196
|
+
2026-09-26 and ends at 2025.
|
|
197
|
+
|
|
198
|
+
## Notes on the CO-OPS sea level record
|
|
199
|
+
|
|
200
|
+
- One request covers the whole window: the `monthly_mean` product answers one
|
|
201
|
+
row per month rather than one row per reading, so 170 years cost one call
|
|
202
|
+
and about 460 KB. The endpoint is keyless.
|
|
203
|
+
- Values are metres against the station's MSL datum, the average of hourly
|
|
204
|
+
heights over the 1983-2001 National Tidal Datum Epoch. Reading the whole
|
|
205
|
+
record against one fixed epoch is what makes two years comparable.
|
|
206
|
+
- The API answers a row for every month in the window and leaves the value
|
|
207
|
+
blank when it has none, which is how the early gaps arrive (1920 holds
|
|
208
|
+
seven months, 1861 and 1879-1892 hold none). Blank rows are dropped, and a
|
|
209
|
+
year's average is taken over the months it does have, so `monthCount` says
|
|
210
|
+
how much of the year a value covers.
|
|
211
|
+
- Errors come back in the body with HTTP 200, as
|
|
212
|
+
`{"error":{"message":"..."}}`, so the parser checks for that before reading
|
|
213
|
+
the data. A wrong product name answers with plain text instead, which fails
|
|
214
|
+
the shape check.
|
|
215
|
+
|
|
216
|
+
## Notes on the Treasury average interest rate
|
|
217
|
+
|
|
218
|
+
- One keyless request covers the whole run. The dataset answers a row per
|
|
219
|
+
month per security type, and the filter pins the response to
|
|
220
|
+
`security_type_desc:eq:Interest-bearing Debt`, which is the portfolio total
|
|
221
|
+
the bureau's own monthly statement is built from. The other rows, the rates
|
|
222
|
+
on bills, notes, bonds, and non-marketable debt, never reach the parser.
|
|
223
|
+
- The rate is the average across everything outstanding, not the rate on
|
|
224
|
+
anything issued today. That is why it moves slowly: a new bill reprices a
|
|
225
|
+
fraction of the portfolio, and a bond issued in 2019 still pays its own
|
|
226
|
+
coupon.
|
|
227
|
+
- A page holds at most 10,000 rows and the run is around 300, so the adapter
|
|
228
|
+
asks for the whole series in one request and does not page. A run longer
|
|
229
|
+
than the cap would be a change worth making here rather than a silent
|
|
230
|
+
truncation, so the row limit is a named constant.
|
|
231
|
+
- Each row is dated to the last day of its month. A month the agency could
|
|
232
|
+
not compute would arrive without a value, and those rows are dropped.
|
|
233
|
+
- A request the API rejects answers with an HTTP error and a body of
|
|
234
|
+
`{"error":"Invalid Query Param","message":"..."}`, which the parser reports
|
|
235
|
+
as an API error rather than a shape error.
|
|
236
|
+
|
|
237
|
+
## Notes on the USGS peak-flow record
|
|
238
|
+
|
|
239
|
+
- The adapter reads the `peaks` collection on the USGS Water Data OGC API
|
|
240
|
+
(`https://api.waterdata.usgs.gov/ogcapi/v0`), not the older
|
|
241
|
+
`waterservices.usgs.gov` endpoints. The older statistics service answers
|
|
242
|
+
`503 Service unavailable` to roughly half of scripted requests, which is
|
|
243
|
+
enough to fail a build; the OGC API answered every request in the same
|
|
244
|
+
window. The daily-values service on the older host still works and is worth
|
|
245
|
+
using when a daily series is what a story needs.
|
|
246
|
+
- Every number in a row arrives as a string, including the peak itself, so
|
|
247
|
+
the parser converts once and keeps the number. `value` blank means the
|
|
248
|
+
agency filed no peak for that year, and the row is dropped rather than
|
|
249
|
+
read as zero.
|
|
250
|
+
- The rows are dated by **water year**, which starts on 1 October. A peak on
|
|
251
|
+
1955-10-08 is filed under water year 1956, so `waterYear` and
|
|
252
|
+
`calendarYear` are separate fields and are not always the same number. The
|
|
253
|
+
record is sorted and charted by water year, because that is the unit the
|
|
254
|
+
agency files and the unit a flood season belongs to.
|
|
255
|
+
- The record has holes. At the default gauge there is no row for the 17
|
|
256
|
+
water years from 1845 to 1861, and the series lists them in
|
|
257
|
+
`missingWaterYears` rather than drawing them as zero, because a year with
|
|
258
|
+
no filing is not a year the river ran dry.
|
|
259
|
+
- A gauge with several peaks in one water year is filed once, with the
|
|
260
|
+
largest. The adapter does not de-duplicate, so a response carrying two rows
|
|
261
|
+
for one water year would be a change in the source worth seeing.
|
|
262
|
+
- Rows carry the agency's qualifiers, for example `UNKNOWNREGULATION` where
|
|
263
|
+
the agency could not tell whether works upstream changed the flow, or
|
|
264
|
+
`MAXDAILYMEAN` where the peak is a daily mean rather than an instantaneous
|
|
265
|
+
reading. They are kept on each year rather than dropped, so the copy can be
|
|
266
|
+
honest about them.
|
|
267
|
+
- One keyless request covers the whole record. The default gauge answers 165
|
|
268
|
+
water years in one page of about 126 KB.
|
|
269
|
+
|
|
270
|
+
## Notes on the CPSC recall service
|
|
271
|
+
|
|
272
|
+
- The adapter reads
|
|
273
|
+
`https://www.saferproducts.gov/RestWebServices/Recall`, the same service
|
|
274
|
+
behind the public SaferProducts.gov search. It is keyless.
|
|
275
|
+
- One request covers one calendar year. A range wider than a year fails: the
|
|
276
|
+
service answers HTTP 200 with a single row whose title reads
|
|
277
|
+
`Error retrieving Recalls: The underlying provider failed on Open.` The
|
|
278
|
+
adapter reads a year at a time for that reason, and the parser throws on
|
|
279
|
+
that row rather than counting it as a recall.
|
|
280
|
+
- A year of rows is heavy as JSON. 2025 is 420 recalls in about 1.3 MB, so the
|
|
281
|
+
default window of 2014 to the current year costs about 11 MB across 13
|
|
282
|
+
requests. A year with no recall keeps a zero row in `years` rather than
|
|
283
|
+
disappearing from the chart.
|
|
284
|
+
- The newest year in the window is the year the agency is still filling, so
|
|
285
|
+
`busiestCompleteYear` and `quietestCompleteYear` are picked from the years
|
|
286
|
+
before it. `completeYearCount` says how many those were.
|
|
287
|
+
- `ManufacturerCountries` names every country a recall touches, so a recall
|
|
288
|
+
can list several and the country counts add to more than the recall count.
|
|
289
|
+
In the 2014 to 2026 window China appears on 2,312 of 3,986 recalls, the
|
|
290
|
+
United States on 753, and 122 countries appear at least once.
|
|
291
|
+
- `RemedyOptions` is nearly an enum (Refund, Repair, Replace, Dispose, New
|
|
292
|
+
Instructions, Label, Inspect) with two junk rows in it: one carries `R` and
|
|
293
|
+
one carries a whole paragraph of consumer instructions. The adapter keeps
|
|
294
|
+
whatever the agency sends and sorts the counts, so the tail of the list
|
|
295
|
+
shows the mess rather than hiding it.
|
|
296
|
+
- The service holds recalls from the 1970s. The adapter starts at 2014, which
|
|
297
|
+
keeps the window to 13 requests and to the years a reading of the current
|
|
298
|
+
trend needs.
|
|
299
|
+
- The committed snapshot (`cpsc-product-recalls-2026-10-02.json`) holds the
|
|
300
|
+
folded counts, not the raw years, because a year of raw rows is about a
|
|
301
|
+
megabyte and the counts are a few kilobytes. The second fixture
|
|
302
|
+
(`cpsc-recall-rows-2014-2026-sampled-2026-10-02.json`) holds the first three
|
|
303
|
+
recalls of every year in the window, which is what the registry test answers
|
|
304
|
+
each per-year request with.
|
|
305
|
+
|
|
306
|
+
## Notes on the Consumer Complaint Database
|
|
307
|
+
|
|
308
|
+
- The adapter reads
|
|
309
|
+
`https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/`,
|
|
310
|
+
the search endpoint behind the public Consumer Complaint Database. It is
|
|
311
|
+
keyless. The bureau answers a scripted request with 403 and a request with a
|
|
312
|
+
declared user agent with 200.
|
|
313
|
+
- The search API computes its term aggregations on every request, which takes
|
|
314
|
+
the body from about fifteen kilobytes to about four hundred. Adding
|
|
315
|
+
`no_aggs=true` drops them, so the adapter asks for `size=0&no_aggs=true` and
|
|
316
|
+
reads `hits.total.value` when it only needs a count, and makes one extra
|
|
317
|
+
request without `no_aggs` for the product and company tallies.
|
|
318
|
+
- The API answers one question per request and has no date sort, so the window
|
|
319
|
+
costs one request per year plus one for the tallies plus one or two to find
|
|
320
|
+
the newest day. `findNewestCfpComplaintDate` walks back from today and stops
|
|
321
|
+
at the first day with a complaint.
|
|
322
|
+
- The first year in the window, 2011, holds only December: the bureau
|
|
323
|
+
published its first complaints on 1 December 2011. It is kept rather than
|
|
324
|
+
dropped, and the page's data note says the first bar covers one month.
|
|
325
|
+
- The product tally splits credit reporting across more than one label because
|
|
326
|
+
the bureau changed its product taxonomy over the years. The adapter keeps
|
|
327
|
+
whatever the API sends rather than merging labels, so a reader of
|
|
328
|
+
`topProducts` should not assume the labels are stable.
|
|
329
|
+
- The committed snapshot (`cfpb-consumer-complaints-2026-10-03.json`) holds
|
|
330
|
+
the folded counts, not the eighteen million raw rows. The registry test
|
|
331
|
+
answers each per-year request, the newest-day probe, and the tally request
|
|
332
|
+
from that one fixture.
|
|
333
|
+
|
|
334
|
+
## Checks
|
|
335
|
+
|
|
336
|
+
```sh
|
|
337
|
+
npm run test --workspace @usa-open-data-connectors/usa-sources
|
|
338
|
+
npm run test:smoke --workspace @usa-open-data-connectors/usa-sources # hits the live API
|
|
339
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { UsDataAdapter } from './types.js';
|
|
2
|
+
/** One month of a BLS time series. */
|
|
3
|
+
export interface BlsObservation {
|
|
4
|
+
seriesId: string;
|
|
5
|
+
year: number;
|
|
6
|
+
/** BLS period code: M01 to M12 for months, M13 for an annual average. */
|
|
7
|
+
period: string;
|
|
8
|
+
periodName: string;
|
|
9
|
+
value: number;
|
|
10
|
+
}
|
|
11
|
+
/** Rolled-up facts about one BLS series. */
|
|
12
|
+
export interface BlsSeries {
|
|
13
|
+
seriesId: string;
|
|
14
|
+
points: BlsObservation[];
|
|
15
|
+
first: BlsObservation;
|
|
16
|
+
latest: BlsObservation;
|
|
17
|
+
peak: BlsObservation;
|
|
18
|
+
lowest: BlsObservation;
|
|
19
|
+
changeFromPeak: number;
|
|
20
|
+
changeFromFirst: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The national unemployment rate, seasonally adjusted. Published monthly by
|
|
24
|
+
* the Bureau of Labor Statistics, the agency that runs the Current Population
|
|
25
|
+
* Survey.
|
|
26
|
+
*/
|
|
27
|
+
export declare const US_UNEMPLOYMENT_SERIES_ID = "LNS14000000";
|
|
28
|
+
/** Human-readable name for the unemployment series. */
|
|
29
|
+
export declare const US_UNEMPLOYMENT_SERIES_LABEL = "US unemployment rate, seasonally adjusted";
|
|
30
|
+
/** Base URL for the BLS public data API. */
|
|
31
|
+
export declare const BLS_API_BASE = "https://api.bls.gov/publicAPI/v2/timeseries/data";
|
|
32
|
+
/**
|
|
33
|
+
* The BLS public API refuses a request spanning more than ten years, so a
|
|
34
|
+
* longer range has to be assembled from several windows.
|
|
35
|
+
*/
|
|
36
|
+
export declare const BLS_MAX_YEARS_PER_REQUEST = 10;
|
|
37
|
+
/**
|
|
38
|
+
* Parses a BLS timeseries payload into flat observations.
|
|
39
|
+
*
|
|
40
|
+
* The annual-average period (M13) is dropped, because mixing it with the
|
|
41
|
+
* twelve monthly points would put two values on the same year.
|
|
42
|
+
*
|
|
43
|
+
* @param payload - the JSON body from the BLS timeseries endpoint
|
|
44
|
+
* @returns one observation per series per month
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseBlsObservations(payload: unknown): BlsObservation[];
|
|
47
|
+
/**
|
|
48
|
+
* Folds observations into one series with its high, low, and end points.
|
|
49
|
+
*
|
|
50
|
+
* @param observations - monthly observations, in any order
|
|
51
|
+
* @param seriesId - the series to keep; rows from other series are ignored
|
|
52
|
+
* @returns the ordered series with its headline points
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildBlsSeries(observations: BlsObservation[], seriesId: string): BlsSeries;
|
|
55
|
+
/**
|
|
56
|
+
* Reads a BLS series across a range of years, splitting the range into
|
|
57
|
+
* windows the API will accept.
|
|
58
|
+
*
|
|
59
|
+
* Keyless. The public API allows 25 series queries a day without a key and
|
|
60
|
+
* 500 with one, so a long range costs more than one call.
|
|
61
|
+
*
|
|
62
|
+
* @param seriesId - the BLS series id, such as LNS14000000
|
|
63
|
+
* @param options - the year range to read, and an optional fetch stub
|
|
64
|
+
* @returns every monthly observation in the range, oldest first
|
|
65
|
+
*/
|
|
66
|
+
export declare function fetchBlsSeries(seriesId: string, options: {
|
|
67
|
+
startYear: number;
|
|
68
|
+
endYear: number;
|
|
69
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
70
|
+
}): Promise<BlsSeries>;
|
|
71
|
+
/** National unemployment rate adapter, keyless. */
|
|
72
|
+
export declare const blsUnemploymentAdapter: UsDataAdapter<BlsSeries>;
|
|
73
|
+
//# sourceMappingURL=blsSeries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blsSeries.d.ts","sourceRoot":"","sources":["../src/blsSeries.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,gBAAgB,CAAC;AAEvD,uDAAuD;AACvD,eAAO,MAAM,4BAA4B,8CAA8C,CAAC;AAExF,4CAA4C;AAC5C,eAAO,MAAM,YAAY,qDAAqD,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAwB5C;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,EAAE,CAiCvE;AAkBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CA+B1F;AAiBD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAE,GACnF,OAAO,CAAC,SAAS,CAAC,CAepB;AAED,mDAAmD;AACnD,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,SAAS,CAmB3D,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UsSourceApiError, UsSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/**
|
|
5
|
+
* The national unemployment rate, seasonally adjusted. Published monthly by
|
|
6
|
+
* the Bureau of Labor Statistics, the agency that runs the Current Population
|
|
7
|
+
* Survey.
|
|
8
|
+
*/
|
|
9
|
+
export const US_UNEMPLOYMENT_SERIES_ID = 'LNS14000000';
|
|
10
|
+
/** Human-readable name for the unemployment series. */
|
|
11
|
+
export const US_UNEMPLOYMENT_SERIES_LABEL = 'US unemployment rate, seasonally adjusted';
|
|
12
|
+
/** Base URL for the BLS public data API. */
|
|
13
|
+
export const BLS_API_BASE = 'https://api.bls.gov/publicAPI/v2/timeseries/data';
|
|
14
|
+
/**
|
|
15
|
+
* The BLS public API refuses a request spanning more than ten years, so a
|
|
16
|
+
* longer range has to be assembled from several windows.
|
|
17
|
+
*/
|
|
18
|
+
export const BLS_MAX_YEARS_PER_REQUEST = 10;
|
|
19
|
+
const BLS_PERIOD_CODE = new RegExp('^M(0[1-9]|1[0-2])$');
|
|
20
|
+
const BLS_OBSERVATION_SCHEMA = z.object({
|
|
21
|
+
year: z.string(),
|
|
22
|
+
period: z.string(),
|
|
23
|
+
periodName: z.string(),
|
|
24
|
+
value: z.string(),
|
|
25
|
+
});
|
|
26
|
+
const BLS_SERIES_SCHEMA = z.object({
|
|
27
|
+
seriesID: z.string(),
|
|
28
|
+
data: z.array(BLS_OBSERVATION_SCHEMA),
|
|
29
|
+
});
|
|
30
|
+
const BLS_RESPONSE_SCHEMA = z.object({
|
|
31
|
+
status: z.string(),
|
|
32
|
+
message: z.array(z.string()).optional(),
|
|
33
|
+
Results: z.object({
|
|
34
|
+
series: z.array(BLS_SERIES_SCHEMA),
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Parses a BLS timeseries payload into flat observations.
|
|
39
|
+
*
|
|
40
|
+
* The annual-average period (M13) is dropped, because mixing it with the
|
|
41
|
+
* twelve monthly points would put two values on the same year.
|
|
42
|
+
*
|
|
43
|
+
* @param payload - the JSON body from the BLS timeseries endpoint
|
|
44
|
+
* @returns one observation per series per month
|
|
45
|
+
*/
|
|
46
|
+
export function parseBlsObservations(payload) {
|
|
47
|
+
const parsed = BLS_RESPONSE_SCHEMA.safeParse(payload);
|
|
48
|
+
if (!parsed.success) {
|
|
49
|
+
throw new UsSourceParseError('bls', parsed.error.message);
|
|
50
|
+
}
|
|
51
|
+
if (parsed.data.status !== 'REQUEST_SUCCEEDED') {
|
|
52
|
+
throw new UsSourceApiError('bls', `${parsed.data.status}: ${(parsed.data.message ?? []).join('; ')}`);
|
|
53
|
+
}
|
|
54
|
+
const observations = [];
|
|
55
|
+
for (const series of parsed.data.Results.series) {
|
|
56
|
+
for (const row of series.data) {
|
|
57
|
+
if (!BLS_PERIOD_CODE.test(row.period)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const year = Number(row.year);
|
|
61
|
+
const value = Number(row.value);
|
|
62
|
+
if (!Number.isInteger(year) || !Number.isFinite(value)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
observations.push({
|
|
66
|
+
seriesId: series.seriesID,
|
|
67
|
+
year,
|
|
68
|
+
period: row.period,
|
|
69
|
+
periodName: row.periodName,
|
|
70
|
+
value,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return sortObservations(observations);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Sorts an observation list oldest first. The agency answers newest first,
|
|
78
|
+
* and a caller reading `points[0]` should get the start of the series.
|
|
79
|
+
*
|
|
80
|
+
* @param observations - observations in any order
|
|
81
|
+
* @returns the same observations, oldest first
|
|
82
|
+
*/
|
|
83
|
+
function sortObservations(observations) {
|
|
84
|
+
return [...observations].sort((left, right) => {
|
|
85
|
+
if (left.year !== right.year) {
|
|
86
|
+
return left.year - right.year;
|
|
87
|
+
}
|
|
88
|
+
return left.period.localeCompare(right.period);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Folds observations into one series with its high, low, and end points.
|
|
93
|
+
*
|
|
94
|
+
* @param observations - monthly observations, in any order
|
|
95
|
+
* @param seriesId - the series to keep; rows from other series are ignored
|
|
96
|
+
* @returns the ordered series with its headline points
|
|
97
|
+
*/
|
|
98
|
+
export function buildBlsSeries(observations, seriesId) {
|
|
99
|
+
const points = sortObservations(observations.filter((observation) => observation.seriesId === seriesId));
|
|
100
|
+
const first = points[0];
|
|
101
|
+
const latest = points[points.length - 1];
|
|
102
|
+
if (first === undefined || latest === undefined) {
|
|
103
|
+
throw new UsSourceParseError('bls', `No observations for series ${seriesId}`);
|
|
104
|
+
}
|
|
105
|
+
let peak = first;
|
|
106
|
+
let lowest = first;
|
|
107
|
+
for (const point of points) {
|
|
108
|
+
if (point.value > peak.value) {
|
|
109
|
+
peak = point;
|
|
110
|
+
}
|
|
111
|
+
if (point.value < lowest.value) {
|
|
112
|
+
lowest = point;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
seriesId,
|
|
117
|
+
points,
|
|
118
|
+
first,
|
|
119
|
+
latest,
|
|
120
|
+
peak,
|
|
121
|
+
lowest,
|
|
122
|
+
changeFromPeak: latest.value - peak.value,
|
|
123
|
+
changeFromFirst: latest.value - first.value,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Reads one ten-year-or-shorter window from the BLS API. */
|
|
127
|
+
async function fetchBlsWindow(seriesId, startYear, endYear, fetchImpl) {
|
|
128
|
+
const url = `${BLS_API_BASE}/${seriesId}?startyear=${startYear}&endyear=${endYear}`;
|
|
129
|
+
const response = await fetchImpl(url);
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
throw new UsSourceApiError('bls', `HTTP ${response.status} reading series ${seriesId}`);
|
|
132
|
+
}
|
|
133
|
+
return parseBlsObservations(await response.json());
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Reads a BLS series across a range of years, splitting the range into
|
|
137
|
+
* windows the API will accept.
|
|
138
|
+
*
|
|
139
|
+
* Keyless. The public API allows 25 series queries a day without a key and
|
|
140
|
+
* 500 with one, so a long range costs more than one call.
|
|
141
|
+
*
|
|
142
|
+
* @param seriesId - the BLS series id, such as LNS14000000
|
|
143
|
+
* @param options - the year range to read, and an optional fetch stub
|
|
144
|
+
* @returns every monthly observation in the range, oldest first
|
|
145
|
+
*/
|
|
146
|
+
export async function fetchBlsSeries(seriesId, options) {
|
|
147
|
+
const { startYear, endYear } = options;
|
|
148
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
149
|
+
const observations = [];
|
|
150
|
+
for (let windowStart = startYear; windowStart <= endYear; windowStart += BLS_MAX_YEARS_PER_REQUEST) {
|
|
151
|
+
const windowEnd = Math.min(windowStart + BLS_MAX_YEARS_PER_REQUEST - 1, endYear);
|
|
152
|
+
observations.push(...(await fetchBlsWindow(seriesId, windowStart, windowEnd, fetchImpl)));
|
|
153
|
+
}
|
|
154
|
+
return buildBlsSeries(observations, seriesId);
|
|
155
|
+
}
|
|
156
|
+
/** National unemployment rate adapter, keyless. */
|
|
157
|
+
export const blsUnemploymentAdapter = {
|
|
158
|
+
id: 'bls-unemployment-rate',
|
|
159
|
+
name: US_UNEMPLOYMENT_SERIES_LABEL,
|
|
160
|
+
auth: 'none',
|
|
161
|
+
description: 'Monthly national unemployment rate from the Current Population Survey.',
|
|
162
|
+
fetchLive: async (options) => {
|
|
163
|
+
const endYear = new Date().getUTCFullYear();
|
|
164
|
+
return fetchBlsSeries(US_UNEMPLOYMENT_SERIES_ID, {
|
|
165
|
+
startYear: endYear - 19,
|
|
166
|
+
endYear,
|
|
167
|
+
...(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
parse: (payload) => buildBlsSeries(parseBlsObservations(payload), US_UNEMPLOYMENT_SERIES_ID),
|
|
171
|
+
loadFixture: () => buildBlsSeries(parseBlsObservations(readFixtureJson('bls-unemployment-rate.json')), US_UNEMPLOYMENT_SERIES_ID),
|
|
172
|
+
};
|
|
173
|
+
//# sourceMappingURL=blsSeries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blsSeries.js","sourceRoot":"","sources":["../src/blsSeries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAyBhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,aAAa,CAAC;AAEvD,uDAAuD;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,2CAA2C,CAAC;AAExF,4CAA4C;AAC5C,MAAM,CAAC,MAAM,YAAY,GAAG,kDAAkD,CAAC;AAE/E;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAE5C,MAAM,eAAe,GAAG,IAAI,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAEzD,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC;CACtC,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;KACnC,CAAC;CACH,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,mBAAmB,EAAE,CAAC;QAC/C,MAAM,IAAI,gBAAgB,CACxB,KAAK,EACL,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAqB,EAAE,CAAC;IAC1C,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtC,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvD,SAAS;YACX,CAAC;YACD,YAAY,CAAC,IAAI,CAAC;gBAChB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,IAAI;gBACJ,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,KAAK;aACN,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,gBAAgB,CAAC,YAAY,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,YAA8B;IACtD,OAAO,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5C,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QAChC,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,YAA8B,EAAE,QAAgB;IAC7E,MAAM,MAAM,GAAG,gBAAgB,CAC7B,YAAY,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,KAAK,QAAQ,CAAC,CACxE,CAAC;IACF,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAChD,MAAM,IAAI,kBAAkB,CAAC,KAAK,EAAE,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ;QACR,MAAM;QACN,KAAK;QACL,MAAM;QACN,IAAI;QACJ,MAAM;QACN,cAAc,EAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK;QACzC,eAAe,EAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;KAC5C,CAAC;AACJ,CAAC;AAED,6DAA6D;AAC7D,KAAK,UAAU,cAAc,CAC3B,QAAgB,EAChB,SAAiB,EACjB,OAAe,EACf,SAAkC;IAElC,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,QAAQ,cAAc,SAAS,YAAY,OAAO,EAAE,CAAC;IACpF,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,QAAQ,QAAQ,CAAC,MAAM,mBAAmB,QAAQ,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,oBAAoB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,OAAoF;IAEpF,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACxD,MAAM,YAAY,GAAqB,EAAE,CAAC;IAE1C,KACE,IAAI,WAAW,GAAG,SAAS,EAC3B,WAAW,IAAI,OAAO,EACtB,WAAW,IAAI,yBAAyB,EACxC,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,yBAAyB,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;QACjF,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,cAAc,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,EAAE,EAAE,uBAAuB;IAC3B,IAAI,EAAE,4BAA4B;IAClC,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,wEAAwE;IACrF,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,cAAc,EAAE,CAAC;QAC5C,OAAO,cAAc,CAAC,yBAAyB,EAAE;YAC/C,SAAS,EAAE,OAAO,GAAG,EAAE;YACvB,OAAO;YACP,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;SAC9E,CAAC,CAAC;IACL,CAAC;IACD,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,yBAAyB,CAAC;IAC5F,WAAW,EAAE,GAAG,EAAE,CAChB,cAAc,CACZ,oBAAoB,CAAC,eAAe,CAAC,4BAA4B,CAAC,CAAC,EACnE,yBAAyB,CAC1B;CACJ,CAAC"}
|