@usa-open-data-connectors/usa-sources 0.1.2 → 0.1.3
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/dist/btsTransportationStats.d.ts +87 -0
- package/dist/btsTransportationStats.d.ts.map +1 -0
- package/dist/btsTransportationStats.js +132 -0
- package/dist/btsTransportationStats.js.map +1 -0
- package/dist/cdcSocrataCatalogue.d.ts +87 -0
- package/dist/cdcSocrataCatalogue.d.ts.map +1 -0
- package/dist/cdcSocrataCatalogue.js +132 -0
- package/dist/cdcSocrataCatalogue.js.map +1 -0
- package/dist/clinicalTrialsStudies.d.ts +92 -0
- package/dist/clinicalTrialsStudies.d.ts.map +1 -0
- package/dist/clinicalTrialsStudies.js +149 -0
- package/dist/clinicalTrialsStudies.js.map +1 -0
- package/dist/epaEnvirofactsFacilities.d.ts +94 -0
- package/dist/epaEnvirofactsFacilities.d.ts.map +1 -0
- package/dist/epaEnvirofactsFacilities.js +142 -0
- package/dist/epaEnvirofactsFacilities.js.map +1 -0
- package/dist/fdicBankDirectory.d.ts +78 -0
- package/dist/fdicBankDirectory.d.ts.map +1 -0
- package/dist/fdicBankDirectory.js +124 -0
- package/dist/fdicBankDirectory.js.map +1 -0
- package/dist/healthdataSocrataCatalogue.d.ts +87 -0
- package/dist/healthdataSocrataCatalogue.d.ts.map +1 -0
- package/dist/healthdataSocrataCatalogue.js +132 -0
- package/dist/healthdataSocrataCatalogue.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/ncbiPubmedSearch.d.ts +71 -0
- package/dist/ncbiPubmedSearch.d.ts.map +1 -0
- package/dist/ncbiPubmedSearch.js +128 -0
- package/dist/ncbiPubmedSearch.js.map +1 -0
- package/dist/nwsPointForecast.d.ts +99 -0
- package/dist/nwsPointForecast.d.ts.map +1 -0
- package/dist/nwsPointForecast.js +146 -0
- package/dist/nwsPointForecast.js.map +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +22 -0
- package/dist/registry.js.map +1 -1
- package/dist/secEdgarFilings.d.ts +103 -0
- package/dist/secEdgarFilings.d.ts.map +1 -0
- package/dist/secEdgarFilings.js +214 -0
- package/dist/secEdgarFilings.js.map +1 -0
- package/dist/treasuryDebtToPenny.d.ts +90 -0
- package/dist/treasuryDebtToPenny.d.ts.map +1 -0
- package/dist/treasuryDebtToPenny.js +170 -0
- package/dist/treasuryDebtToPenny.js.map +1 -0
- package/dist/usaspendingAgencies.d.ts +78 -0
- package/dist/usaspendingAgencies.d.ts.map +1 -0
- package/dist/usaspendingAgencies.js +121 -0
- package/dist/usaspendingAgencies.js.map +1 -0
- package/package.json +10 -2
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { UsDataAdapter } from './types.js';
|
|
2
|
+
/** Adapter id for the bts-transportation-stats source. */
|
|
3
|
+
export declare const BTS_TRANSPORTATION_STATS_SOURCE_ID = "bts-transportation-stats";
|
|
4
|
+
/** One catalogue entry from the Socrata discovery API. */
|
|
5
|
+
export interface BtsCatalogueEntry {
|
|
6
|
+
/** Four-by-four Socrata dataset id, e.g. "ahfs-x44r". */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Human-readable title of the dataset, chart, story, or file. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** What the entry is: "dataset", "chart", "story", "file", or "href". */
|
|
11
|
+
assetType: string;
|
|
12
|
+
/** Publishing office the entry belongs to, or null when the catalogue lists none. */
|
|
13
|
+
category: string | null;
|
|
14
|
+
/** How the entry is rendered, e.g. "tabular", "href", or "story". */
|
|
15
|
+
viewType: string;
|
|
16
|
+
/** When the entry was created, in seconds since the Unix epoch. */
|
|
17
|
+
createdAtEpochSeconds: number;
|
|
18
|
+
/** When the entry was last modified, in seconds since the Unix epoch. */
|
|
19
|
+
viewLastModifiedEpochSeconds: number;
|
|
20
|
+
/** How many times the entry has been viewed. */
|
|
21
|
+
viewCount: number;
|
|
22
|
+
/** How many times the entry has been downloaded. */
|
|
23
|
+
downloadCount: number;
|
|
24
|
+
}
|
|
25
|
+
/** One category and how many catalogue entries carry it. */
|
|
26
|
+
export interface BtsCatalogueCategoryCount {
|
|
27
|
+
category: string;
|
|
28
|
+
entryCount: number;
|
|
29
|
+
}
|
|
30
|
+
/** One page of the catalogue, folded for a story. */
|
|
31
|
+
export interface BtsTransportationCatalogue {
|
|
32
|
+
/** One entry per catalogue item in the page. */
|
|
33
|
+
entries: BtsCatalogueEntry[];
|
|
34
|
+
entryCount: number;
|
|
35
|
+
/** Entries whose asset type is "dataset". */
|
|
36
|
+
datasetCount: number;
|
|
37
|
+
/** Entry with the latest creation time. Ties go to the earlier list entry. */
|
|
38
|
+
newestEntry: BtsCatalogueEntry;
|
|
39
|
+
/**
|
|
40
|
+
* Categories in the page, most entries first, ties broken by name. An entry
|
|
41
|
+
* the catalogue lists without a category is counted under "Uncategorised".
|
|
42
|
+
*/
|
|
43
|
+
categories: BtsCatalogueCategoryCount[];
|
|
44
|
+
}
|
|
45
|
+
/** Base URL for the Bureau of Transportation Statistics Socrata catalogue. */
|
|
46
|
+
export declare const BTS_TRANSPORTATION_STATS_API_BASE = "https://data.bts.gov/api/views.json";
|
|
47
|
+
/** How many catalogue entries one request reads. */
|
|
48
|
+
export declare const BTS_TRANSPORTATION_STATS_LIMIT = 10;
|
|
49
|
+
/**
|
|
50
|
+
* Builds the request URL for one page of the catalogue.
|
|
51
|
+
*
|
|
52
|
+
* The endpoint is keyless. The service answers the whole catalogue by default,
|
|
53
|
+
* so the adapter asks for a short page.
|
|
54
|
+
*
|
|
55
|
+
* @param options - an optional entry cap
|
|
56
|
+
* @returns the full request URL
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildBtsTransportationStatsUrl(options?: {
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): string;
|
|
61
|
+
/**
|
|
62
|
+
* Parses one page of the catalogue into the entries and the category counts.
|
|
63
|
+
*
|
|
64
|
+
* An entry without an id, a name, or a creation time is a changed shape and
|
|
65
|
+
* stops the parse. An empty page also stops the parse, because a catalogue
|
|
66
|
+
* with no entries is not a usable answer.
|
|
67
|
+
*
|
|
68
|
+
* @param payload - the JSON body from the Socrata discovery API
|
|
69
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
70
|
+
*/
|
|
71
|
+
export declare function parseBtsTransportationStatsPayload(payload: unknown): BtsTransportationCatalogue;
|
|
72
|
+
/**
|
|
73
|
+
* Reads one page of the Bureau of Transportation Statistics catalogue.
|
|
74
|
+
*
|
|
75
|
+
* One keyless request covers the page and reports what the catalogue holds,
|
|
76
|
+
* without reading any dataset's rows.
|
|
77
|
+
*
|
|
78
|
+
* @param options - an optional entry cap and fetch stub
|
|
79
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
80
|
+
*/
|
|
81
|
+
export declare function fetchBtsTransportationStats(options?: {
|
|
82
|
+
limit?: number;
|
|
83
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
84
|
+
}): Promise<BtsTransportationCatalogue>;
|
|
85
|
+
/** Bureau of Transportation Statistics catalogue of datasets, charts, and stories, keyless. */
|
|
86
|
+
export declare const btsTransportationStatsAdapter: UsDataAdapter<BtsTransportationCatalogue>;
|
|
87
|
+
//# sourceMappingURL=btsTransportationStats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btsTransportationStats.d.ts","sourceRoot":"","sources":["../src/btsTransportationStats.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,0DAA0D;AAC1D,eAAO,MAAM,kCAAkC,6BAA6B,CAAC;AAE7E,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,yEAAyE;IACzE,4BAA4B,EAAE,MAAM,CAAC;IACrC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,4DAA4D;AAC5D,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qDAAqD;AACrD,MAAM,WAAW,0BAA0B;IACzC,gDAAgD;IAChD,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,iBAAiB,CAAC;IAC/B;;;OAGG;IACH,UAAU,EAAE,yBAAyB,EAAE,CAAC;CACzC;AAED,8EAA8E;AAC9E,eAAO,MAAM,iCAAiC,wCAAwC,CAAC;AAEvF,oDAAoD;AACpD,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAkDjD;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAKnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,OAAO,GAAG,0BAA0B,CAuC/F;AAED;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAAC,OAAO,CAAC,EAAE;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAatC;AAED,+FAA+F;AAC/F,eAAO,MAAM,6BAA6B,EAAE,aAAa,CAAC,0BAA0B,CAanF,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UsSourceApiError, UsSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/** Adapter id for the bts-transportation-stats source. */
|
|
5
|
+
export const BTS_TRANSPORTATION_STATS_SOURCE_ID = 'bts-transportation-stats';
|
|
6
|
+
/** Base URL for the Bureau of Transportation Statistics Socrata catalogue. */
|
|
7
|
+
export const BTS_TRANSPORTATION_STATS_API_BASE = 'https://data.bts.gov/api/views.json';
|
|
8
|
+
/** How many catalogue entries one request reads. */
|
|
9
|
+
export const BTS_TRANSPORTATION_STATS_LIMIT = 10;
|
|
10
|
+
/** Committed snapshot, so a build works when the Bureau of Transportation Statistics host is unreachable. */
|
|
11
|
+
const BTS_TRANSPORTATION_STATS_FIXTURE_FILENAME = 'bts-transportation-stats-2026-10-05.json';
|
|
12
|
+
/** Category bucket the adapter uses for an entry the catalogue lists without one. */
|
|
13
|
+
const BTS_TRANSPORTATION_UNCATEGORISED = 'Uncategorised';
|
|
14
|
+
// The discovery API sends a bare JSON array. A value the catalogue does not
|
|
15
|
+
// carry arrives as null, and every count arrives as a number.
|
|
16
|
+
const BTS_TRANSPORTATION_STATS_ENTRY_SCHEMA = z.object({
|
|
17
|
+
id: z.string().optional(),
|
|
18
|
+
name: z.string().optional(),
|
|
19
|
+
assetType: z.string().optional(),
|
|
20
|
+
category: z.string().nullable().optional(),
|
|
21
|
+
viewType: z.string().optional(),
|
|
22
|
+
createdAt: z.number().optional(),
|
|
23
|
+
viewLastModified: z.number().optional(),
|
|
24
|
+
viewCount: z.number().optional(),
|
|
25
|
+
downloadCount: z.number().optional(),
|
|
26
|
+
});
|
|
27
|
+
const BTS_TRANSPORTATION_STATS_PAYLOAD_SCHEMA = z.array(BTS_TRANSPORTATION_STATS_ENTRY_SCHEMA);
|
|
28
|
+
/** Counts entries per category, most entries first, ties by name. */
|
|
29
|
+
function countBtsCatalogueCategories(entries) {
|
|
30
|
+
const counts = new Map();
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const category = entry.category ?? BTS_TRANSPORTATION_UNCATEGORISED;
|
|
33
|
+
counts.set(category, (counts.get(category) ?? 0) + 1);
|
|
34
|
+
}
|
|
35
|
+
return [...counts.entries()]
|
|
36
|
+
.sort(([leftCategory, leftCount], [rightCategory, rightCount]) => leftCount === rightCount ? leftCategory.localeCompare(rightCategory) : rightCount - leftCount)
|
|
37
|
+
.map(([category, entryCount]) => ({ category, entryCount }));
|
|
38
|
+
}
|
|
39
|
+
/** Picks the entry with the latest creation time, ties to the earlier entry. */
|
|
40
|
+
function newestBtsCatalogueEntry(entries) {
|
|
41
|
+
const first = entries[0];
|
|
42
|
+
if (first === undefined) {
|
|
43
|
+
throw new UsSourceParseError(BTS_TRANSPORTATION_STATS_SOURCE_ID, 'No entries to summarise');
|
|
44
|
+
}
|
|
45
|
+
return entries.reduce((best, entry) => (entry.createdAtEpochSeconds > best.createdAtEpochSeconds ? entry : best), first);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds the request URL for one page of the catalogue.
|
|
49
|
+
*
|
|
50
|
+
* The endpoint is keyless. The service answers the whole catalogue by default,
|
|
51
|
+
* so the adapter asks for a short page.
|
|
52
|
+
*
|
|
53
|
+
* @param options - an optional entry cap
|
|
54
|
+
* @returns the full request URL
|
|
55
|
+
*/
|
|
56
|
+
export function buildBtsTransportationStatsUrl(options) {
|
|
57
|
+
const params = new URLSearchParams({
|
|
58
|
+
limit: String(options?.limit ?? BTS_TRANSPORTATION_STATS_LIMIT),
|
|
59
|
+
});
|
|
60
|
+
return `https://data.bts.gov/api/views.json?${params.toString()}`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parses one page of the catalogue into the entries and the category counts.
|
|
64
|
+
*
|
|
65
|
+
* An entry without an id, a name, or a creation time is a changed shape and
|
|
66
|
+
* stops the parse. An empty page also stops the parse, because a catalogue
|
|
67
|
+
* with no entries is not a usable answer.
|
|
68
|
+
*
|
|
69
|
+
* @param payload - the JSON body from the Socrata discovery API
|
|
70
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
71
|
+
*/
|
|
72
|
+
export function parseBtsTransportationStatsPayload(payload) {
|
|
73
|
+
const parsed = BTS_TRANSPORTATION_STATS_PAYLOAD_SCHEMA.safeParse(payload);
|
|
74
|
+
if (!parsed.success) {
|
|
75
|
+
throw new UsSourceParseError(BTS_TRANSPORTATION_STATS_SOURCE_ID, parsed.error.message);
|
|
76
|
+
}
|
|
77
|
+
if (parsed.data.length === 0) {
|
|
78
|
+
throw new UsSourceParseError(BTS_TRANSPORTATION_STATS_SOURCE_ID, 'The response carried no entries');
|
|
79
|
+
}
|
|
80
|
+
const entries = parsed.data.map((row) => {
|
|
81
|
+
if (row.id === undefined || row.name === undefined || row.createdAt === undefined) {
|
|
82
|
+
throw new UsSourceParseError(BTS_TRANSPORTATION_STATS_SOURCE_ID, 'A catalogue entry arrived without its id, name, or creation time');
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
id: row.id,
|
|
86
|
+
name: row.name,
|
|
87
|
+
assetType: row.assetType ?? '',
|
|
88
|
+
category: row.category ?? null,
|
|
89
|
+
viewType: row.viewType ?? '',
|
|
90
|
+
createdAtEpochSeconds: row.createdAt,
|
|
91
|
+
viewLastModifiedEpochSeconds: row.viewLastModified ?? row.createdAt,
|
|
92
|
+
viewCount: row.viewCount ?? 0,
|
|
93
|
+
downloadCount: row.downloadCount ?? 0,
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
entries,
|
|
98
|
+
entryCount: entries.length,
|
|
99
|
+
datasetCount: entries.filter((entry) => entry.assetType === 'dataset').length,
|
|
100
|
+
newestEntry: newestBtsCatalogueEntry(entries),
|
|
101
|
+
categories: countBtsCatalogueCategories(entries),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reads one page of the Bureau of Transportation Statistics catalogue.
|
|
106
|
+
*
|
|
107
|
+
* One keyless request covers the page and reports what the catalogue holds,
|
|
108
|
+
* without reading any dataset's rows.
|
|
109
|
+
*
|
|
110
|
+
* @param options - an optional entry cap and fetch stub
|
|
111
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
112
|
+
*/
|
|
113
|
+
export async function fetchBtsTransportationStats(options) {
|
|
114
|
+
const fetchImpl = options?.fetchImpl ?? globalThis.fetch;
|
|
115
|
+
const url = buildBtsTransportationStatsUrl(options?.limit === undefined ? {} : { limit: options.limit });
|
|
116
|
+
const response = await fetchImpl(url);
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new UsSourceApiError(BTS_TRANSPORTATION_STATS_SOURCE_ID, `HTTP ${response.status} reading the catalogue`);
|
|
119
|
+
}
|
|
120
|
+
return parseBtsTransportationStatsPayload(await response.json());
|
|
121
|
+
}
|
|
122
|
+
/** Bureau of Transportation Statistics catalogue of datasets, charts, and stories, keyless. */
|
|
123
|
+
export const btsTransportationStatsAdapter = {
|
|
124
|
+
id: BTS_TRANSPORTATION_STATS_SOURCE_ID,
|
|
125
|
+
name: 'BTS transportation statistics',
|
|
126
|
+
auth: 'none',
|
|
127
|
+
description: 'The Bureau of Transportation Statistics Socrata catalogue, one row per published dataset, chart, or story, with its category, view count, and download count.',
|
|
128
|
+
fetchLive: async (options) => fetchBtsTransportationStats(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
129
|
+
parse: (payload) => parseBtsTransportationStatsPayload(payload),
|
|
130
|
+
loadFixture: () => parseBtsTransportationStatsPayload(readFixtureJson(BTS_TRANSPORTATION_STATS_FIXTURE_FILENAME)),
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=btsTransportationStats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btsTransportationStats.js","sourceRoot":"","sources":["../src/btsTransportationStats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { UsDataAdapter } from './types.js';
|
|
2
|
+
/** Adapter id for the cdc-socrata-catalogue source. */
|
|
3
|
+
export declare const CDC_SOCRATA_CATALOGUE_SOURCE_ID = "cdc-socrata-catalogue";
|
|
4
|
+
/** One catalogue entry from the Socrata discovery API. */
|
|
5
|
+
export interface CdcSocrataCatalogueEntry {
|
|
6
|
+
/** Four-by-four Socrata dataset id, e.g. "ahfs-x44r". */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Human-readable title of the dataset, chart, story, or file. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** What the entry is: "dataset", "chart", "story", "file", or "href". */
|
|
11
|
+
assetType: string;
|
|
12
|
+
/** Publishing office the entry belongs to, or null when the catalogue lists none. */
|
|
13
|
+
category: string | null;
|
|
14
|
+
/** How the entry is rendered, e.g. "tabular", "href", or "story". */
|
|
15
|
+
viewType: string;
|
|
16
|
+
/** When the entry was created, in seconds since the Unix epoch. */
|
|
17
|
+
createdAtEpochSeconds: number;
|
|
18
|
+
/** When the entry was last modified, in seconds since the Unix epoch. */
|
|
19
|
+
viewLastModifiedEpochSeconds: number;
|
|
20
|
+
/** How many times the entry has been viewed. */
|
|
21
|
+
viewCount: number;
|
|
22
|
+
/** How many times the entry has been downloaded. */
|
|
23
|
+
downloadCount: number;
|
|
24
|
+
}
|
|
25
|
+
/** One category and how many catalogue entries carry it. */
|
|
26
|
+
export interface CdcSocrataCategoryCount {
|
|
27
|
+
category: string;
|
|
28
|
+
entryCount: number;
|
|
29
|
+
}
|
|
30
|
+
/** One page of the catalogue, folded for a story. */
|
|
31
|
+
export interface CdcSocrataCatalogue {
|
|
32
|
+
/** One entry per catalogue item in the page. */
|
|
33
|
+
entries: CdcSocrataCatalogueEntry[];
|
|
34
|
+
entryCount: number;
|
|
35
|
+
/** Entries whose asset type is "dataset". */
|
|
36
|
+
datasetCount: number;
|
|
37
|
+
/** Entry with the latest creation time. Ties go to the earlier list entry. */
|
|
38
|
+
newestEntry: CdcSocrataCatalogueEntry;
|
|
39
|
+
/**
|
|
40
|
+
* Categories in the page, most entries first, ties broken by name. An entry
|
|
41
|
+
* the catalogue lists without a category is counted under "Uncategorised".
|
|
42
|
+
*/
|
|
43
|
+
categories: CdcSocrataCategoryCount[];
|
|
44
|
+
}
|
|
45
|
+
/** Base URL for the CDC Socrata catalogue. */
|
|
46
|
+
export declare const CDC_SOCRATA_CATALOGUE_API_BASE = "https://data.cdc.gov/api/views.json";
|
|
47
|
+
/** How many catalogue entries one request reads. */
|
|
48
|
+
export declare const CDC_SOCRATA_CATALOGUE_LIMIT = 10;
|
|
49
|
+
/**
|
|
50
|
+
* Builds the request URL for one page of the catalogue.
|
|
51
|
+
*
|
|
52
|
+
* The endpoint is keyless. The service answers the whole catalogue by default,
|
|
53
|
+
* so the adapter asks for a short page.
|
|
54
|
+
*
|
|
55
|
+
* @param options - an optional entry cap
|
|
56
|
+
* @returns the full request URL
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildCdcSocrataCatalogueUrl(options?: {
|
|
59
|
+
limit?: number;
|
|
60
|
+
}): string;
|
|
61
|
+
/**
|
|
62
|
+
* Parses one page of the catalogue into the entries and the category counts.
|
|
63
|
+
*
|
|
64
|
+
* An entry without an id, a name, or a creation time is a changed shape and
|
|
65
|
+
* stops the parse. An empty page also stops the parse, because a catalogue
|
|
66
|
+
* with no entries is not a usable answer.
|
|
67
|
+
*
|
|
68
|
+
* @param payload - the JSON body from the Socrata discovery API
|
|
69
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
70
|
+
*/
|
|
71
|
+
export declare function parseCdcSocrataCataloguePayload(payload: unknown): CdcSocrataCatalogue;
|
|
72
|
+
/**
|
|
73
|
+
* Reads one page of the CDC catalogue.
|
|
74
|
+
*
|
|
75
|
+
* One keyless request covers the page and reports what the catalogue holds,
|
|
76
|
+
* without reading any dataset's rows.
|
|
77
|
+
*
|
|
78
|
+
* @param options - an optional entry cap and fetch stub
|
|
79
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
80
|
+
*/
|
|
81
|
+
export declare function fetchCdcSocrataCatalogue(options?: {
|
|
82
|
+
limit?: number;
|
|
83
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
84
|
+
}): Promise<CdcSocrataCatalogue>;
|
|
85
|
+
/** CDC Socrata catalogue of datasets, charts, and stories, keyless. */
|
|
86
|
+
export declare const cdcSocrataCatalogueAdapter: UsDataAdapter<CdcSocrataCatalogue>;
|
|
87
|
+
//# sourceMappingURL=cdcSocrataCatalogue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdcSocrataCatalogue.d.ts","sourceRoot":"","sources":["../src/cdcSocrataCatalogue.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,uDAAuD;AACvD,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE,0DAA0D;AAC1D,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,yEAAyE;IACzE,4BAA4B,EAAE,MAAM,CAAC;IACrC,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,4DAA4D;AAC5D,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qDAAqD;AACrD,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,wBAAwB,CAAC;IACtC;;;OAGG;IACH,UAAU,EAAE,uBAAuB,EAAE,CAAC;CACvC;AAED,8CAA8C;AAC9C,eAAO,MAAM,8BAA8B,wCAAwC,CAAC;AAEpF,oDAAoD;AACpD,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAkD9C;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAKhF;AAED;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAuCrF;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,CAAC,EAAE;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAa/B;AAED,uEAAuE;AACvE,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,mBAAmB,CAazE,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UsSourceApiError, UsSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/** Adapter id for the cdc-socrata-catalogue source. */
|
|
5
|
+
export const CDC_SOCRATA_CATALOGUE_SOURCE_ID = 'cdc-socrata-catalogue';
|
|
6
|
+
/** Base URL for the CDC Socrata catalogue. */
|
|
7
|
+
export const CDC_SOCRATA_CATALOGUE_API_BASE = 'https://data.cdc.gov/api/views.json';
|
|
8
|
+
/** How many catalogue entries one request reads. */
|
|
9
|
+
export const CDC_SOCRATA_CATALOGUE_LIMIT = 10;
|
|
10
|
+
/** Committed snapshot, so a build works when the CDC host is unreachable. */
|
|
11
|
+
const CDC_SOCRATA_CATALOGUE_FIXTURE_FILENAME = 'cdc-socrata-catalogue-2026-10-05.json';
|
|
12
|
+
/** Category bucket the adapter uses for an entry the catalogue lists without one. */
|
|
13
|
+
const CDC_SOCRATA_UNCATEGORISED = 'Uncategorised';
|
|
14
|
+
// The discovery API sends a bare JSON array. A value the catalogue does not
|
|
15
|
+
// carry arrives as null, and every count arrives as a number.
|
|
16
|
+
const CDC_SOCRATA_CATALOGUE_ENTRY_SCHEMA = z.object({
|
|
17
|
+
id: z.string().optional(),
|
|
18
|
+
name: z.string().optional(),
|
|
19
|
+
assetType: z.string().optional(),
|
|
20
|
+
category: z.string().nullable().optional(),
|
|
21
|
+
viewType: z.string().optional(),
|
|
22
|
+
createdAt: z.number().optional(),
|
|
23
|
+
viewLastModified: z.number().optional(),
|
|
24
|
+
viewCount: z.number().optional(),
|
|
25
|
+
downloadCount: z.number().optional(),
|
|
26
|
+
});
|
|
27
|
+
const CDC_SOCRATA_CATALOGUE_PAYLOAD_SCHEMA = z.array(CDC_SOCRATA_CATALOGUE_ENTRY_SCHEMA);
|
|
28
|
+
/** Counts entries per category, most entries first, ties by name. */
|
|
29
|
+
function countCdcSocrataCategories(entries) {
|
|
30
|
+
const counts = new Map();
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const category = entry.category ?? CDC_SOCRATA_UNCATEGORISED;
|
|
33
|
+
counts.set(category, (counts.get(category) ?? 0) + 1);
|
|
34
|
+
}
|
|
35
|
+
return [...counts.entries()]
|
|
36
|
+
.sort(([leftCategory, leftCount], [rightCategory, rightCount]) => leftCount === rightCount ? leftCategory.localeCompare(rightCategory) : rightCount - leftCount)
|
|
37
|
+
.map(([category, entryCount]) => ({ category, entryCount }));
|
|
38
|
+
}
|
|
39
|
+
/** Picks the entry with the latest creation time, ties to the earlier entry. */
|
|
40
|
+
function newestCdcSocrataEntry(entries) {
|
|
41
|
+
const first = entries[0];
|
|
42
|
+
if (first === undefined) {
|
|
43
|
+
throw new UsSourceParseError(CDC_SOCRATA_CATALOGUE_SOURCE_ID, 'No entries to summarise');
|
|
44
|
+
}
|
|
45
|
+
return entries.reduce((best, entry) => (entry.createdAtEpochSeconds > best.createdAtEpochSeconds ? entry : best), first);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds the request URL for one page of the catalogue.
|
|
49
|
+
*
|
|
50
|
+
* The endpoint is keyless. The service answers the whole catalogue by default,
|
|
51
|
+
* so the adapter asks for a short page.
|
|
52
|
+
*
|
|
53
|
+
* @param options - an optional entry cap
|
|
54
|
+
* @returns the full request URL
|
|
55
|
+
*/
|
|
56
|
+
export function buildCdcSocrataCatalogueUrl(options) {
|
|
57
|
+
const params = new URLSearchParams({
|
|
58
|
+
limit: String(options?.limit ?? CDC_SOCRATA_CATALOGUE_LIMIT),
|
|
59
|
+
});
|
|
60
|
+
return `https://data.cdc.gov/api/views.json?${params.toString()}`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parses one page of the catalogue into the entries and the category counts.
|
|
64
|
+
*
|
|
65
|
+
* An entry without an id, a name, or a creation time is a changed shape and
|
|
66
|
+
* stops the parse. An empty page also stops the parse, because a catalogue
|
|
67
|
+
* with no entries is not a usable answer.
|
|
68
|
+
*
|
|
69
|
+
* @param payload - the JSON body from the Socrata discovery API
|
|
70
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
71
|
+
*/
|
|
72
|
+
export function parseCdcSocrataCataloguePayload(payload) {
|
|
73
|
+
const parsed = CDC_SOCRATA_CATALOGUE_PAYLOAD_SCHEMA.safeParse(payload);
|
|
74
|
+
if (!parsed.success) {
|
|
75
|
+
throw new UsSourceParseError(CDC_SOCRATA_CATALOGUE_SOURCE_ID, parsed.error.message);
|
|
76
|
+
}
|
|
77
|
+
if (parsed.data.length === 0) {
|
|
78
|
+
throw new UsSourceParseError(CDC_SOCRATA_CATALOGUE_SOURCE_ID, 'The response carried no entries');
|
|
79
|
+
}
|
|
80
|
+
const entries = parsed.data.map((row) => {
|
|
81
|
+
if (row.id === undefined || row.name === undefined || row.createdAt === undefined) {
|
|
82
|
+
throw new UsSourceParseError(CDC_SOCRATA_CATALOGUE_SOURCE_ID, 'A catalogue entry arrived without its id, name, or creation time');
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
id: row.id,
|
|
86
|
+
name: row.name,
|
|
87
|
+
assetType: row.assetType ?? '',
|
|
88
|
+
category: row.category ?? null,
|
|
89
|
+
viewType: row.viewType ?? '',
|
|
90
|
+
createdAtEpochSeconds: row.createdAt,
|
|
91
|
+
viewLastModifiedEpochSeconds: row.viewLastModified ?? row.createdAt,
|
|
92
|
+
viewCount: row.viewCount ?? 0,
|
|
93
|
+
downloadCount: row.downloadCount ?? 0,
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
entries,
|
|
98
|
+
entryCount: entries.length,
|
|
99
|
+
datasetCount: entries.filter((entry) => entry.assetType === 'dataset').length,
|
|
100
|
+
newestEntry: newestCdcSocrataEntry(entries),
|
|
101
|
+
categories: countCdcSocrataCategories(entries),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reads one page of the CDC catalogue.
|
|
106
|
+
*
|
|
107
|
+
* One keyless request covers the page and reports what the catalogue holds,
|
|
108
|
+
* without reading any dataset's rows.
|
|
109
|
+
*
|
|
110
|
+
* @param options - an optional entry cap and fetch stub
|
|
111
|
+
* @returns the entries in the page, the counts, and the newest entry
|
|
112
|
+
*/
|
|
113
|
+
export async function fetchCdcSocrataCatalogue(options) {
|
|
114
|
+
const fetchImpl = options?.fetchImpl ?? globalThis.fetch;
|
|
115
|
+
const url = buildCdcSocrataCatalogueUrl(options?.limit === undefined ? {} : { limit: options.limit });
|
|
116
|
+
const response = await fetchImpl(url);
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new UsSourceApiError(CDC_SOCRATA_CATALOGUE_SOURCE_ID, `HTTP ${response.status} reading the catalogue`);
|
|
119
|
+
}
|
|
120
|
+
return parseCdcSocrataCataloguePayload(await response.json());
|
|
121
|
+
}
|
|
122
|
+
/** CDC Socrata catalogue of datasets, charts, and stories, keyless. */
|
|
123
|
+
export const cdcSocrataCatalogueAdapter = {
|
|
124
|
+
id: CDC_SOCRATA_CATALOGUE_SOURCE_ID,
|
|
125
|
+
name: 'CDC Socrata catalogue',
|
|
126
|
+
auth: 'none',
|
|
127
|
+
description: "The CDC's Socrata catalogue, one row per published dataset, chart, or story, with its category, view count, and download count.",
|
|
128
|
+
fetchLive: async (options) => fetchCdcSocrataCatalogue(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
129
|
+
parse: (payload) => parseCdcSocrataCataloguePayload(payload),
|
|
130
|
+
loadFixture: () => parseCdcSocrataCataloguePayload(readFixtureJson(CDC_SOCRATA_CATALOGUE_FIXTURE_FILENAME)),
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=cdcSocrataCatalogue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdcSocrataCatalogue.js","sourceRoot":"","sources":["../src/cdcSocrataCatalogue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { UsDataAdapter } from './types.js';
|
|
2
|
+
/** Adapter id for the ClinicalTrials.gov studies source. */
|
|
3
|
+
export declare const CLINICALTRIALS_STUDIES_SOURCE_ID = "clinicaltrials-studies";
|
|
4
|
+
/** One registered clinical study, trimmed to the fields a story reads. */
|
|
5
|
+
export interface ClinicalTrialStudy {
|
|
6
|
+
/** The registry's own study id, e.g. "NCT07065435". */
|
|
7
|
+
nctId: string;
|
|
8
|
+
/** Short title the sponsor registered. */
|
|
9
|
+
briefTitle: string;
|
|
10
|
+
/** Recruitment status, e.g. "RECRUITING" or "COMPLETED". */
|
|
11
|
+
overallStatus: string;
|
|
12
|
+
/** Study type, e.g. "INTERVENTIONAL" or "OBSERVATIONAL", or null when absent. */
|
|
13
|
+
studyType: string | null;
|
|
14
|
+
/** Trial phases, e.g. ["PHASE2"]. Empty for observational studies. */
|
|
15
|
+
phases: string[];
|
|
16
|
+
/** Conditions the study covers. */
|
|
17
|
+
conditions: string[];
|
|
18
|
+
/** Name of the lead sponsor, or null when the study names none. */
|
|
19
|
+
leadSponsor: string | null;
|
|
20
|
+
/** Number of participants the study plans or enrolled, or null when absent. */
|
|
21
|
+
enrollmentCount: number | null;
|
|
22
|
+
/** Whether the enrollment count is expected or actual, or null when absent. */
|
|
23
|
+
enrollmentType: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Date the study started. ClinicalTrials.gov writes a day-precision date
|
|
26
|
+
* when it has one and a month-precision date such as "2000-10" when it does
|
|
27
|
+
* not, so the adapter keeps the string as the registry wrote it.
|
|
28
|
+
*/
|
|
29
|
+
startDate: string | null;
|
|
30
|
+
/** Date of the study's latest update post, or null when absent. */
|
|
31
|
+
lastUpdatePostDate: string | null;
|
|
32
|
+
}
|
|
33
|
+
/** One recruitment status and how many studies in the page carry it. */
|
|
34
|
+
export interface ClinicalTrialStatusCount {
|
|
35
|
+
status: string;
|
|
36
|
+
studyCount: number;
|
|
37
|
+
}
|
|
38
|
+
/** One page of studies, folded for a story. */
|
|
39
|
+
export interface ClinicalTrialStudyPage {
|
|
40
|
+
/** One entry per study in the page. */
|
|
41
|
+
studies: ClinicalTrialStudy[];
|
|
42
|
+
studyCount: number;
|
|
43
|
+
/** Recruitment statuses in the page, most studies first, ties by status name. */
|
|
44
|
+
statusCounts: ClinicalTrialStatusCount[];
|
|
45
|
+
/** Token for the next page of the same query, or null on the last page. */
|
|
46
|
+
nextPageToken: string | null;
|
|
47
|
+
}
|
|
48
|
+
/** Base URL for the ClinicalTrials.gov studies API. */
|
|
49
|
+
export declare const CLINICALTRIALS_STUDIES_API_BASE = "https://clinicaltrials.gov/api/v2/studies";
|
|
50
|
+
/** Page size for one request. */
|
|
51
|
+
export declare const CLINICALTRIALS_STUDIES_PAGE_SIZE = 10;
|
|
52
|
+
/**
|
|
53
|
+
* Builds the request URL for one page of studies.
|
|
54
|
+
*
|
|
55
|
+
* The endpoint is keyless. The default page size is small on purpose: one
|
|
56
|
+
* study carries a deep protocol record, so a caller reading many pages should
|
|
57
|
+
* pass the page token from the previous response.
|
|
58
|
+
*
|
|
59
|
+
* @param options - an optional page size
|
|
60
|
+
* @returns the full request URL
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildClinicalTrialsStudiesUrl(options?: {
|
|
63
|
+
pageSize?: number;
|
|
64
|
+
}): string;
|
|
65
|
+
/**
|
|
66
|
+
* Parses one page of studies into the fields a story reads.
|
|
67
|
+
*
|
|
68
|
+
* A study without a registry id or a brief title is a changed shape and stops
|
|
69
|
+
* the parse. Optional protocol blocks are read when present and left null
|
|
70
|
+
* when the registry did not carry them.
|
|
71
|
+
*
|
|
72
|
+
* @param payload - the JSON body from the ClinicalTrials.gov studies API
|
|
73
|
+
* @returns the studies in the page, the status counts, and the next page token
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseClinicalTrialsStudiesPayload(payload: unknown): ClinicalTrialStudyPage;
|
|
76
|
+
/**
|
|
77
|
+
* Reads one page of registered clinical studies.
|
|
78
|
+
*
|
|
79
|
+
* One keyless request covers the page. The response also carries a token for
|
|
80
|
+
* the next page of the same query, which the adapter keeps on the page it
|
|
81
|
+
* returns.
|
|
82
|
+
*
|
|
83
|
+
* @param options - an optional page size and fetch stub
|
|
84
|
+
* @returns the studies in the page, the status counts, and the next page token
|
|
85
|
+
*/
|
|
86
|
+
export declare function fetchClinicalTrialsStudies(options?: {
|
|
87
|
+
pageSize?: number;
|
|
88
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
89
|
+
}): Promise<ClinicalTrialStudyPage>;
|
|
90
|
+
/** ClinicalTrials.gov registered studies, one row per study, keyless. */
|
|
91
|
+
export declare const clinicalTrialsStudiesAdapter: UsDataAdapter<ClinicalTrialStudyPage>;
|
|
92
|
+
//# sourceMappingURL=clinicalTrialsStudies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clinicalTrialsStudies.d.ts","sourceRoot":"","sources":["../src/clinicalTrialsStudies.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,4DAA4D;AAC5D,eAAO,MAAM,gCAAgC,2BAA2B,CAAC;AAEzE,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,sEAAsE;IACtE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mCAAmC;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,+EAA+E;IAC/E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,+EAA+E;IAC/E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,wEAAwE;AACxE,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,+CAA+C;AAC/C,MAAM,WAAW,sBAAsB;IACrC,uCAAuC;IACvC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,YAAY,EAAE,wBAAwB,EAAE,CAAC;IACzC,2EAA2E;IAC3E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,uDAAuD;AACvD,eAAO,MAAM,+BAA+B,8CAA8C,CAAC;AAE3F,iCAAiC;AACjC,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAwCnD;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAKrF;AAeD;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,OAAO,GAAG,sBAAsB,CA+C1F;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,CAAC,EAAE;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAalC;AAED,yEAAyE;AACzE,eAAO,MAAM,4BAA4B,EAAE,aAAa,CAAC,sBAAsB,CAa9E,CAAC"}
|