@uk-open-data-connectors/uk-sources 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +206 -0
  2. package/dist/ancientWoodland.d.ts +102 -0
  3. package/dist/ancientWoodland.d.ts.map +1 -0
  4. package/dist/ancientWoodland.js +245 -0
  5. package/dist/ancientWoodland.js.map +1 -0
  6. package/dist/bankRate.d.ts +107 -0
  7. package/dist/bankRate.d.ts.map +1 -0
  8. package/dist/bankRate.js +251 -0
  9. package/dist/bankRate.js.map +1 -0
  10. package/dist/carbonIntensity.d.ts +151 -0
  11. package/dist/carbonIntensity.d.ts.map +1 -0
  12. package/dist/carbonIntensity.js +257 -0
  13. package/dist/carbonIntensity.js.map +1 -0
  14. package/dist/errors.d.ts +15 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +26 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/fixtures.d.ts +5 -0
  19. package/dist/fixtures.d.ts.map +1 -0
  20. package/dist/fixtures.js +11 -0
  21. package/dist/fixtures.js.map +1 -0
  22. package/dist/floodMonitoring.d.ts +70 -0
  23. package/dist/floodMonitoring.d.ts.map +1 -0
  24. package/dist/floodMonitoring.js +164 -0
  25. package/dist/floodMonitoring.js.map +1 -0
  26. package/dist/foodHygiene.d.ts +68 -0
  27. package/dist/foodHygiene.d.ts.map +1 -0
  28. package/dist/foodHygiene.js +103 -0
  29. package/dist/foodHygiene.js.map +1 -0
  30. package/dist/index.d.ts +47 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +25 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/onsDatasets.d.ts +67 -0
  35. package/dist/onsDatasets.d.ts.map +1 -0
  36. package/dist/onsDatasets.js +111 -0
  37. package/dist/onsDatasets.js.map +1 -0
  38. package/dist/parliamentSeats.d.ts +115 -0
  39. package/dist/parliamentSeats.d.ts.map +1 -0
  40. package/dist/parliamentSeats.js +202 -0
  41. package/dist/parliamentSeats.js.map +1 -0
  42. package/dist/planningDatasets.d.ts +65 -0
  43. package/dist/planningDatasets.d.ts.map +1 -0
  44. package/dist/planningDatasets.js +100 -0
  45. package/dist/planningDatasets.js.map +1 -0
  46. package/dist/policeCrimes.d.ts +152 -0
  47. package/dist/policeCrimes.d.ts.map +1 -0
  48. package/dist/policeCrimes.js +262 -0
  49. package/dist/policeCrimes.js.map +1 -0
  50. package/dist/registry.d.ts +17 -0
  51. package/dist/registry.d.ts.map +1 -0
  52. package/dist/registry.js +67 -0
  53. package/dist/registry.js.map +1 -0
  54. package/dist/tflBikePoints.d.ts +65 -0
  55. package/dist/tflBikePoints.d.ts.map +1 -0
  56. package/dist/tflBikePoints.js +149 -0
  57. package/dist/tflBikePoints.js.map +1 -0
  58. package/dist/types.d.ts +30 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/package.json +46 -0
@@ -0,0 +1,262 @@
1
+ import { z } from 'zod';
2
+ import { UkSourceApiError, UkSourceParseError } from './errors.js';
3
+ import { readFixtureJson } from './fixtures.js';
4
+ /**
5
+ * Street-level recorded crime from the Home Office's police.uk open data API.
6
+ * Every police force in England, Wales and Northern Ireland publishes to it,
7
+ * keyless and monthly, under the Open Government Licence v3:
8
+ * <https://data.police.uk/docs/>
9
+ */
10
+ export const POLICE_CRIMES_SOURCE_ID = 'police-crimes';
11
+ /** The API root every call here is built from. */
12
+ export const POLICE_API_BASE_URL = 'https://data.police.uk/api';
13
+ /** Street-level crime within a mile of a point, for one published month. */
14
+ export const POLICE_STREET_CRIMES_URL = `${POLICE_API_BASE_URL}/crimes-street/all-crime`;
15
+ /** The months the API holds, newest first, with the forces behind each. */
16
+ export const POLICE_STREET_DATES_URL = `${POLICE_API_BASE_URL}/crimes-street-dates`;
17
+ /** The crime types the API uses, with the label it gives each one. */
18
+ export const POLICE_CRIME_CATEGORIES_URL = `${POLICE_API_BASE_URL}/crime-categories`;
19
+ /**
20
+ * The radius the API answers for. Its street-level endpoints take a point and
21
+ * return everything within one mile of it; there is no way to ask for less.
22
+ */
23
+ export const POLICE_STREET_CRIMES_RADIUS_MILES = 1;
24
+ /** How many published months the summary covers by default. */
25
+ export const DEFAULT_POLICE_CRIME_MONTH_COUNT = 12;
26
+ /** The point the story counts around, and the name the page gives it. */
27
+ export const DEFAULT_POLICE_CRIME_LOCATION = {
28
+ label: 'Leeds city centre',
29
+ latitude: 53.7997,
30
+ longitude: -1.5492,
31
+ };
32
+ const POLICE_CATEGORY_SCHEMA = z.object({ url: z.string(), name: z.string() });
33
+ const POLICE_RECORD_SCHEMA = z.object({
34
+ month: z.string(),
35
+ category: z.string(),
36
+ // The API writes no outcome at all for offences still without one.
37
+ outcome_status: z.object({ category: z.string() }).nullable().optional(),
38
+ });
39
+ const POLICE_RESPONSES_SCHEMA = z.object({
40
+ categories: z.array(POLICE_CATEGORY_SCHEMA),
41
+ months: z.array(z.object({ month: z.string(), records: z.array(POLICE_RECORD_SCHEMA) })),
42
+ });
43
+ const POLICE_DATES_SCHEMA = z.array(z.object({ date: z.string() }));
44
+ const POLICE_MONTH_PATTERN = /^\d{4}-(0[1-9]|1[0-2])$/;
45
+ /** Counts records per crime type, biggest first, ties broken by label. */
46
+ function countByCategory(records, namesBySlug) {
47
+ const counts = new Map();
48
+ for (const record of records) {
49
+ counts.set(record.category, (counts.get(record.category) ?? 0) + 1);
50
+ }
51
+ return [...counts.entries()]
52
+ .map(([slug, recordCount]) => ({
53
+ slug,
54
+ name: namesBySlug.get(slug) ?? slug,
55
+ recordCount,
56
+ }))
57
+ .sort((left, right) => right.recordCount - left.recordCount || left.name.localeCompare(right.name));
58
+ }
59
+ /** Counts records per outcome, biggest first, ties broken by label. */
60
+ function countByOutcome(records) {
61
+ const counts = new Map();
62
+ for (const record of records) {
63
+ const key = record.outcomeCategory ?? '';
64
+ const existing = counts.get(key);
65
+ if (existing === undefined) {
66
+ counts.set(key, { outcome: record.outcomeCategory, recordCount: 1 });
67
+ }
68
+ else {
69
+ existing.recordCount += 1;
70
+ }
71
+ }
72
+ return [...counts.values()].sort((left, right) => right.recordCount - left.recordCount ||
73
+ (left.outcome ?? '').localeCompare(right.outcome ?? ''));
74
+ }
75
+ /**
76
+ * Parses the API responses behind one window into the counts the chart draws.
77
+ *
78
+ * The API writes one record per offence, so the counts come from the same read
79
+ * the page renders. Every check here is one the page would otherwise show as a
80
+ * wrong number: a month that came back empty, a record filed under a month
81
+ * other than the one it was asked for, or a crime type the category list does
82
+ * not carry all throw rather than quietly changing the totals.
83
+ *
84
+ * @param payload - the raw responses, or the committed fixture of them
85
+ * @returns the counts, with months oldest first
86
+ */
87
+ export function parsePoliceCrimeSummary(payload) {
88
+ const parsed = POLICE_RESPONSES_SCHEMA.safeParse(payload);
89
+ if (!parsed.success) {
90
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, parsed.error.message);
91
+ }
92
+ const { categories, months } = parsed.data;
93
+ if (months.length === 0) {
94
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, 'the window covers no months');
95
+ }
96
+ const namesBySlug = new Map();
97
+ for (const category of categories) {
98
+ if (!namesBySlug.has(category.url)) {
99
+ namesBySlug.set(category.url, category.name);
100
+ }
101
+ }
102
+ const oldestFirst = [...months].sort((left, right) => left.month.localeCompare(right.month));
103
+ for (const [index, entry] of oldestFirst.entries()) {
104
+ if (!POLICE_MONTH_PATTERN.test(entry.month)) {
105
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, `expected a month as YYYY-MM, got ${JSON.stringify(entry.month)}`);
106
+ }
107
+ const previous = oldestFirst[index - 1];
108
+ if (previous?.month === entry.month) {
109
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, `two answers for ${entry.month}`);
110
+ }
111
+ if (entry.records.length === 0) {
112
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, `the API returned no records for ${entry.month}`);
113
+ }
114
+ }
115
+ const allRecords = [];
116
+ const monthCounts = oldestFirst.map((entry) => {
117
+ const records = entry.records.map((record) => {
118
+ if (record.month !== entry.month) {
119
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, `a record filed under ${entry.month} carries the month ${record.month}`);
120
+ }
121
+ if (!namesBySlug.has(record.category)) {
122
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, `unrecognised crime type ${JSON.stringify(record.category)}`);
123
+ }
124
+ return {
125
+ category: record.category,
126
+ outcomeCategory: record.outcome_status?.category ?? null,
127
+ };
128
+ });
129
+ allRecords.push(...records);
130
+ return {
131
+ month: entry.month,
132
+ recordCount: records.length,
133
+ categoryCounts: countByCategory(records, namesBySlug),
134
+ };
135
+ });
136
+ const byMonthSize = [...monthCounts].sort((left, right) => right.recordCount - left.recordCount || left.month.localeCompare(right.month));
137
+ const categoryCounts = countByCategory(allRecords, namesBySlug);
138
+ const outcomeCounts = countByOutcome(allRecords);
139
+ const busiestMonth = byMonthSize[0];
140
+ const quietestMonth = byMonthSize.at(-1);
141
+ const topCategory = categoryCounts[0];
142
+ const topOutcome = outcomeCounts[0];
143
+ const firstMonth = monthCounts[0];
144
+ const latestMonth = monthCounts.at(-1);
145
+ if (busiestMonth === undefined ||
146
+ quietestMonth === undefined ||
147
+ topCategory === undefined ||
148
+ topOutcome === undefined ||
149
+ firstMonth === undefined ||
150
+ latestMonth === undefined) {
151
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, 'the window holds no counts');
152
+ }
153
+ return {
154
+ monthCount: monthCounts.length,
155
+ recordCount: allRecords.length,
156
+ firstMonth: firstMonth.month,
157
+ latestMonth: latestMonth.month,
158
+ categoryCounts,
159
+ outcomeCounts,
160
+ months: monthCounts,
161
+ busiestMonth,
162
+ quietestMonth,
163
+ topCategory,
164
+ topOutcome,
165
+ };
166
+ }
167
+ /**
168
+ * Builds the street-level crime URL for one point and one month.
169
+ *
170
+ * @param options - where to count, and which month to ask for
171
+ * @returns the full query URL
172
+ */
173
+ export function buildPoliceStreetCrimesUrl(options) {
174
+ const url = new URL(POLICE_STREET_CRIMES_URL);
175
+ url.searchParams.set('lat', String(options.latitude));
176
+ url.searchParams.set('lng', String(options.longitude));
177
+ url.searchParams.set('date', options.month);
178
+ return url.href;
179
+ }
180
+ /**
181
+ * Lists the months the API holds street-level crime for, newest first.
182
+ *
183
+ * @param options - an optional fetch implementation
184
+ * @returns the published months as YYYY-MM
185
+ */
186
+ export async function fetchPoliceCrimeMonths(options = {}) {
187
+ const { fetchImpl = globalThis.fetch } = options;
188
+ const response = await fetchImpl(POLICE_STREET_DATES_URL);
189
+ if (!response.ok) {
190
+ throw new UkSourceApiError(POLICE_CRIMES_SOURCE_ID, `HTTP ${response.status} listing the published months`);
191
+ }
192
+ const parsed = POLICE_DATES_SCHEMA.safeParse(await response.json());
193
+ if (!parsed.success) {
194
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, parsed.error.message);
195
+ }
196
+ const months = parsed.data
197
+ .map((entry) => entry.date)
198
+ .filter((month) => POLICE_MONTH_PATTERN.test(month));
199
+ if (months.length === 0) {
200
+ throw new UkSourceParseError(POLICE_CRIMES_SOURCE_ID, 'the API listed no published months');
201
+ }
202
+ return months;
203
+ }
204
+ /**
205
+ * Reads the crime types the API uses.
206
+ *
207
+ * @param options - an optional fetch implementation
208
+ * @returns one entry per crime type
209
+ */
210
+ export async function fetchPoliceCrimeCategories(options = {}) {
211
+ const { fetchImpl = globalThis.fetch } = options;
212
+ const response = await fetchImpl(POLICE_CRIME_CATEGORIES_URL);
213
+ if (!response.ok) {
214
+ throw new UkSourceApiError(POLICE_CRIMES_SOURCE_ID, `HTTP ${response.status} listing the crime types`);
215
+ }
216
+ return response.json();
217
+ }
218
+ /** One month's raw records, or a throw naming the month that failed. */
219
+ async function fetchPoliceStreetCrimeRecords(options) {
220
+ const url = buildPoliceStreetCrimesUrl(options);
221
+ const response = await options.fetchImpl(url);
222
+ if (!response.ok) {
223
+ throw new UkSourceApiError(POLICE_CRIMES_SOURCE_ID, `HTTP ${response.status} reading ${options.month}`);
224
+ }
225
+ return response.json();
226
+ }
227
+ /**
228
+ * Counts recorded crime within a mile of a point over the latest months.
229
+ *
230
+ * The months are read one at a time rather than in one burst, because the API
231
+ * is free to use and rate-limited per caller. A window of a year is 13 calls.
232
+ *
233
+ * @param options - where to count, how many months, and an optional fetch implementation
234
+ * @returns the counts for the window
235
+ */
236
+ export async function fetchPoliceCrimeSummary(options = {}) {
237
+ const { latitude = DEFAULT_POLICE_CRIME_LOCATION.latitude, longitude = DEFAULT_POLICE_CRIME_LOCATION.longitude, monthCount = DEFAULT_POLICE_CRIME_MONTH_COUNT, fetchImpl = globalThis.fetch, } = options;
238
+ const published = await fetchPoliceCrimeMonths({ fetchImpl });
239
+ const months = published.slice(0, monthCount);
240
+ const categories = await fetchPoliceCrimeCategories({ fetchImpl });
241
+ const responses = [];
242
+ for (const month of months) {
243
+ responses.push({
244
+ month,
245
+ records: await fetchPoliceStreetCrimeRecords({ latitude, longitude, month, fetchImpl }),
246
+ });
247
+ }
248
+ return parsePoliceCrimeSummary({ categories, months: responses });
249
+ }
250
+ /** Home Office street-level recorded crime, keyless. */
251
+ export const policeCrimesAdapter = {
252
+ id: POLICE_CRIMES_SOURCE_ID,
253
+ name: 'Home Office police.uk recorded crime',
254
+ auth: 'none',
255
+ description: 'Street-level crime recorded within a mile of a point, counted by crime type and by outcome.',
256
+ fetchLive: (options) => fetchPoliceCrimeSummary({
257
+ ...(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
258
+ }),
259
+ parse: parsePoliceCrimeSummary,
260
+ loadFixture: () => parsePoliceCrimeSummary(readFixtureJson('police-crimes.json')),
261
+ };
262
+ //# sourceMappingURL=policeCrimes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policeCrimes.js","sourceRoot":"","sources":["../src/policeCrimes.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;AAGhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,eAAe,CAAC;AAEvD,kDAAkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,4BAA4B,CAAC;AAEhE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,mBAAmB,0BAA0B,CAAC;AAEzF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,mBAAmB,sBAAsB,CAAC;AAEpF,sEAAsE;AACtE,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,mBAAmB,mBAAmB,CAAC;AAErF;;;GAGG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,CAAC;AAEnD,yEAAyE;AACzE,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,KAAK,EAAE,mBAAmB;IAC1B,QAAQ,EAAE,OAAO;IACjB,SAAS,EAAE,CAAC,MAAM;CACV,CAAC;AAqEX,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAE/E,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,mEAAmE;IACnE,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACzE,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC;IAC3C,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;CACzF,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;AAEpE,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;AAEvD,0EAA0E;AAC1E,SAAS,eAAe,CACtB,OAA+B,EAC/B,WAAgC;IAEhC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACzB,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI;QACJ,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI;QACnC,WAAW;KACZ,CAAC,CAAC;SACF,IAAI,CACH,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAC7F,CAAC;AACN,CAAC;AAED,uEAAuE;AACvE,SAAS,cAAc,CAAC,OAA6C;IACnE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;IACrD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,eAAe,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACd,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW;QACpC,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAC1D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAC3C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,6BAA6B,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7F,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,kBAAkB,CAC1B,uBAAuB,EACvB,oCAAoC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAClE,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACxC,IAAI,QAAQ,EAAE,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;YACpC,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,mBAAmB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,kBAAkB,CAC1B,uBAAuB,EACvB,mCAAmC,KAAK,CAAC,KAAK,EAAE,CACjD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAA2D,EAAE,CAAC;IAC9E,MAAM,WAAW,GAAuB,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAChE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YAC3C,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;gBACjC,MAAM,IAAI,kBAAkB,CAC1B,uBAAuB,EACvB,wBAAwB,KAAK,CAAC,KAAK,sBAAsB,MAAM,CAAC,KAAK,EAAE,CACxE,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,kBAAkB,CAC1B,uBAAuB,EACvB,2BAA2B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAC7D,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,QAAQ,IAAI,IAAI;aACzD,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC;QAC5B,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,WAAW,EAAE,OAAO,CAAC,MAAM;YAC3B,cAAc,EAAE,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC;SACtD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CACvC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAC/F,CAAC;IACF,MAAM,cAAc,GAAG,eAAe,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,aAAa,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,WAAW,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,IACE,YAAY,KAAK,SAAS;QAC1B,aAAa,KAAK,SAAS;QAC3B,WAAW,KAAK,SAAS;QACzB,UAAU,KAAK,SAAS;QACxB,UAAU,KAAK,SAAS;QACxB,WAAW,KAAK,SAAS,EACzB,CAAC;QACD,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,4BAA4B,CAAC,CAAC;IACtF,CAAC;IAED,OAAO;QACL,UAAU,EAAE,WAAW,CAAC,MAAM;QAC9B,WAAW,EAAE,UAAU,CAAC,MAAM;QAC9B,UAAU,EAAE,UAAU,CAAC,KAAK;QAC5B,WAAW,EAAE,WAAW,CAAC,KAAK;QAC9B,cAAc;QACd,aAAa;QACb,MAAM,EAAE,WAAW;QACnB,YAAY;QACZ,aAAa;QACb,WAAW;QACX,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAI1C;IACC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAC9C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACvD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,UAAmD,EAAE;IAErD,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,uBAAuB,EACvB,QAAQ,QAAQ,CAAC,MAAM,+BAA+B,CACvD,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACpE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI;SACvB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,EAAE,oCAAoC,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,UAAmD,EAAE;IAErD,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,2BAA2B,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,uBAAuB,EACvB,QAAQ,QAAQ,CAAC,MAAM,0BAA0B,CAClD,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,wEAAwE;AACxE,KAAK,UAAU,6BAA6B,CAAC,OAK5C;IACC,MAAM,GAAG,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,uBAAuB,EACvB,QAAQ,QAAQ,CAAC,MAAM,YAAY,OAAO,CAAC,KAAK,EAAE,CACnD,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,UAKI,EAAE;IAEN,MAAM,EACJ,QAAQ,GAAG,6BAA6B,CAAC,QAAQ,EACjD,SAAS,GAAG,6BAA6B,CAAC,SAAS,EACnD,UAAU,GAAG,gCAAgC,EAC7C,SAAS,GAAG,UAAU,CAAC,KAAK,GAC7B,GAAG,OAAO,CAAC;IAEZ,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,MAAM,0BAA0B,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IACnE,MAAM,SAAS,GAA0C,EAAE,CAAC;IAC5D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,SAAS,CAAC,IAAI,CAAC;YACb,KAAK;YACL,OAAO,EAAE,MAAM,6BAA6B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SACxF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,uBAAuB,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,MAAM,mBAAmB,GAAsC;IACpE,EAAE,EAAE,uBAAuB;IAC3B,IAAI,EAAE,sCAAsC;IAC5C,IAAI,EAAE,MAAM;IACZ,WAAW,EACT,6FAA6F;IAC/F,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CACrB,uBAAuB,CAAC;QACtB,GAAG,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;KAC9E,CAAC;IACJ,KAAK,EAAE,uBAAuB;IAC9B,WAAW,EAAE,GAAG,EAAE,CAAC,uBAAuB,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC;CAClF,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { UkDataAdapter, UkSourceProbe } from './types.js';
2
+ /** Every UK data source behind the uniform adapter interface. */
3
+ export declare const UK_DATA_SOURCES: UkDataAdapter<unknown>[];
4
+ /** Looks up a source adapter by id. */
5
+ export declare function getUkDataSource<T>(id: string): UkDataAdapter<T> | undefined;
6
+ /** Probes one source with a live fetch and reports the outcome. */
7
+ export declare function probeUkDataSource<T>(adapter: UkDataAdapter<T>, options?: {
8
+ apiKey?: string;
9
+ fetchImpl?: typeof globalThis.fetch;
10
+ }): Promise<UkSourceProbe>;
11
+ /** Probes every registered source in parallel, with optional per-source keys. */
12
+ export declare function probeAllUkDataSources(options?: {
13
+ apiKey?: string;
14
+ apiKeys?: Record<string, string>;
15
+ fetchImpl?: typeof globalThis.fetch;
16
+ }): Promise<UkSourceProbe[]>;
17
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAkB,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/E,iEAAiE;AACjE,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,OAAO,CAAC,EAYnD,CAAC;AAEF,uCAAuC;AACvC,wBAAgB,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAE3E;AAED,mEAAmE;AACnE,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAE,GACjE,OAAO,CAAC,aAAa,CAAC,CAwBxB;AAED,iFAAiF;AACjF,wBAAsB,qBAAqB,CAAC,OAAO,CAAC,EAAE;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAW3B"}
@@ -0,0 +1,67 @@
1
+ import { ancientWoodlandAdapter } from './ancientWoodland.js';
2
+ import { bankRateAdapter } from './bankRate.js';
3
+ import { carbonIntensityAdapter } from './carbonIntensity.js';
4
+ import { floodReadingsAdapter, floodStationsAdapter } from './floodMonitoring.js';
5
+ import { foodHygieneAuthoritiesAdapter } from './foodHygiene.js';
6
+ import { onsDatasetsAdapter } from './onsDatasets.js';
7
+ import { parliamentSeatsAdapter } from './parliamentSeats.js';
8
+ import { planningDatasetsAdapter } from './planningDatasets.js';
9
+ import { policeCrimesAdapter } from './policeCrimes.js';
10
+ import { tflBikePointsAdapter } from './tflBikePoints.js';
11
+ /** Every UK data source behind the uniform adapter interface. */
12
+ export const UK_DATA_SOURCES = [
13
+ floodStationsAdapter,
14
+ floodReadingsAdapter,
15
+ onsDatasetsAdapter,
16
+ foodHygieneAuthoritiesAdapter,
17
+ tflBikePointsAdapter,
18
+ planningDatasetsAdapter,
19
+ ancientWoodlandAdapter,
20
+ bankRateAdapter,
21
+ policeCrimesAdapter,
22
+ carbonIntensityAdapter,
23
+ parliamentSeatsAdapter,
24
+ ];
25
+ /** Looks up a source adapter by id. */
26
+ export function getUkDataSource(id) {
27
+ return UK_DATA_SOURCES.find((source) => source.id === id);
28
+ }
29
+ /** Probes one source with a live fetch and reports the outcome. */
30
+ export async function probeUkDataSource(adapter, options) {
31
+ try {
32
+ const fetchOptions = {
33
+ ...(options?.apiKey === undefined ? {} : { apiKey: options.apiKey }),
34
+ ...(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
35
+ };
36
+ const data = await adapter.fetchLive(fetchOptions);
37
+ return {
38
+ id: adapter.id,
39
+ name: adapter.name,
40
+ auth: adapter.auth,
41
+ ok: true,
42
+ status: 'ok',
43
+ sample: JSON.stringify(data).slice(0, 120),
44
+ };
45
+ }
46
+ catch (error) {
47
+ return {
48
+ id: adapter.id,
49
+ name: adapter.name,
50
+ auth: adapter.auth,
51
+ ok: false,
52
+ status: error instanceof Error ? error.message : String(error),
53
+ };
54
+ }
55
+ }
56
+ /** Probes every registered source in parallel, with optional per-source keys. */
57
+ export async function probeAllUkDataSources(options) {
58
+ const { apiKey, apiKeys, fetchImpl } = options ?? {};
59
+ return Promise.all(UK_DATA_SOURCES.map((source) => {
60
+ const key = apiKeys?.[source.id] ?? apiKey;
61
+ return probeUkDataSource(source, {
62
+ ...(key === undefined ? {} : { apiKey: key }),
63
+ ...(fetchImpl === undefined ? {} : { fetchImpl }),
64
+ });
65
+ }));
66
+ }
67
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { UkDataAdapter } from './types.js';
2
+ /**
3
+ * Transport for London's Santander Cycles docking station list. Keyless: the
4
+ * endpoint answers without an app key, and TfL asks for one only above the
5
+ * free rate limit. Published as TfL Open Data.
6
+ */
7
+ export declare const TFL_BIKE_POINTS_URL = "https://api.tfl.gov.uk/BikePoint";
8
+ /** How many of the largest docking stations the summary keeps by default. */
9
+ export declare const DEFAULT_LARGEST_STATION_LIMIT = 10;
10
+ /** One Santander Cycles docking station, as TfL lists it. */
11
+ export interface DockingStation {
12
+ id: string;
13
+ /** TfL's own station name, with the spacing around commas tidied. */
14
+ name: string;
15
+ /** Docking points the station has, empty or full. */
16
+ dockCount: number;
17
+ /** Bikes docked at the time of the call. */
18
+ bikeCount: number;
19
+ /** Electric bikes among the docked bikes. */
20
+ electricBikeCount: number;
21
+ emptyDockCount: number;
22
+ /** True for TfL's pop-up stations, which come and go with events. */
23
+ temporary: boolean;
24
+ }
25
+ /** Rolled-up counts across every docking station TfL lists. */
26
+ export interface DockingStationSummary {
27
+ stationCount: number;
28
+ /** Docking points summed across every station. */
29
+ dockCount: number;
30
+ bikeCount: number;
31
+ electricBikeCount: number;
32
+ /** The stations with the most docking points, largest first. */
33
+ largestStations: DockingStation[];
34
+ }
35
+ /**
36
+ * Parses a TfL BikePoint payload into docking stations.
37
+ *
38
+ * Stations TfL has marked for removal are dropped: they still appear in the
39
+ * list after they close, and they carry no docking points to count.
40
+ *
41
+ * @param payload - the raw JSON body from the BikePoint endpoint
42
+ * @returns one docking station per live station in the payload
43
+ */
44
+ export declare function parseTflBikePoints(payload: unknown): DockingStation[];
45
+ /**
46
+ * Totals the network and keeps the largest stations.
47
+ *
48
+ * @param stations - stations from {@link parseTflBikePoints}
49
+ * @param largestStationLimit - how many stations to keep in `largestStations`
50
+ * @returns station, dock, and bike counts plus the largest stations
51
+ */
52
+ export declare function summarizeTflBikePoints(stations: DockingStation[], largestStationLimit?: number): DockingStationSummary;
53
+ /**
54
+ * Lists every Santander Cycles docking station from TfL.
55
+ *
56
+ * @param options - an optional app key and fetch implementation
57
+ * @returns the docking stations the endpoint returns
58
+ */
59
+ export declare function fetchTflBikePoints(options?: {
60
+ apiKey?: string;
61
+ fetchImpl?: typeof globalThis.fetch;
62
+ }): Promise<DockingStation[]>;
63
+ /** Transport for London Santander Cycles docking stations, keyless. */
64
+ export declare const tflBikePointsAdapter: UkDataAdapter<DockingStation[]>;
65
+ //# sourceMappingURL=tflBikePoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tflBikePoints.d.ts","sourceRoot":"","sources":["../src/tflBikePoints.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,qCAAqC,CAAC;AAEtE,6EAA6E;AAC7E,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,+DAA+D;AAC/D,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,eAAe,EAAE,cAAc,EAAE,CAAC;CACnC;AAwED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,EAAE,CAwBrE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,cAAc,EAAE,EAC1B,mBAAmB,GAAE,MAAsC,GAC1D,qBAAqB,CAYvB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAO,GACrE,OAAO,CAAC,cAAc,EAAE,CAAC,CAc3B;AAED,uEAAuE;AACvE,eAAO,MAAM,oBAAoB,EAAE,aAAa,CAAC,cAAc,EAAE,CAYhE,CAAC"}
@@ -0,0 +1,149 @@
1
+ import { z } from 'zod';
2
+ import { UkSourceApiError, UkSourceParseError } from './errors.js';
3
+ import { readFixtureJson } from './fixtures.js';
4
+ /**
5
+ * Transport for London's Santander Cycles docking station list. Keyless: the
6
+ * endpoint answers without an app key, and TfL asks for one only above the
7
+ * free rate limit. Published as TfL Open Data.
8
+ */
9
+ export const TFL_BIKE_POINTS_URL = 'https://api.tfl.gov.uk/BikePoint';
10
+ /** How many of the largest docking stations the summary keeps by default. */
11
+ export const DEFAULT_LARGEST_STATION_LIMIT = 10;
12
+ const TFL_PLACE_SCHEMA = z.object({
13
+ id: z.string(),
14
+ commonName: z.string(),
15
+ additionalProperties: z.array(z.object({
16
+ key: z.string(),
17
+ value: z.string(),
18
+ })),
19
+ });
20
+ const TFL_PLACES_SCHEMA = z.array(TFL_PLACE_SCHEMA);
21
+ /** The station fields this adapter reads out of TfL's key/value property list. */
22
+ const DOCK_COUNT_KEY = 'NbDocks';
23
+ const BIKE_COUNT_KEY = 'NbBikes';
24
+ const ELECTRIC_BIKE_COUNT_KEY = 'NbEBikes';
25
+ const EMPTY_DOCK_COUNT_KEY = 'NbEmptyDocks';
26
+ const TEMPORARY_KEY = 'Temporary';
27
+ const REMOVAL_DATE_KEY = 'RemovalDate';
28
+ /**
29
+ * Indexes TfL's key/value property list for one station.
30
+ *
31
+ * @param station - one place from the BikePoint payload
32
+ * @returns the station's properties by key
33
+ */
34
+ function readProperties(station) {
35
+ return new Map(station.additionalProperties.map((property) => [property.key, property.value]));
36
+ }
37
+ /**
38
+ * Reads one numeric property, naming the station when it is missing.
39
+ *
40
+ * @param properties - the station's properties
41
+ * @param key - the property key to read
42
+ * @param stationName - the station name, for the error message
43
+ * @returns the property as a number
44
+ */
45
+ function readCountProperty(properties, key, stationName) {
46
+ const raw = properties.get(key);
47
+ if (raw === undefined || raw.trim() === '') {
48
+ throw new UkSourceParseError('tfl-bike-points', `no ${key} on ${stationName}`);
49
+ }
50
+ const value = Number(raw);
51
+ if (!Number.isFinite(value)) {
52
+ throw new UkSourceParseError('tfl-bike-points', `${key} "${raw}" on ${stationName} is not a number`);
53
+ }
54
+ return value;
55
+ }
56
+ /**
57
+ * Tidies the spacing TfL leaves around commas in station names, so
58
+ * "River Street , Clerkenwell" reads as "River Street, Clerkenwell". The words
59
+ * are TfL's own.
60
+ *
61
+ * @param name - the station name as TfL publishes it
62
+ * @returns the name with collapsed spaces and tidy commas
63
+ */
64
+ function tidyStationName(name) {
65
+ return name.replace(/\s+/g, ' ').replace(/\s+,/g, ',').trim();
66
+ }
67
+ /**
68
+ * Parses a TfL BikePoint payload into docking stations.
69
+ *
70
+ * Stations TfL has marked for removal are dropped: they still appear in the
71
+ * list after they close, and they carry no docking points to count.
72
+ *
73
+ * @param payload - the raw JSON body from the BikePoint endpoint
74
+ * @returns one docking station per live station in the payload
75
+ */
76
+ export function parseTflBikePoints(payload) {
77
+ const parsed = TFL_PLACES_SCHEMA.safeParse(payload);
78
+ if (!parsed.success) {
79
+ throw new UkSourceParseError('tfl-bike-points', parsed.error.message);
80
+ }
81
+ const stations = [];
82
+ for (const place of parsed.data) {
83
+ const name = tidyStationName(place.commonName);
84
+ const properties = readProperties(place);
85
+ if ((properties.get(REMOVAL_DATE_KEY) ?? '').trim() !== '') {
86
+ continue;
87
+ }
88
+ stations.push({
89
+ id: place.id,
90
+ name,
91
+ dockCount: readCountProperty(properties, DOCK_COUNT_KEY, name),
92
+ bikeCount: readCountProperty(properties, BIKE_COUNT_KEY, name),
93
+ electricBikeCount: readCountProperty(properties, ELECTRIC_BIKE_COUNT_KEY, name),
94
+ emptyDockCount: readCountProperty(properties, EMPTY_DOCK_COUNT_KEY, name),
95
+ temporary: properties.get(TEMPORARY_KEY) === 'true',
96
+ });
97
+ }
98
+ return stations;
99
+ }
100
+ /**
101
+ * Totals the network and keeps the largest stations.
102
+ *
103
+ * @param stations - stations from {@link parseTflBikePoints}
104
+ * @param largestStationLimit - how many stations to keep in `largestStations`
105
+ * @returns station, dock, and bike counts plus the largest stations
106
+ */
107
+ export function summarizeTflBikePoints(stations, largestStationLimit = DEFAULT_LARGEST_STATION_LIMIT) {
108
+ const largestStations = [...stations]
109
+ .sort((left, right) => right.dockCount - left.dockCount || left.name.localeCompare(right.name))
110
+ .slice(0, largestStationLimit);
111
+ return {
112
+ stationCount: stations.length,
113
+ dockCount: stations.reduce((total, station) => total + station.dockCount, 0),
114
+ bikeCount: stations.reduce((total, station) => total + station.bikeCount, 0),
115
+ electricBikeCount: stations.reduce((total, station) => total + station.electricBikeCount, 0),
116
+ largestStations,
117
+ };
118
+ }
119
+ /**
120
+ * Lists every Santander Cycles docking station from TfL.
121
+ *
122
+ * @param options - an optional app key and fetch implementation
123
+ * @returns the docking stations the endpoint returns
124
+ */
125
+ export async function fetchTflBikePoints(options = {}) {
126
+ const { apiKey, fetchImpl = globalThis.fetch } = options;
127
+ const url = apiKey === undefined
128
+ ? TFL_BIKE_POINTS_URL
129
+ : `${TFL_BIKE_POINTS_URL}?app_key=${encodeURIComponent(apiKey)}`;
130
+ const response = await fetchImpl(url);
131
+ if (!response.ok) {
132
+ throw new UkSourceApiError('tfl-bike-points', `HTTP ${response.status} listing docking stations`);
133
+ }
134
+ return parseTflBikePoints(await response.json());
135
+ }
136
+ /** Transport for London Santander Cycles docking stations, keyless. */
137
+ export const tflBikePointsAdapter = {
138
+ id: 'tfl-bike-points',
139
+ name: 'Transport for London Santander Cycles docking stations',
140
+ auth: 'none',
141
+ description: 'Every Santander Cycles docking station, with its docking points and docked bikes.',
142
+ fetchLive: (options) => fetchTflBikePoints({
143
+ ...(options?.apiKey === undefined ? {} : { apiKey: options.apiKey }),
144
+ ...(options?.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
145
+ }),
146
+ parse: parseTflBikePoints,
147
+ loadFixture: () => parseTflBikePoints(readFixtureJson('tfl-bike-points.json')),
148
+ };
149
+ //# sourceMappingURL=tflBikePoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tflBikePoints.js","sourceRoot":"","sources":["../src/tflBikePoints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /** What a UK source needs before it will answer a request. */
2
+ export type UkSourceAuth = 'none' | 'key';
3
+ /** Options shared by every adapter's live fetch. */
4
+ export interface UkFetchOptions {
5
+ apiKey?: string;
6
+ fetchImpl?: typeof globalThis.fetch;
7
+ }
8
+ /**
9
+ * One UK data source behind a uniform interface: a live fetch, a strict
10
+ * parse, and a committed fixture fallback so builds work offline.
11
+ */
12
+ export interface UkDataAdapter<T> {
13
+ readonly id: string;
14
+ readonly name: string;
15
+ readonly auth: UkSourceAuth;
16
+ readonly description: string;
17
+ fetchLive(options?: UkFetchOptions): Promise<T>;
18
+ parse(payload: unknown): T;
19
+ loadFixture(): T;
20
+ }
21
+ /** Result of a live verification probe against one source. */
22
+ export interface UkSourceProbe {
23
+ id: string;
24
+ name: string;
25
+ auth: UkSourceAuth;
26
+ ok: boolean;
27
+ status: string;
28
+ sample?: string;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1C,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,CAAC;IAC3B,WAAW,IAAI,CAAC,CAAC;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}