11import { type BBox , fetchJson } from "@openmapx/core" ;
22import type { CacheClient } from "@openmapx/integration-framework" ;
33import { COUNTRY_BBOXES } from "./country-bboxes" ;
4+ import {
5+ TRANSPORT_APIS_COMMIT ,
6+ TRANSPORT_APIS_GITHUB_TREE_URL ,
7+ TRANSPORT_APIS_JSDELIVR_CDN_BASE ,
8+ TRANSPORT_APIS_JSDELIVR_PKG_URL ,
9+ TRANSPORT_APIS_RAW_BASE ,
10+ } from "./pin" ;
411import type { CoverageTier , ProtocolType , RegistryEntry } from "./registry-types" ;
12+ import { registryEndpointRejection } from "./validate-endpoint" ;
513
6- // JSDelivr CDN — mirrors GitHub without API rate limits
7- // @HEAD resolves to the repo's default branch (no releases/tags needed)
8- const JSDELIVR_PKG_URL =
9- "https://data.jsdelivr.com/v1/packages/gh/public-transport/transport-apis@HEAD" ;
10- const JSDELIVR_CDN_BASE = "https://cdn.jsdelivr.net/gh/public-transport/transport-apis@HEAD" ;
11- // GitHub API fallback
12- const GITHUB_TREE_URL =
13- "https://api.github.com/repos/public-transport/transport-apis/git/trees/v1?recursive=1" ;
14- const RAW_BASE = "https://raw.githubusercontent.com/public-transport/transport-apis/v1" ;
1514const REGISTRY_CACHE_KEY = "transit:registry" ;
1615const REGISTRY_CACHE_TTL = 172800 ; // 48 hours
1716const MAX_CONCURRENT = 10 ;
@@ -134,10 +133,31 @@ function parseCoverageTier(
134133}
135134
136135// biome-ignore lint/suspicious/noExplicitAny: external JSON
137- function parseEntry ( path : string , json : any ) : RegistryEntry | null {
138- const protocol = parseProtocol ( json . type ?? { } ) ;
136+ export function parseEntry ( path : string , json : any ) : RegistryEntry | null {
137+ if ( ! json || typeof json !== "object" || Array . isArray ( json ) ) return null ;
138+
139+ const type = json . type ;
140+ const protocol = parseProtocol (
141+ type && typeof type === "object" && ! Array . isArray ( type ) ? type : { } ,
142+ ) ;
139143 if ( ! protocol ) return null ;
140144
145+ const rawOptions = json . options ;
146+ if (
147+ rawOptions !== undefined &&
148+ rawOptions !== null &&
149+ ( typeof rawOptions !== "object" || Array . isArray ( rawOptions ) )
150+ ) {
151+ console . warn ( `[transit-registry] Dropping ${ path } : options rejected (not-an-object)` ) ;
152+ return null ;
153+ }
154+ const options = ( rawOptions ?? { } ) as Record < string , unknown > ;
155+ const rejection = registryEndpointRejection ( options ) ;
156+ if ( rejection ) {
157+ console . warn ( `[transit-registry] Dropping ${ path } : endpoint rejected (${ rejection } )` ) ;
158+ return null ;
159+ }
160+
141161 const id = idFromPath ( path ) ;
142162 const slug = slugFromPath ( path ) ;
143163 const prefix = `${ slug } :` ;
@@ -171,7 +191,7 @@ function parseEntry(path: string, json: any): RegistryEntry | null {
171191 protocol,
172192 supportedLanguages : json . supportedLanguages ?? [ ] ,
173193 timezone : json . timezone ,
174- options : json . options ?? { } ,
194+ options,
175195 coverage : { bbox, tiers } ,
176196 attribution : json . attribution
177197 ? {
@@ -232,21 +252,21 @@ function collectDataPaths(
232252 return [ ...new Set ( paths ) ] ;
233253}
234254
235- /** Fetch file listing via JSDelivr @HEAD (no auth, no rate limits). */
255+ /** Fetch file listing via jsDelivr (no auth, no rate limits). */
236256async function fetchPathsFromJsdelivr ( ) : Promise < string [ ] > {
237- const json = await fetchJson < JsDelivrFile > ( JSDELIVR_PKG_URL , {
257+ const json = await fetchJson < JsDelivrFile > ( TRANSPORT_APIS_JSDELIVR_PKG_URL , {
238258 timeoutMs : 15_000 ,
239- headers : githubAuthHeaders ( JSDELIVR_PKG_URL ) ,
259+ headers : githubAuthHeaders ( TRANSPORT_APIS_JSDELIVR_PKG_URL ) ,
240260 errorMessage : ( { status } ) => `JSDelivr listing: ${ status } ` ,
241261 } ) ;
242262 return collectDataPaths ( json ) ;
243263}
244264
245265/** Fetch file listing via GitHub Tree API (requires GITHUB_TOKEN for reliable access). */
246266async function fetchPathsFromGithub ( ) : Promise < string [ ] > {
247- const tree = await fetchJson < { tree : GitTreeEntry [ ] } > ( GITHUB_TREE_URL , {
267+ const tree = await fetchJson < { tree : GitTreeEntry [ ] } > ( TRANSPORT_APIS_GITHUB_TREE_URL , {
248268 timeoutMs : 15_000 ,
249- headers : githubAuthHeaders ( GITHUB_TREE_URL ) ,
269+ headers : githubAuthHeaders ( TRANSPORT_APIS_GITHUB_TREE_URL ) ,
250270 errorMessage : ( { status } ) => `GitHub tree: ${ status } ` ,
251271 } ) ;
252272 return tree . tree
@@ -281,11 +301,13 @@ async function fetchInBatchesFrom(paths: string[], baseUrl: string): Promise<Reg
281301export async function fetchRegistryEntries ( ) : Promise < RegistryEntry [ ] > {
282302 let entries : RegistryEntry [ ] | null = null ;
283303
284- // 1. Primary: JSDelivr CDN (@HEAD — no auth, no rate limits, no GitHub API calls)
304+ // 1. Primary: jsDelivr CDN (no auth, no rate limits, no GitHub API calls)
285305 try {
286306 const paths = await fetchPathsFromJsdelivr ( ) ;
287- entries = await fetchInBatchesFrom ( paths , JSDELIVR_CDN_BASE ) ;
288- console . log ( `[transit-registry] ${ entries . length } entries loaded (JSDelivr)` ) ;
307+ entries = await fetchInBatchesFrom ( paths , TRANSPORT_APIS_JSDELIVR_CDN_BASE ) ;
308+ console . log (
309+ `[transit-registry] ${ entries . length } entries loaded (JSDelivr @ ${ TRANSPORT_APIS_COMMIT . slice ( 0 , 12 ) } )` ,
310+ ) ;
289311 } catch ( err ) {
290312 console . warn ( "[transit-registry] JSDelivr unavailable, trying GitHub API:" , err ) ;
291313 }
@@ -294,8 +316,10 @@ export async function fetchRegistryEntries(): Promise<RegistryEntry[]> {
294316 if ( ! entries ) {
295317 try {
296318 const paths = await fetchPathsFromGithub ( ) ;
297- entries = await fetchInBatchesFrom ( paths , RAW_BASE ) ;
298- console . log ( `[transit-registry] ${ entries . length } entries loaded (GitHub API)` ) ;
319+ entries = await fetchInBatchesFrom ( paths , TRANSPORT_APIS_RAW_BASE ) ;
320+ console . log (
321+ `[transit-registry] ${ entries . length } entries loaded (GitHub API @ ${ TRANSPORT_APIS_COMMIT . slice ( 0 , 12 ) } )` ,
322+ ) ;
299323 } catch ( err ) {
300324 console . warn ( "[transit-registry] GitHub API unavailable, trying cache:" , err ) ;
301325 }
0 commit comments