@uk-open-data-connectors/uk-sources 0.1.0 → 0.1.1
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 +21 -0
- package/dist/exploreEducationStatistics.d.ts +72 -0
- package/dist/exploreEducationStatistics.d.ts.map +1 -0
- package/dist/exploreEducationStatistics.js +110 -0
- package/dist/exploreEducationStatistics.js.map +1 -0
- package/dist/findATender.d.ts +76 -0
- package/dist/findATender.d.ts.map +1 -0
- package/dist/findATender.js +109 -0
- package/dist/findATender.js.map +1 -0
- package/dist/fingertipsIndicators.d.ts +67 -0
- package/dist/fingertipsIndicators.d.ts.map +1 -0
- package/dist/fingertipsIndicators.js +94 -0
- package/dist/fingertipsIndicators.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/londonDatastore.d.ts +46 -0
- package/dist/londonDatastore.d.ts.map +1 -0
- package/dist/londonDatastore.js +64 -0
- package/dist/londonDatastore.js.map +1 -0
- package/dist/nhsbsaCkan.d.ts +43 -0
- package/dist/nhsbsaCkan.d.ts.map +1 -0
- package/dist/nhsbsaCkan.js +61 -0
- package/dist/nhsbsaCkan.js.map +1 -0
- package/dist/nomis.d.ts +71 -0
- package/dist/nomis.d.ts.map +1 -0
- package/dist/nomis.js +126 -0
- package/dist/nomis.js.map +1 -0
- package/dist/postcodeLookup.d.ts +55 -0
- package/dist/postcodeLookup.d.ts.map +1 -0
- package/dist/postcodeLookup.js +89 -0
- package/dist/postcodeLookup.js.map +1 -0
- package/dist/publicHealthScotland.d.ts +43 -0
- package/dist/publicHealthScotland.d.ts.map +1 -0
- package/dist/publicHealthScotland.js +61 -0
- package/dist/publicHealthScotland.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/tflLineStatus.d.ts +59 -0
- package/dist/tflLineStatus.d.ts.map +1 -0
- package/dist/tflLineStatus.js +93 -0
- package/dist/tflLineStatus.js.map +1 -0
- package/dist/tnaDiscovery.d.ts +76 -0
- package/dist/tnaDiscovery.d.ts.map +1 -0
- package/dist/tnaDiscovery.js +101 -0
- package/dist/tnaDiscovery.js.map +1 -0
- package/dist/ukhsaDashboard.d.ts +92 -0
- package/dist/ukhsaDashboard.d.ts.map +1 -0
- package/dist/ukhsaDashboard.js +136 -0
- package/dist/ukhsaDashboard.js.map +1 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -19,6 +19,17 @@ builds work offline.
|
|
|
19
19
|
| `police-crimes` | Home Office police.uk | none | Street-level crime within a mile of a point, counted by crime type and outcome |
|
|
20
20
|
| `carbon-intensity` | National Energy System Operator | none | Half-hourly carbon intensity for Great Britain, with the window's cleanest and dirtiest half hours |
|
|
21
21
|
| `parliament-seats` | UK Parliament Members API | none | Seats each party holds in the Commons, with the members counted behind them |
|
|
22
|
+
| `postcode-lookup` | postcodes.io (ONS and OS data) | none | One postcode's country, region, wards, and coordinates |
|
|
23
|
+
| `explore-education-statistics` | Department for Education | none | The most recent statistics releases, with their publish stamps |
|
|
24
|
+
| `london-datastore` | Greater London Authority | none | Every dataset on the London Datastore |
|
|
25
|
+
| `tna-discovery` | The National Archives | none | Catalogue records matching a search, with the total hit count |
|
|
26
|
+
| `ukhsa-dashboard` | UK Health Security Agency | none | Metric points for new HIV diagnoses in England |
|
|
27
|
+
| `nomis` | ONS Nomis | none | SDMX dataset definitions, with maintenance status and keywords |
|
|
28
|
+
| `fingertips-indicators` | Office for Health Improvement and Disparities | none | Public health indicator metadata, with unit, type, and data source |
|
|
29
|
+
| `find-a-tender` | Cabinet Office | none | Recent procurement notices as OCDS 1.1 releases |
|
|
30
|
+
| `tfl-line-status` | Transport for London | none | Live status of every Tube line, with disruption reasons |
|
|
31
|
+
| `public-health-scotland` | Public Health Scotland | none | Every dataset on the Public Health Scotland CKAN portal |
|
|
32
|
+
| `nhsbsa-ckan` | NHS Business Services Authority | none | Every dataset on the NHSBSA CKAN portal |
|
|
22
33
|
|
|
23
34
|
The flood-monitoring adapters use `environment.data.gov.uk` under the Open
|
|
24
35
|
Government Licence v3:
|
|
@@ -89,6 +100,16 @@ under the Open Parliament Licence v3.0:
|
|
|
89
100
|
The call needs a house and a date, so the adapter asks for the caller's own
|
|
90
101
|
date rather than a fixed one.
|
|
91
102
|
|
|
103
|
+
Eleven more keyless adapters landed on 2026-10-05: `postcode-lookup` reads
|
|
104
|
+
one postcode from postcodes.io, `explore-education-statistics` reads the
|
|
105
|
+
Department for Education's most recent releases, `london-datastore` and
|
|
106
|
+
`public-health-scotland` and `nhsbsa-ckan` read CKAN catalogues, `tna-discovery`
|
|
107
|
+
searches The National Archives catalogue, `ukhsa-dashboard` reads UKHSA metric
|
|
108
|
+
points, `nomis` reads the ONS Nomis SDMX catalogue, `fingertips-indicators`
|
|
109
|
+
reads OHID indicator metadata, `find-a-tender` reads OCDS procurement notices,
|
|
110
|
+
and `tfl-line-status` reads live Tube status. Endpoints, exact curl commands,
|
|
111
|
+
capture date, and licences are in `docs/CONNECTOR_DISCOVERY.md`.
|
|
112
|
+
|
|
92
113
|
Note on sources that look obvious but are not usable: `api.ons.gov.uk` was
|
|
93
114
|
retired on 2024-11-25 and now answers every request with a decommission notice.
|
|
94
115
|
The beta API at `api.beta.ons.gov.uk/v1` replaced it for dataset metadata,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { UkDataAdapter } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The Department for Education's Explore Education Statistics publications
|
|
4
|
+
* API. Keyless, published under the Open Government Licence v3.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EXPLORE_EDUCATION_STATISTICS_SOURCE_ID = "explore-education-statistics";
|
|
7
|
+
/** Base URL for the Explore Education Statistics publications endpoint. */
|
|
8
|
+
export declare const EXPLORE_EDUCATION_PUBLICATIONS_URL = "https://api.education.gov.uk/statistics/v1/publications";
|
|
9
|
+
/** How many publications the adapter asks for per page by default. */
|
|
10
|
+
export declare const EXPLORE_EDUCATION_PUBLICATIONS_PAGE_SIZE = 20;
|
|
11
|
+
/** One published statistics release as the API lists it. */
|
|
12
|
+
export interface ExploreEducationPublication {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
/** URL-safe identifier, unique across releases. */
|
|
16
|
+
slug: string;
|
|
17
|
+
summary: string;
|
|
18
|
+
/** Publication stamp the API reports, ISO 8601 with an offset. */
|
|
19
|
+
lastPublishedIso: string;
|
|
20
|
+
}
|
|
21
|
+
/** One page of the publications catalogue plus the API's paging counts. */
|
|
22
|
+
export interface ExploreEducationPublicationsPage {
|
|
23
|
+
page: number;
|
|
24
|
+
pageSize: number;
|
|
25
|
+
totalResults: number;
|
|
26
|
+
totalPages: number;
|
|
27
|
+
publications: ExploreEducationPublication[];
|
|
28
|
+
}
|
|
29
|
+
/** Rolled-up view of one publications page. */
|
|
30
|
+
export interface ExploreEducationPublicationsSummary {
|
|
31
|
+
publicationCount: number;
|
|
32
|
+
/** Total releases the API holds, not just this page. */
|
|
33
|
+
totalResults: number;
|
|
34
|
+
/** The most recent publish stamp on this page, or "" when the page is empty. */
|
|
35
|
+
latestPublishedIso: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds a publications URL for one page.
|
|
39
|
+
*
|
|
40
|
+
* @param page - one-based page number
|
|
41
|
+
* @param pageSize - records per page
|
|
42
|
+
* @returns the publications catalogue URL
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildExploreEducationPublicationsUrl(page: number, pageSize: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Parses an Explore Education Statistics publications payload.
|
|
47
|
+
*
|
|
48
|
+
* @param payload - the raw JSON body from the publications endpoint
|
|
49
|
+
* @returns the page of publications plus the API's paging counts
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseExploreEducationPublications(payload: unknown): ExploreEducationPublicationsPage;
|
|
52
|
+
/**
|
|
53
|
+
* Sums one publications page.
|
|
54
|
+
*
|
|
55
|
+
* @param page - a page from {@link parseExploreEducationPublications}
|
|
56
|
+
* @returns publication counts and the newest publish stamp on the page
|
|
57
|
+
*/
|
|
58
|
+
export declare function summarizeExploreEducationPublications(page: ExploreEducationPublicationsPage): ExploreEducationPublicationsSummary;
|
|
59
|
+
/**
|
|
60
|
+
* Lists one page of Explore Education Statistics publications.
|
|
61
|
+
*
|
|
62
|
+
* @param options - page, page size, and an optional fetch implementation
|
|
63
|
+
* @returns the page of publications the endpoint returns
|
|
64
|
+
*/
|
|
65
|
+
export declare function fetchExploreEducationPublications(options?: {
|
|
66
|
+
page?: number;
|
|
67
|
+
pageSize?: number;
|
|
68
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
69
|
+
}): Promise<ExploreEducationPublicationsPage>;
|
|
70
|
+
/** Explore Education Statistics publications, keyless. */
|
|
71
|
+
export declare const exploreEducationStatisticsAdapter: UkDataAdapter<ExploreEducationPublicationsPage>;
|
|
72
|
+
//# sourceMappingURL=exploreEducationStatistics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exploreEducationStatistics.d.ts","sourceRoot":"","sources":["../src/exploreEducationStatistics.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,sCAAsC,iCAAiC,CAAC;AAErF,2EAA2E;AAC3E,eAAO,MAAM,kCAAkC,4DACY,CAAC;AAE5D,sEAAsE;AACtE,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAE3D,4DAA4D;AAC5D,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,2EAA2E;AAC3E,MAAM,WAAW,gCAAgC;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,2BAA2B,EAAE,CAAC;CAC7C;AAED,+CAA+C;AAC/C,MAAM,WAAW,mCAAmC;IAClD,gBAAgB,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAoBD;;;;;;GAMG;AACH,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3F;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,OAAO,GACf,gCAAgC,CAkBlC;AAED;;;;;GAKG;AACH,wBAAgB,qCAAqC,CACnD,IAAI,EAAE,gCAAgC,GACrC,mCAAmC,CAerC;AAED;;;;;GAKG;AACH,wBAAsB,iCAAiC,CACrD,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAChC,GACL,OAAO,CAAC,gCAAgC,CAAC,CAc3C;AAED,0DAA0D;AAC1D,eAAO,MAAM,iCAAiC,EAAE,aAAa,CAAC,gCAAgC,CAc7F,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UkSourceApiError, UkSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Department for Education's Explore Education Statistics publications
|
|
6
|
+
* API. Keyless, published under the Open Government Licence v3.
|
|
7
|
+
*/
|
|
8
|
+
export const EXPLORE_EDUCATION_STATISTICS_SOURCE_ID = 'explore-education-statistics';
|
|
9
|
+
/** Base URL for the Explore Education Statistics publications endpoint. */
|
|
10
|
+
export const EXPLORE_EDUCATION_PUBLICATIONS_URL = 'https://api.education.gov.uk/statistics/v1/publications';
|
|
11
|
+
/** How many publications the adapter asks for per page by default. */
|
|
12
|
+
export const EXPLORE_EDUCATION_PUBLICATIONS_PAGE_SIZE = 20;
|
|
13
|
+
const EXPLORE_EDUCATION_PUBLICATION_SCHEMA = z.object({
|
|
14
|
+
id: z.string(),
|
|
15
|
+
title: z.string(),
|
|
16
|
+
slug: z.string(),
|
|
17
|
+
summary: z.string().nullable().optional(),
|
|
18
|
+
lastPublished: z.string(),
|
|
19
|
+
});
|
|
20
|
+
const EXPLORE_EDUCATION_PUBLICATIONS_SCHEMA = z.object({
|
|
21
|
+
paging: z.object({
|
|
22
|
+
page: z.number(),
|
|
23
|
+
pageSize: z.number(),
|
|
24
|
+
totalResults: z.number(),
|
|
25
|
+
totalPages: z.number(),
|
|
26
|
+
}),
|
|
27
|
+
results: z.array(EXPLORE_EDUCATION_PUBLICATION_SCHEMA),
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Builds a publications URL for one page.
|
|
31
|
+
*
|
|
32
|
+
* @param page - one-based page number
|
|
33
|
+
* @param pageSize - records per page
|
|
34
|
+
* @returns the publications catalogue URL
|
|
35
|
+
*/
|
|
36
|
+
export function buildExploreEducationPublicationsUrl(page, pageSize) {
|
|
37
|
+
return `${EXPLORE_EDUCATION_PUBLICATIONS_URL}?page=${page}&pageSize=${pageSize}`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parses an Explore Education Statistics publications payload.
|
|
41
|
+
*
|
|
42
|
+
* @param payload - the raw JSON body from the publications endpoint
|
|
43
|
+
* @returns the page of publications plus the API's paging counts
|
|
44
|
+
*/
|
|
45
|
+
export function parseExploreEducationPublications(payload) {
|
|
46
|
+
const parsed = EXPLORE_EDUCATION_PUBLICATIONS_SCHEMA.safeParse(payload);
|
|
47
|
+
if (!parsed.success) {
|
|
48
|
+
throw new UkSourceParseError('explore-education-statistics', parsed.error.message);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
page: parsed.data.paging.page,
|
|
52
|
+
pageSize: parsed.data.paging.pageSize,
|
|
53
|
+
totalResults: parsed.data.paging.totalResults,
|
|
54
|
+
totalPages: parsed.data.paging.totalPages,
|
|
55
|
+
publications: parsed.data.results.map((publication) => ({
|
|
56
|
+
id: publication.id,
|
|
57
|
+
title: publication.title,
|
|
58
|
+
slug: publication.slug,
|
|
59
|
+
summary: publication.summary ?? '',
|
|
60
|
+
lastPublishedIso: publication.lastPublished,
|
|
61
|
+
})),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Sums one publications page.
|
|
66
|
+
*
|
|
67
|
+
* @param page - a page from {@link parseExploreEducationPublications}
|
|
68
|
+
* @returns publication counts and the newest publish stamp on the page
|
|
69
|
+
*/
|
|
70
|
+
export function summarizeExploreEducationPublications(page) {
|
|
71
|
+
let latestPublishedIso = '';
|
|
72
|
+
let latestPublishedEpoch = Number.NEGATIVE_INFINITY;
|
|
73
|
+
for (const publication of page.publications) {
|
|
74
|
+
const epoch = Date.parse(publication.lastPublishedIso);
|
|
75
|
+
if (Number.isFinite(epoch) && epoch > latestPublishedEpoch) {
|
|
76
|
+
latestPublishedEpoch = epoch;
|
|
77
|
+
latestPublishedIso = publication.lastPublishedIso;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
publicationCount: page.publications.length,
|
|
82
|
+
totalResults: page.totalResults,
|
|
83
|
+
latestPublishedIso,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Lists one page of Explore Education Statistics publications.
|
|
88
|
+
*
|
|
89
|
+
* @param options - page, page size, and an optional fetch implementation
|
|
90
|
+
* @returns the page of publications the endpoint returns
|
|
91
|
+
*/
|
|
92
|
+
export async function fetchExploreEducationPublications(options = {}) {
|
|
93
|
+
const { page = 1, pageSize = EXPLORE_EDUCATION_PUBLICATIONS_PAGE_SIZE, fetchImpl = globalThis.fetch, } = options;
|
|
94
|
+
const response = await fetchImpl(buildExploreEducationPublicationsUrl(page, pageSize));
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new UkSourceApiError('explore-education-statistics', `HTTP ${response.status} listing publications`);
|
|
97
|
+
}
|
|
98
|
+
return parseExploreEducationPublications(await response.json());
|
|
99
|
+
}
|
|
100
|
+
/** Explore Education Statistics publications, keyless. */
|
|
101
|
+
export const exploreEducationStatisticsAdapter = {
|
|
102
|
+
id: EXPLORE_EDUCATION_STATISTICS_SOURCE_ID,
|
|
103
|
+
name: 'Department for Education Explore Education Statistics publications',
|
|
104
|
+
auth: 'none',
|
|
105
|
+
description: 'The most recent statistics releases the Department for Education has published.',
|
|
106
|
+
fetchLive: (options) => fetchExploreEducationPublications(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
107
|
+
parse: parseExploreEducationPublications,
|
|
108
|
+
loadFixture: () => parseExploreEducationPublications(readFixtureJson('explore-education-statistics-2026-10-05.json')),
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=exploreEducationStatistics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exploreEducationStatistics.js","sourceRoot":"","sources":["../src/exploreEducationStatistics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { UkDataAdapter } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Find a Tender, the UK's post-Brexit public procurement service run by the
|
|
4
|
+
* Cabinet Office. Keyless, and the notices are published under the Open
|
|
5
|
+
* Government Licence v3, matching the licence the payload itself carries.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FIND_A_TENDER_SOURCE_ID = "find-a-tender";
|
|
8
|
+
/** The OCDS 1.1 release packages endpoint. */
|
|
9
|
+
export declare const FIND_A_TENDER_RELEASE_PACKAGES_URL = "https://www.find-tender.service.gov.uk/api/1.0/ocdsReleasePackages";
|
|
10
|
+
/** How many release packages the adapter asks for by default. */
|
|
11
|
+
export declare const FIND_A_TENDER_RELEASE_LIMIT = 10;
|
|
12
|
+
/** One Open Contracting Data Standard release, as Find a Tender publishes it. */
|
|
13
|
+
export interface FindATenderRelease {
|
|
14
|
+
/** Open Contracting identifier shared by every release of one tender. */
|
|
15
|
+
ocid: string;
|
|
16
|
+
/** The release id, unique within the tender. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** When the release was published, ISO 8601 with an offset. */
|
|
19
|
+
date: string;
|
|
20
|
+
/** Release tags, such as "tender", "award", or "contractAmendment". */
|
|
21
|
+
tags: string[];
|
|
22
|
+
tenderTitle: string;
|
|
23
|
+
/** Tender status, such as "active" or "complete", or "" when unset. */
|
|
24
|
+
tenderStatus: string;
|
|
25
|
+
procurementMethod: string;
|
|
26
|
+
buyerName: string;
|
|
27
|
+
/** The tender value amount, or null when the release leaves it out. */
|
|
28
|
+
tenderValueAmount: number | null;
|
|
29
|
+
}
|
|
30
|
+
/** One page of OCDS releases plus the package metadata around them. */
|
|
31
|
+
export interface FindATenderReleasePage {
|
|
32
|
+
/** The package URI the service echoes back, including its own cursor. */
|
|
33
|
+
uri: string;
|
|
34
|
+
version: string;
|
|
35
|
+
publishedDate: string;
|
|
36
|
+
releases: FindATenderRelease[];
|
|
37
|
+
}
|
|
38
|
+
/** Release counts by tender status. */
|
|
39
|
+
export interface FindATenderStatusCount {
|
|
40
|
+
status: string;
|
|
41
|
+
releaseCount: number;
|
|
42
|
+
}
|
|
43
|
+
/** Rolled-up view of one page of Find a Tender releases. */
|
|
44
|
+
export interface FindATenderSummary {
|
|
45
|
+
releaseCount: number;
|
|
46
|
+
/** Releases with an unset tender status. */
|
|
47
|
+
unsetStatusCount: number;
|
|
48
|
+
statusCounts: FindATenderStatusCount[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parses an OCDS release packages payload into releases.
|
|
52
|
+
*
|
|
53
|
+
* @param payload - the raw JSON body from the release packages endpoint
|
|
54
|
+
* @returns the releases plus the package metadata
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseFindATenderReleases(payload: unknown): FindATenderReleasePage;
|
|
57
|
+
/**
|
|
58
|
+
* Counts the releases on one page by tender status.
|
|
59
|
+
*
|
|
60
|
+
* @param page - a page from {@link parseFindATenderReleases}
|
|
61
|
+
* @returns the release count and a per-status breakdown, most releases first
|
|
62
|
+
*/
|
|
63
|
+
export declare function summarizeFindATenderReleases(page: FindATenderReleasePage): FindATenderSummary;
|
|
64
|
+
/**
|
|
65
|
+
* Lists the most recent Find a Tender release packages.
|
|
66
|
+
*
|
|
67
|
+
* @param options - release limit and an optional fetch implementation
|
|
68
|
+
* @returns the releases the endpoint returns
|
|
69
|
+
*/
|
|
70
|
+
export declare function fetchFindATenderReleases(options?: {
|
|
71
|
+
limit?: number;
|
|
72
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
73
|
+
}): Promise<FindATenderReleasePage>;
|
|
74
|
+
/** Find a Tender OCDS release packages, keyless. */
|
|
75
|
+
export declare const findATenderAdapter: UkDataAdapter<FindATenderReleasePage>;
|
|
76
|
+
//# sourceMappingURL=findATender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findATender.d.ts","sourceRoot":"","sources":["../src/findATender.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAEvD,8CAA8C;AAC9C,eAAO,MAAM,kCAAkC,uEACuB,CAAC;AAEvE,iEAAiE;AACjE,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,uEAAuE;AACvE,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED,uCAAuC;AACvC,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,4DAA4D;AAC5D,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,sBAAsB,EAAE,CAAC;CACxC;AA0BD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,sBAAsB,CAqBjF;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,CAiB7F;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAO,GACpE,OAAO,CAAC,sBAAsB,CAAC,CAOjC;AAED,oDAAoD;AACpD,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,sBAAsB,CAWpE,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UkSourceApiError, UkSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/**
|
|
5
|
+
* Find a Tender, the UK's post-Brexit public procurement service run by the
|
|
6
|
+
* Cabinet Office. Keyless, and the notices are published under the Open
|
|
7
|
+
* Government Licence v3, matching the licence the payload itself carries.
|
|
8
|
+
*/
|
|
9
|
+
export const FIND_A_TENDER_SOURCE_ID = 'find-a-tender';
|
|
10
|
+
/** The OCDS 1.1 release packages endpoint. */
|
|
11
|
+
export const FIND_A_TENDER_RELEASE_PACKAGES_URL = 'https://www.find-tender.service.gov.uk/api/1.0/ocdsReleasePackages';
|
|
12
|
+
/** How many release packages the adapter asks for by default. */
|
|
13
|
+
export const FIND_A_TENDER_RELEASE_LIMIT = 10;
|
|
14
|
+
const FIND_A_TENDER_RELEASE_SCHEMA = z.object({
|
|
15
|
+
ocid: z.string(),
|
|
16
|
+
id: z.string(),
|
|
17
|
+
date: z.string(),
|
|
18
|
+
tag: z.array(z.string()).optional(),
|
|
19
|
+
tender: z
|
|
20
|
+
.object({
|
|
21
|
+
title: z.string().nullable().optional(),
|
|
22
|
+
status: z.string().nullable().optional(),
|
|
23
|
+
procurementMethod: z.string().nullable().optional(),
|
|
24
|
+
value: z.object({ amount: z.number().nullable().optional() }).nullable().optional(),
|
|
25
|
+
})
|
|
26
|
+
.nullable()
|
|
27
|
+
.optional(),
|
|
28
|
+
buyer: z.object({ name: z.string().nullable().optional() }).nullable().optional(),
|
|
29
|
+
});
|
|
30
|
+
const FIND_A_TENDER_RELEASE_PACKAGE_SCHEMA = z.object({
|
|
31
|
+
uri: z.string(),
|
|
32
|
+
version: z.string(),
|
|
33
|
+
publishedDate: z.string(),
|
|
34
|
+
releases: z.array(FIND_A_TENDER_RELEASE_SCHEMA),
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Parses an OCDS release packages payload into releases.
|
|
38
|
+
*
|
|
39
|
+
* @param payload - the raw JSON body from the release packages endpoint
|
|
40
|
+
* @returns the releases plus the package metadata
|
|
41
|
+
*/
|
|
42
|
+
export function parseFindATenderReleases(payload) {
|
|
43
|
+
const parsed = FIND_A_TENDER_RELEASE_PACKAGE_SCHEMA.safeParse(payload);
|
|
44
|
+
if (!parsed.success) {
|
|
45
|
+
throw new UkSourceParseError('find-a-tender', parsed.error.message);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
uri: parsed.data.uri,
|
|
49
|
+
version: parsed.data.version,
|
|
50
|
+
publishedDate: parsed.data.publishedDate,
|
|
51
|
+
releases: parsed.data.releases.map((release) => ({
|
|
52
|
+
ocid: release.ocid,
|
|
53
|
+
id: release.id,
|
|
54
|
+
date: release.date,
|
|
55
|
+
tags: release.tag ?? [],
|
|
56
|
+
tenderTitle: release.tender?.title ?? '',
|
|
57
|
+
tenderStatus: release.tender?.status ?? '',
|
|
58
|
+
procurementMethod: release.tender?.procurementMethod ?? '',
|
|
59
|
+
buyerName: release.buyer?.name ?? '',
|
|
60
|
+
tenderValueAmount: release.tender?.value?.amount ?? null,
|
|
61
|
+
})),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Counts the releases on one page by tender status.
|
|
66
|
+
*
|
|
67
|
+
* @param page - a page from {@link parseFindATenderReleases}
|
|
68
|
+
* @returns the release count and a per-status breakdown, most releases first
|
|
69
|
+
*/
|
|
70
|
+
export function summarizeFindATenderReleases(page) {
|
|
71
|
+
const countByStatus = new Map();
|
|
72
|
+
let unsetStatusCount = 0;
|
|
73
|
+
for (const release of page.releases) {
|
|
74
|
+
if (release.tenderStatus === '') {
|
|
75
|
+
unsetStatusCount += 1;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
countByStatus.set(release.tenderStatus, (countByStatus.get(release.tenderStatus) ?? 0) + 1);
|
|
79
|
+
}
|
|
80
|
+
const statusCounts = [...countByStatus.entries()]
|
|
81
|
+
.map(([status, releaseCount]) => ({ status, releaseCount }))
|
|
82
|
+
.sort((left, right) => right.releaseCount - left.releaseCount || left.status.localeCompare(right.status));
|
|
83
|
+
return { releaseCount: page.releases.length, unsetStatusCount, statusCounts };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Lists the most recent Find a Tender release packages.
|
|
87
|
+
*
|
|
88
|
+
* @param options - release limit and an optional fetch implementation
|
|
89
|
+
* @returns the releases the endpoint returns
|
|
90
|
+
*/
|
|
91
|
+
export async function fetchFindATenderReleases(options = {}) {
|
|
92
|
+
const { limit = FIND_A_TENDER_RELEASE_LIMIT, fetchImpl = globalThis.fetch } = options;
|
|
93
|
+
const response = await fetchImpl(`${FIND_A_TENDER_RELEASE_PACKAGES_URL}?limit=${limit}`);
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
throw new UkSourceApiError('find-a-tender', `HTTP ${response.status} listing releases`);
|
|
96
|
+
}
|
|
97
|
+
return parseFindATenderReleases(await response.json());
|
|
98
|
+
}
|
|
99
|
+
/** Find a Tender OCDS release packages, keyless. */
|
|
100
|
+
export const findATenderAdapter = {
|
|
101
|
+
id: FIND_A_TENDER_SOURCE_ID,
|
|
102
|
+
name: 'Find a Tender OCDS release packages',
|
|
103
|
+
auth: 'none',
|
|
104
|
+
description: 'The most recent public procurement notices Find a Tender publishes.',
|
|
105
|
+
fetchLive: (options) => fetchFindATenderReleases(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
106
|
+
parse: parseFindATenderReleases,
|
|
107
|
+
loadFixture: () => parseFindATenderReleases(readFixtureJson('find-a-tender-2026-10-05.json')),
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=findATender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findATender.js","sourceRoot":"","sources":["../src/findATender.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { UkDataAdapter } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The Office for Health Improvement and Disparities public health profiles,
|
|
4
|
+
* published by Fingertips. Keyless, under the Open Government Licence v3.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FINGERTIPS_INDICATORS_SOURCE_ID = "fingertips-indicators";
|
|
7
|
+
/** Base URL for the Fingertips indicator metadata call. */
|
|
8
|
+
export declare const FINGERTIPS_INDICATOR_METADATA_URL = "https://fingertips.phe.org.uk/api/indicator_metadata/by_indicator_id";
|
|
9
|
+
/** The indicator the adapter reads when the caller does not name one. */
|
|
10
|
+
export declare const DEFAULT_FINGERTIPS_INDICATOR_IDS: number[];
|
|
11
|
+
/** One public health indicator and the metadata behind it. */
|
|
12
|
+
export interface FingertipsIndicatorMetadata {
|
|
13
|
+
indicatorId: number;
|
|
14
|
+
name: string;
|
|
15
|
+
/** The body that produced the figures, such as the ONS. */
|
|
16
|
+
dataSource: string;
|
|
17
|
+
/** The unit the indicator counts in, or "" when Fingertips leaves it out. */
|
|
18
|
+
unitLabel: string;
|
|
19
|
+
/** The measure type, such as "Life expectancy". */
|
|
20
|
+
valueType: string;
|
|
21
|
+
/** The year basis, such as "Calendar". */
|
|
22
|
+
yearType: string;
|
|
23
|
+
/** When Fingertips last uploaded data, or "" when unset. */
|
|
24
|
+
lastUploadedAt: string;
|
|
25
|
+
/** Fingertips' override stamp for the last change, or "" when unset. */
|
|
26
|
+
latestChangeTimestampOverride: string;
|
|
27
|
+
}
|
|
28
|
+
/** Rolled-up view of the indicators a call returns. */
|
|
29
|
+
export interface FingertipsIndicatorsSummary {
|
|
30
|
+
indicatorCount: number;
|
|
31
|
+
/** The distinct data sources behind the indicators, sorted. */
|
|
32
|
+
dataSources: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds the Fingertips indicator metadata URL for a set of indicator ids.
|
|
36
|
+
*
|
|
37
|
+
* @param indicatorIds - the indicator ids to read
|
|
38
|
+
* @returns the metadata URL, ids comma-separated
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildFingertipsIndicatorUrl(indicatorIds: number[]): string;
|
|
41
|
+
/**
|
|
42
|
+
* Parses a Fingertips indicator metadata payload.
|
|
43
|
+
*
|
|
44
|
+
* @param payload - the raw JSON body from the metadata endpoint
|
|
45
|
+
* @returns one entry per indicator the payload carries
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseFingertipsIndicators(payload: unknown): FingertipsIndicatorMetadata[];
|
|
48
|
+
/**
|
|
49
|
+
* Sums the indicators a call returns.
|
|
50
|
+
*
|
|
51
|
+
* @param indicators - indicators from {@link parseFingertipsIndicators}
|
|
52
|
+
* @returns the indicator count and the distinct data sources behind them
|
|
53
|
+
*/
|
|
54
|
+
export declare function summarizeFingertipsIndicators(indicators: FingertipsIndicatorMetadata[]): FingertipsIndicatorsSummary;
|
|
55
|
+
/**
|
|
56
|
+
* Reads metadata for a set of Fingertips public health indicators.
|
|
57
|
+
*
|
|
58
|
+
* @param options - indicator ids and an optional fetch implementation
|
|
59
|
+
* @returns the indicator metadata the endpoint returns
|
|
60
|
+
*/
|
|
61
|
+
export declare function fetchFingertipsIndicators(options?: {
|
|
62
|
+
indicatorIds?: number[];
|
|
63
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
64
|
+
}): Promise<FingertipsIndicatorMetadata[]>;
|
|
65
|
+
/** Fingertips public health indicator metadata, keyless. */
|
|
66
|
+
export declare const fingertipsIndicatorsAdapter: UkDataAdapter<FingertipsIndicatorMetadata[]>;
|
|
67
|
+
//# sourceMappingURL=fingertipsIndicators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingertipsIndicators.d.ts","sourceRoot":"","sources":["../src/fingertipsIndicators.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAEvE,2DAA2D;AAC3D,eAAO,MAAM,iCAAiC,yEAC0B,CAAC;AAEzE,yEAAyE;AACzE,eAAO,MAAM,gCAAgC,EAAE,MAAM,EAAY,CAAC;AAElE,8DAA8D;AAC9D,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,6BAA6B,EAAE,MAAM,CAAC;CACvC;AAED,uDAAuD;AACvD,MAAM,WAAW,2BAA2B;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAkBD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,CAE1E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,2BAA2B,EAAE,CAezF;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,2BAA2B,EAAE,GACxC,2BAA2B,CAK7B;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAO,GAC7E,OAAO,CAAC,2BAA2B,EAAE,CAAC,CAOxC;AAED,4DAA4D;AAC5D,eAAO,MAAM,2BAA2B,EAAE,aAAa,CAAC,2BAA2B,EAAE,CAapF,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UkSourceApiError, UkSourceParseError } from './errors.js';
|
|
3
|
+
import { readFixtureJson } from './fixtures.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Office for Health Improvement and Disparities public health profiles,
|
|
6
|
+
* published by Fingertips. Keyless, under the Open Government Licence v3.
|
|
7
|
+
*/
|
|
8
|
+
export const FINGERTIPS_INDICATORS_SOURCE_ID = 'fingertips-indicators';
|
|
9
|
+
/** Base URL for the Fingertips indicator metadata call. */
|
|
10
|
+
export const FINGERTIPS_INDICATOR_METADATA_URL = 'https://fingertips.phe.org.uk/api/indicator_metadata/by_indicator_id';
|
|
11
|
+
/** The indicator the adapter reads when the caller does not name one. */
|
|
12
|
+
export const DEFAULT_FINGERTIPS_INDICATOR_IDS = [90366];
|
|
13
|
+
const FINGERTIPS_INDICATOR_SCHEMA = z.object({
|
|
14
|
+
IID: z.number(),
|
|
15
|
+
LatestChangeTimestampOverride: z.string().nullable().optional(),
|
|
16
|
+
Unit: z.object({ Label: z.string() }).nullable().optional(),
|
|
17
|
+
YearType: z.object({ Name: z.string() }).nullable().optional(),
|
|
18
|
+
ValueType: z.object({ Name: z.string() }).nullable().optional(),
|
|
19
|
+
Descriptive: z
|
|
20
|
+
.object({ Name: z.string(), DataSource: z.string().nullable().optional() })
|
|
21
|
+
.nullable()
|
|
22
|
+
.optional(),
|
|
23
|
+
DataChange: z.object({ LastUploadedAt: z.string().nullable().optional() }).nullable().optional(),
|
|
24
|
+
});
|
|
25
|
+
/** Indicator metadata keyed by indicator id, as Fingertips returns it. */
|
|
26
|
+
const FINGERTIPS_INDICATORS_SCHEMA = z.record(z.string(), FINGERTIPS_INDICATOR_SCHEMA);
|
|
27
|
+
/**
|
|
28
|
+
* Builds the Fingertips indicator metadata URL for a set of indicator ids.
|
|
29
|
+
*
|
|
30
|
+
* @param indicatorIds - the indicator ids to read
|
|
31
|
+
* @returns the metadata URL, ids comma-separated
|
|
32
|
+
*/
|
|
33
|
+
export function buildFingertipsIndicatorUrl(indicatorIds) {
|
|
34
|
+
return `${FINGERTIPS_INDICATOR_METADATA_URL}?indicator_ids=${indicatorIds.join(',')}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parses a Fingertips indicator metadata payload.
|
|
38
|
+
*
|
|
39
|
+
* @param payload - the raw JSON body from the metadata endpoint
|
|
40
|
+
* @returns one entry per indicator the payload carries
|
|
41
|
+
*/
|
|
42
|
+
export function parseFingertipsIndicators(payload) {
|
|
43
|
+
const parsed = FINGERTIPS_INDICATORS_SCHEMA.safeParse(payload);
|
|
44
|
+
if (!parsed.success) {
|
|
45
|
+
throw new UkSourceParseError('fingertips-indicators', parsed.error.message);
|
|
46
|
+
}
|
|
47
|
+
return Object.values(parsed.data).map((indicator) => ({
|
|
48
|
+
indicatorId: indicator.IID,
|
|
49
|
+
name: indicator.Descriptive?.Name ?? '',
|
|
50
|
+
dataSource: indicator.Descriptive?.DataSource ?? '',
|
|
51
|
+
unitLabel: indicator.Unit?.Label ?? '',
|
|
52
|
+
valueType: indicator.ValueType?.Name ?? '',
|
|
53
|
+
yearType: indicator.YearType?.Name ?? '',
|
|
54
|
+
lastUploadedAt: indicator.DataChange?.LastUploadedAt ?? '',
|
|
55
|
+
latestChangeTimestampOverride: indicator.LatestChangeTimestampOverride ?? '',
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Sums the indicators a call returns.
|
|
60
|
+
*
|
|
61
|
+
* @param indicators - indicators from {@link parseFingertipsIndicators}
|
|
62
|
+
* @returns the indicator count and the distinct data sources behind them
|
|
63
|
+
*/
|
|
64
|
+
export function summarizeFingertipsIndicators(indicators) {
|
|
65
|
+
const dataSources = [...new Set(indicators.map((indicator) => indicator.dataSource))]
|
|
66
|
+
.filter((dataSource) => dataSource !== '')
|
|
67
|
+
.sort((left, right) => left.localeCompare(right));
|
|
68
|
+
return { indicatorCount: indicators.length, dataSources };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Reads metadata for a set of Fingertips public health indicators.
|
|
72
|
+
*
|
|
73
|
+
* @param options - indicator ids and an optional fetch implementation
|
|
74
|
+
* @returns the indicator metadata the endpoint returns
|
|
75
|
+
*/
|
|
76
|
+
export async function fetchFingertipsIndicators(options = {}) {
|
|
77
|
+
const { indicatorIds = DEFAULT_FINGERTIPS_INDICATOR_IDS, fetchImpl = globalThis.fetch } = options;
|
|
78
|
+
const response = await fetchImpl(buildFingertipsIndicatorUrl(indicatorIds));
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new UkSourceApiError('fingertips-indicators', `HTTP ${response.status} reading metadata`);
|
|
81
|
+
}
|
|
82
|
+
return parseFingertipsIndicators(await response.json());
|
|
83
|
+
}
|
|
84
|
+
/** Fingertips public health indicator metadata, keyless. */
|
|
85
|
+
export const fingertipsIndicatorsAdapter = {
|
|
86
|
+
id: FINGERTIPS_INDICATORS_SOURCE_ID,
|
|
87
|
+
name: 'Office for Health Improvement and Disparities Fingertips indicators',
|
|
88
|
+
auth: 'none',
|
|
89
|
+
description: 'Metadata for the public health indicators Fingertips publishes, with their sources.',
|
|
90
|
+
fetchLive: (options) => fetchFingertipsIndicators(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
|
|
91
|
+
parse: parseFingertipsIndicators,
|
|
92
|
+
loadFixture: () => parseFingertipsIndicators(readFixtureJson('fingertips-indicators-2026-10-05.json')),
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=fingertipsIndicators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingertipsIndicators.js","sourceRoot":"","sources":["../src/fingertipsIndicators.ts"],"names":[],"mappings":"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"}
|