lt-public-transport-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/assets/architecture.png +0 -0
- package/dist/config.d.ts +221 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +200 -0
- package/dist/config.js.map +1 -0
- package/dist/enrichment/index.d.ts +6 -0
- package/dist/enrichment/index.d.ts.map +1 -0
- package/dist/enrichment/index.js +6 -0
- package/dist/enrichment/index.js.map +1 -0
- package/dist/enrichment/route-matcher.d.ts +64 -0
- package/dist/enrichment/route-matcher.d.ts.map +1 -0
- package/dist/enrichment/route-matcher.js +121 -0
- package/dist/enrichment/route-matcher.js.map +1 -0
- package/dist/errors.d.ts +70 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +104 -0
- package/dist/errors.js.map +1 -0
- package/dist/gtfs/index.d.ts +7 -0
- package/dist/gtfs/index.d.ts.map +1 -0
- package/dist/gtfs/index.js +7 -0
- package/dist/gtfs/index.js.map +1 -0
- package/dist/gtfs/parser.d.ts +39 -0
- package/dist/gtfs/parser.d.ts.map +1 -0
- package/dist/gtfs/parser.js +189 -0
- package/dist/gtfs/parser.js.map +1 -0
- package/dist/gtfs/sync.d.ts +72 -0
- package/dist/gtfs/sync.d.ts.map +1 -0
- package/dist/gtfs/sync.js +271 -0
- package/dist/gtfs/sync.js.map +1 -0
- package/dist/index.d.ts +203 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +342 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/gps-full.d.ts +39 -0
- package/dist/parsers/gps-full.d.ts.map +1 -0
- package/dist/parsers/gps-full.js +212 -0
- package/dist/parsers/gps-full.js.map +1 -0
- package/dist/parsers/gps-lite.d.ts +60 -0
- package/dist/parsers/gps-lite.d.ts.map +1 -0
- package/dist/parsers/gps-lite.js +141 -0
- package/dist/parsers/gps-lite.js.map +1 -0
- package/dist/parsers/index.d.ts +7 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +7 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/schemas.d.ts +129 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +200 -0
- package/dist/schemas.js.map +1 -0
- package/dist/scripts/test-city-specific.d.ts +2 -0
- package/dist/scripts/test-city-specific.d.ts.map +1 -0
- package/dist/scripts/test-city-specific.js +264 -0
- package/dist/scripts/test-city-specific.js.map +1 -0
- package/dist/scripts/test-config-options.d.ts +2 -0
- package/dist/scripts/test-config-options.d.ts.map +1 -0
- package/dist/scripts/test-config-options.js +166 -0
- package/dist/scripts/test-config-options.js.map +1 -0
- package/dist/scripts/test-data-quality.d.ts +2 -0
- package/dist/scripts/test-data-quality.d.ts.map +1 -0
- package/dist/scripts/test-data-quality.js +204 -0
- package/dist/scripts/test-data-quality.js.map +1 -0
- package/dist/scripts/test-error-handling.d.ts +2 -0
- package/dist/scripts/test-error-handling.d.ts.map +1 -0
- package/dist/scripts/test-error-handling.js +146 -0
- package/dist/scripts/test-error-handling.js.map +1 -0
- package/dist/scripts/test-live.d.ts +2 -0
- package/dist/scripts/test-live.d.ts.map +1 -0
- package/dist/scripts/test-live.js +121 -0
- package/dist/scripts/test-live.js.map +1 -0
- package/dist/types.d.ts +120 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/coordinates.d.ts +68 -0
- package/dist/utils/coordinates.d.ts.map +1 -0
- package/dist/utils/coordinates.js +98 -0
- package/dist/utils/coordinates.js.map +1 -0
- package/dist/utils/encoding.d.ts +47 -0
- package/dist/utils/encoding.d.ts.map +1 -0
- package/dist/utils/encoding.js +153 -0
- package/dist/utils/encoding.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/time.d.ts +50 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +94 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/gtfs/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAe,MAAM,aAAa,CAAC;AAoE5D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAsDtE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,CAiDzD"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTFS file parsers for routes.txt and stops.txt
|
|
3
|
+
* @module gtfs/parser
|
|
4
|
+
*/
|
|
5
|
+
import { GTFS_ROUTE_TYPE_MAP } from '../types.js';
|
|
6
|
+
import { cleanTextField } from '../utils/index.js';
|
|
7
|
+
import { gtfsRouteSchema, gtfsStopSchema } from '../schemas.js';
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// CSV Parsing Utilities
|
|
10
|
+
// =============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Parse a CSV line handling quoted fields.
|
|
13
|
+
*/
|
|
14
|
+
function parseCSVLine(line) {
|
|
15
|
+
const result = [];
|
|
16
|
+
let current = '';
|
|
17
|
+
let inQuotes = false;
|
|
18
|
+
for (let i = 0; i < line.length; i++) {
|
|
19
|
+
const char = line[i];
|
|
20
|
+
if (char === '"') {
|
|
21
|
+
if (inQuotes && line[i + 1] === '"') {
|
|
22
|
+
// Escaped quote
|
|
23
|
+
current += '"';
|
|
24
|
+
i++;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
inQuotes = !inQuotes;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (char === ',' && !inQuotes) {
|
|
31
|
+
result.push(current.trim());
|
|
32
|
+
current = '';
|
|
33
|
+
}
|
|
34
|
+
else if (char !== undefined) {
|
|
35
|
+
current += char;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
result.push(current.trim());
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a header-to-index map from CSV header row.
|
|
43
|
+
*/
|
|
44
|
+
function buildHeaderMap(headers) {
|
|
45
|
+
const map = new Map();
|
|
46
|
+
headers.forEach((header, index) => {
|
|
47
|
+
map.set(header.trim(), index);
|
|
48
|
+
});
|
|
49
|
+
return map;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build a key-value object from a row using header map.
|
|
53
|
+
*/
|
|
54
|
+
function buildRowObject(row, headerMap) {
|
|
55
|
+
const obj = {};
|
|
56
|
+
for (const [header, index] of headerMap.entries()) {
|
|
57
|
+
if (index < row.length) {
|
|
58
|
+
obj[header] = row[index]?.trim() ?? '';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return obj;
|
|
62
|
+
}
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// Routes Parser
|
|
65
|
+
// =============================================================================
|
|
66
|
+
/**
|
|
67
|
+
* Parse routes.txt content into a Map keyed by route short name.
|
|
68
|
+
*
|
|
69
|
+
* GTFS routes.txt fields:
|
|
70
|
+
* - route_id: Unique identifier
|
|
71
|
+
* - agency_id: Agency reference
|
|
72
|
+
* - route_short_name: Short name (e.g., "4G", "N1")
|
|
73
|
+
* - route_long_name: Full name with endpoints
|
|
74
|
+
* - route_desc: Description
|
|
75
|
+
* - route_type: GTFS route type (3=bus, 800=trolleybus)
|
|
76
|
+
* - route_url: URL
|
|
77
|
+
* - route_color: Background color (hex)
|
|
78
|
+
* - route_text_color: Text color (hex)
|
|
79
|
+
*
|
|
80
|
+
* @param content - Raw routes.txt content
|
|
81
|
+
* @returns Map from route short name to Route object
|
|
82
|
+
*/
|
|
83
|
+
export function parseRoutesContent(content) {
|
|
84
|
+
const lines = content.split('\n').filter(line => line.trim());
|
|
85
|
+
if (lines.length < 2) {
|
|
86
|
+
return new Map();
|
|
87
|
+
}
|
|
88
|
+
const firstLine = lines[0];
|
|
89
|
+
if (firstLine === undefined || firstLine === '') {
|
|
90
|
+
return new Map();
|
|
91
|
+
}
|
|
92
|
+
const headers = parseCSVLine(firstLine);
|
|
93
|
+
const headerMap = buildHeaderMap(headers);
|
|
94
|
+
const routes = new Map();
|
|
95
|
+
for (let i = 1; i < lines.length; i++) {
|
|
96
|
+
const line = lines[i];
|
|
97
|
+
if (line === undefined || line === '')
|
|
98
|
+
continue;
|
|
99
|
+
const row = parseCSVLine(line);
|
|
100
|
+
try {
|
|
101
|
+
// Build object from row for Zod validation
|
|
102
|
+
const rowObject = buildRowObject(row, headerMap);
|
|
103
|
+
const parseResult = gtfsRouteSchema.safeParse(rowObject);
|
|
104
|
+
if (!parseResult.success) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const validated = parseResult.data;
|
|
108
|
+
const type = GTFS_ROUTE_TYPE_MAP[validated.route_type] ?? 'unknown';
|
|
109
|
+
const routeObj = {
|
|
110
|
+
id: validated.route_id,
|
|
111
|
+
shortName: cleanTextField(validated.route_short_name),
|
|
112
|
+
longName: cleanTextField(validated.route_long_name),
|
|
113
|
+
type,
|
|
114
|
+
color: validated.route_color.replace('#', ''),
|
|
115
|
+
textColor: validated.route_text_color.replace('#', ''),
|
|
116
|
+
};
|
|
117
|
+
// Key by short name for fast lookup from GPS data
|
|
118
|
+
routes.set(routeObj.shortName, routeObj);
|
|
119
|
+
// Also key by route_id for GTFS trip references
|
|
120
|
+
routes.set(routeObj.id, routeObj);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Skip malformed rows
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return routes;
|
|
128
|
+
}
|
|
129
|
+
// =============================================================================
|
|
130
|
+
// Stops Parser
|
|
131
|
+
// =============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Parse stops.txt content into an array of Stop objects.
|
|
134
|
+
*
|
|
135
|
+
* GTFS stops.txt fields:
|
|
136
|
+
* - stop_id: Unique identifier
|
|
137
|
+
* - stop_code: Short code
|
|
138
|
+
* - stop_name: Human-readable name
|
|
139
|
+
* - stop_desc: Description
|
|
140
|
+
* - stop_lat: Latitude
|
|
141
|
+
* - stop_lon: Longitude
|
|
142
|
+
*
|
|
143
|
+
* @param content - Raw stops.txt content
|
|
144
|
+
* @returns Array of Stop objects
|
|
145
|
+
*/
|
|
146
|
+
export function parseStopsContent(content) {
|
|
147
|
+
const lines = content.split('\n').filter(line => line.trim());
|
|
148
|
+
if (lines.length < 2) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
const firstLine = lines[0];
|
|
152
|
+
if (firstLine === undefined || firstLine === '') {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
const headers = parseCSVLine(firstLine);
|
|
156
|
+
const headerMap = buildHeaderMap(headers);
|
|
157
|
+
const stops = [];
|
|
158
|
+
for (let i = 1; i < lines.length; i++) {
|
|
159
|
+
const line = lines[i];
|
|
160
|
+
if (line === undefined || line === '')
|
|
161
|
+
continue;
|
|
162
|
+
const row = parseCSVLine(line);
|
|
163
|
+
try {
|
|
164
|
+
// Build object from row for Zod validation
|
|
165
|
+
const rowObject = buildRowObject(row, headerMap);
|
|
166
|
+
const parseResult = gtfsStopSchema.safeParse(rowObject);
|
|
167
|
+
if (!parseResult.success) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const validated = parseResult.data;
|
|
171
|
+
stops.push({
|
|
172
|
+
id: validated.stop_id,
|
|
173
|
+
code: validated.stop_code ?? null,
|
|
174
|
+
name: cleanTextField(validated.stop_name),
|
|
175
|
+
description: validated.stop_desc !== undefined && validated.stop_desc !== ''
|
|
176
|
+
? cleanTextField(validated.stop_desc)
|
|
177
|
+
: null,
|
|
178
|
+
latitude: validated.stop_lat,
|
|
179
|
+
longitude: validated.stop_lon,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// Skip malformed rows
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return stops;
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/gtfs/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTFS sync and caching module
|
|
3
|
+
* @module gtfs/sync
|
|
4
|
+
*
|
|
5
|
+
* Handles downloading, extracting, and caching GTFS data from stops.lt.
|
|
6
|
+
* Uses yauzl-promise for proper ZIP extraction.
|
|
7
|
+
*/
|
|
8
|
+
import type { CityId, Route, Stop, SyncResult } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* GTFS cache metadata.
|
|
11
|
+
*/
|
|
12
|
+
interface CacheMeta {
|
|
13
|
+
/** Last-Modified header from server */
|
|
14
|
+
lastModified: string | null;
|
|
15
|
+
/** When cache was synced */
|
|
16
|
+
syncedAt: string;
|
|
17
|
+
/** Number of routes in cache */
|
|
18
|
+
routeCount: number;
|
|
19
|
+
/** Number of stops in cache */
|
|
20
|
+
stopCount: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Cached GTFS data for a city.
|
|
24
|
+
*/
|
|
25
|
+
export interface GtfsCache {
|
|
26
|
+
readonly meta: CacheMeta;
|
|
27
|
+
readonly routes: Map<string, Route>;
|
|
28
|
+
readonly stops: Stop[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Options for GTFS sync.
|
|
32
|
+
*/
|
|
33
|
+
export interface SyncOptions {
|
|
34
|
+
/** Directory for caching GTFS data */
|
|
35
|
+
cacheDir?: string;
|
|
36
|
+
/** Request timeout in ms (default: 30000) */
|
|
37
|
+
timeout?: number;
|
|
38
|
+
/** User-Agent header for requests */
|
|
39
|
+
userAgent?: string;
|
|
40
|
+
/** Force re-download even if cache is current */
|
|
41
|
+
force?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load cached routes for a city.
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadCachedRoutes(cacheDir: string, city: CityId): Promise<Map<string, Route> | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Load cached stops for a city.
|
|
49
|
+
*/
|
|
50
|
+
export declare function loadCachedStops(cacheDir: string, city: CityId): Promise<Stop[] | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Sync GTFS data for a city.
|
|
53
|
+
*
|
|
54
|
+
* Downloads the GTFS ZIP archive if newer than cached version,
|
|
55
|
+
* extracts routes.txt and stops.txt, and caches the parsed data.
|
|
56
|
+
*
|
|
57
|
+
* @param city - City to sync
|
|
58
|
+
* @param options - Sync options
|
|
59
|
+
* @returns Sync result
|
|
60
|
+
*/
|
|
61
|
+
export declare function syncGtfs(city: CityId, options?: SyncOptions): Promise<SyncResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Load GTFS cache for a city.
|
|
64
|
+
* Returns null if not cached.
|
|
65
|
+
*
|
|
66
|
+
* @param city - City to load
|
|
67
|
+
* @param cacheDir - Cache directory
|
|
68
|
+
* @returns Cached GTFS data or null
|
|
69
|
+
*/
|
|
70
|
+
export declare function loadGtfsCache(city: CityId, cacheDir?: string): Promise<GtfsCache | null>;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/gtfs/sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASnE;;GAEG;AACH,UAAU,SAAS;IACjB,uCAAuC;IACvC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;CACxB;AAMD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAmED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,CAczG;AAYD;;GAEG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAa5F;AAeD;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAwI3F;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAgB9F"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GTFS sync and caching module
|
|
3
|
+
* @module gtfs/sync
|
|
4
|
+
*
|
|
5
|
+
* Handles downloading, extracting, and caching GTFS data from stops.lt.
|
|
6
|
+
* Uses yauzl-promise for proper ZIP extraction.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { mkdir, readFile, writeFile, unlink } from 'node:fs/promises';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import * as yauzl from 'yauzl-promise';
|
|
13
|
+
import { CITY_CONFIGS } from '../config.js';
|
|
14
|
+
import { GtfsSyncError } from '../errors.js';
|
|
15
|
+
import { parseRoutesContent, parseStopsContent } from './parser.js';
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Default Values
|
|
18
|
+
// =============================================================================
|
|
19
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
20
|
+
const DEFAULT_USER_AGENT = 'lt-public-transport-sdk/1.0.0';
|
|
21
|
+
/**
|
|
22
|
+
* Get default cache directory.
|
|
23
|
+
*/
|
|
24
|
+
function getDefaultCacheDir() {
|
|
25
|
+
return join(tmpdir(), 'lt-transport-sdk-cache');
|
|
26
|
+
}
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Helper Functions
|
|
29
|
+
// =============================================================================
|
|
30
|
+
/**
|
|
31
|
+
* Ensure directory exists.
|
|
32
|
+
*/
|
|
33
|
+
async function ensureDir(dir) {
|
|
34
|
+
if (!existsSync(dir)) {
|
|
35
|
+
await mkdir(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read a stream to string.
|
|
40
|
+
*/
|
|
41
|
+
async function streamToString(stream) {
|
|
42
|
+
const chunks = [];
|
|
43
|
+
for await (const chunk of stream) {
|
|
44
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
45
|
+
}
|
|
46
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load cache metadata for a city.
|
|
50
|
+
*/
|
|
51
|
+
async function loadCacheMeta(cacheDir, city) {
|
|
52
|
+
const metaPath = join(cacheDir, city, 'meta.json');
|
|
53
|
+
if (!existsSync(metaPath)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const content = await readFile(metaPath, 'utf-8');
|
|
58
|
+
return JSON.parse(content);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Save cache metadata for a city.
|
|
66
|
+
*/
|
|
67
|
+
async function saveCacheMeta(cacheDir, city, meta) {
|
|
68
|
+
const cityDir = join(cacheDir, city);
|
|
69
|
+
await ensureDir(cityDir);
|
|
70
|
+
await writeFile(join(cityDir, 'meta.json'), JSON.stringify(meta, null, 2));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Load cached routes for a city.
|
|
74
|
+
*/
|
|
75
|
+
export async function loadCachedRoutes(cacheDir, city) {
|
|
76
|
+
const routesPath = join(cacheDir, city, 'routes.json');
|
|
77
|
+
if (!existsSync(routesPath)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const content = await readFile(routesPath, 'utf-8');
|
|
82
|
+
const entries = JSON.parse(content);
|
|
83
|
+
return new Map(entries);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Save routes to cache.
|
|
91
|
+
*/
|
|
92
|
+
async function saveRoutes(cacheDir, city, routes) {
|
|
93
|
+
const cityDir = join(cacheDir, city);
|
|
94
|
+
await ensureDir(cityDir);
|
|
95
|
+
const entries = Array.from(routes.entries());
|
|
96
|
+
await writeFile(join(cityDir, 'routes.json'), JSON.stringify(entries));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Load cached stops for a city.
|
|
100
|
+
*/
|
|
101
|
+
export async function loadCachedStops(cacheDir, city) {
|
|
102
|
+
const stopsPath = join(cacheDir, city, 'stops.json');
|
|
103
|
+
if (!existsSync(stopsPath)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const content = await readFile(stopsPath, 'utf-8');
|
|
108
|
+
return JSON.parse(content);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Save stops to cache.
|
|
116
|
+
*/
|
|
117
|
+
async function saveStops(cacheDir, city, stops) {
|
|
118
|
+
const cityDir = join(cacheDir, city);
|
|
119
|
+
await ensureDir(cityDir);
|
|
120
|
+
await writeFile(join(cityDir, 'stops.json'), JSON.stringify(stops));
|
|
121
|
+
}
|
|
122
|
+
// =============================================================================
|
|
123
|
+
// Main Sync Function
|
|
124
|
+
// =============================================================================
|
|
125
|
+
/**
|
|
126
|
+
* Sync GTFS data for a city.
|
|
127
|
+
*
|
|
128
|
+
* Downloads the GTFS ZIP archive if newer than cached version,
|
|
129
|
+
* extracts routes.txt and stops.txt, and caches the parsed data.
|
|
130
|
+
*
|
|
131
|
+
* @param city - City to sync
|
|
132
|
+
* @param options - Sync options
|
|
133
|
+
* @returns Sync result
|
|
134
|
+
*/
|
|
135
|
+
export async function syncGtfs(city, options = {}) {
|
|
136
|
+
const { cacheDir = getDefaultCacheDir(), timeout = DEFAULT_TIMEOUT, userAgent = DEFAULT_USER_AGENT, force = false, } = options;
|
|
137
|
+
const config = CITY_CONFIGS[city];
|
|
138
|
+
if (!config.gtfs.enabled) {
|
|
139
|
+
throw new GtfsSyncError(city, 'GTFS not available for this city');
|
|
140
|
+
}
|
|
141
|
+
const gtfsUrl = config.gtfs.url;
|
|
142
|
+
try {
|
|
143
|
+
// Check Last-Modified header
|
|
144
|
+
const headController = new AbortController();
|
|
145
|
+
const headTimeout = setTimeout(() => { headController.abort(); }, timeout);
|
|
146
|
+
let remoteLastModified = null;
|
|
147
|
+
try {
|
|
148
|
+
const headResp = await fetch(gtfsUrl, {
|
|
149
|
+
method: 'HEAD',
|
|
150
|
+
headers: { 'User-Agent': userAgent },
|
|
151
|
+
signal: headController.signal,
|
|
152
|
+
});
|
|
153
|
+
remoteLastModified = headResp.headers.get('Last-Modified');
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
clearTimeout(headTimeout);
|
|
157
|
+
}
|
|
158
|
+
// Check if cache is current
|
|
159
|
+
const cachedMeta = await loadCacheMeta(cacheDir, city);
|
|
160
|
+
if (!force && cachedMeta?.lastModified === remoteLastModified && remoteLastModified !== null) {
|
|
161
|
+
// Cache is current
|
|
162
|
+
return {
|
|
163
|
+
city,
|
|
164
|
+
status: 'up-to-date',
|
|
165
|
+
routeCount: cachedMeta.routeCount,
|
|
166
|
+
stopCount: cachedMeta.stopCount,
|
|
167
|
+
lastModified: cachedMeta.lastModified,
|
|
168
|
+
syncedAt: new Date(cachedMeta.syncedAt),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// Download ZIP
|
|
172
|
+
const downloadController = new AbortController();
|
|
173
|
+
const downloadTimeout = setTimeout(() => { downloadController.abort(); }, timeout * 3);
|
|
174
|
+
let zipBuffer;
|
|
175
|
+
try {
|
|
176
|
+
const response = await fetch(gtfsUrl, {
|
|
177
|
+
headers: { 'User-Agent': userAgent },
|
|
178
|
+
signal: downloadController.signal,
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
throw new GtfsSyncError(city, `HTTP ${String(response.status)}: ${response.statusText}`);
|
|
182
|
+
}
|
|
183
|
+
zipBuffer = Buffer.from(await response.arrayBuffer());
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
clearTimeout(downloadTimeout);
|
|
187
|
+
}
|
|
188
|
+
// Save to temp file for yauzl
|
|
189
|
+
const tempPath = join(tmpdir(), `gtfs-${city}-${String(Date.now())}.zip`);
|
|
190
|
+
await writeFile(tempPath, zipBuffer);
|
|
191
|
+
let routes = new Map();
|
|
192
|
+
let stops = [];
|
|
193
|
+
try {
|
|
194
|
+
// Extract with yauzl
|
|
195
|
+
const zip = await yauzl.open(tempPath);
|
|
196
|
+
try {
|
|
197
|
+
for await (const entry of zip) {
|
|
198
|
+
if (entry.filename === 'routes.txt') {
|
|
199
|
+
const stream = await entry.openReadStream();
|
|
200
|
+
const content = await streamToString(stream);
|
|
201
|
+
routes = parseRoutesContent(content);
|
|
202
|
+
}
|
|
203
|
+
else if (entry.filename === 'stops.txt') {
|
|
204
|
+
const stream = await entry.openReadStream();
|
|
205
|
+
const content = await streamToString(stream);
|
|
206
|
+
stops = parseStopsContent(content);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
await zip.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
// Clean up temp file
|
|
216
|
+
try {
|
|
217
|
+
await unlink(tempPath);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Ignore cleanup errors
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Save to cache
|
|
224
|
+
const syncedAt = new Date();
|
|
225
|
+
const meta = {
|
|
226
|
+
lastModified: remoteLastModified,
|
|
227
|
+
syncedAt: syncedAt.toISOString(),
|
|
228
|
+
routeCount: routes.size,
|
|
229
|
+
stopCount: stops.length,
|
|
230
|
+
};
|
|
231
|
+
await saveCacheMeta(cacheDir, city, meta);
|
|
232
|
+
await saveRoutes(cacheDir, city, routes);
|
|
233
|
+
await saveStops(cacheDir, city, stops);
|
|
234
|
+
return {
|
|
235
|
+
city,
|
|
236
|
+
status: 'updated',
|
|
237
|
+
routeCount: routes.size,
|
|
238
|
+
stopCount: stops.length,
|
|
239
|
+
lastModified: remoteLastModified,
|
|
240
|
+
syncedAt,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
if (error instanceof GtfsSyncError) {
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
throw new GtfsSyncError(city, error instanceof Error ? error.message : 'Unknown error', error instanceof Error ? error : undefined);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Load GTFS cache for a city.
|
|
252
|
+
* Returns null if not cached.
|
|
253
|
+
*
|
|
254
|
+
* @param city - City to load
|
|
255
|
+
* @param cacheDir - Cache directory
|
|
256
|
+
* @returns Cached GTFS data or null
|
|
257
|
+
*/
|
|
258
|
+
export async function loadGtfsCache(city, cacheDir) {
|
|
259
|
+
const dir = cacheDir ?? getDefaultCacheDir();
|
|
260
|
+
const meta = await loadCacheMeta(dir, city);
|
|
261
|
+
if (!meta) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
const routes = await loadCachedRoutes(dir, city);
|
|
265
|
+
const stops = await loadCachedStops(dir, city);
|
|
266
|
+
if (!routes || !stops) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
return { meta, routes, stops };
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/gtfs/sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AAGvC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAkDpE,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF,MAAM,eAAe,GAAG,KAAK,CAAC;AAC9B,MAAM,kBAAkB,GAAG,+BAA+B,CAAC;AAE3D;;GAEG;AACH,SAAS,kBAAkB;IACzB,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC;AAClD,CAAC;AAED,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAAC,MAA6B;IACzD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAY;IACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAEnD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAc,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,IAAY,EAAE,IAAe;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB,EAAE,IAAY;IACnE,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;IAEvD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAC;QACzD,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,IAAY,EAAE,MAA0B;IAClF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,IAAY;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IAErD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,IAAY,EAAE,KAAa;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY,EAAE,UAAuB,EAAE;IACpE,MAAM,EACJ,QAAQ,GAAG,kBAAkB,EAAE,EAC/B,OAAO,GAAG,eAAe,EACzB,SAAS,GAAG,kBAAkB,EAC9B,KAAK,GAAG,KAAK,GACd,GAAG,OAAO,CAAC;IAEZ,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,aAAa,CAAC,IAAI,EAAE,kCAAkC,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IAEhC,IAAI,CAAC;QACH,6BAA6B;QAC7B,MAAM,cAAc,GAAG,IAAI,eAAe,EAAE,CAAC;QAC7C,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAE3E,IAAI,kBAAkB,GAAkB,IAAI,CAAC;QAE7C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE;gBACpC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE;gBACpC,MAAM,EAAE,cAAc,CAAC,MAAM;aAC9B,CAAC,CAAC;YACH,kBAAkB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7D,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,WAAW,CAAC,CAAC;QAC5B,CAAC;QAED,4BAA4B;QAC5B,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEvD,IAAI,CAAC,KAAK,IAAI,UAAU,EAAE,YAAY,KAAK,kBAAkB,IAAI,kBAAkB,KAAK,IAAI,EAAE,CAAC;YAC7F,mBAAmB;YACnB,OAAO;gBACL,IAAI;gBACJ,MAAM,EAAE,YAAY;gBACpB,UAAU,EAAE,UAAU,CAAC,UAAU;gBACjC,SAAS,EAAE,UAAU,CAAC,SAAS;gBAC/B,YAAY,EAAE,UAAU,CAAC,YAAY;gBACrC,QAAQ,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;aACxC,CAAC;QACJ,CAAC;QAED,eAAe;QACf,MAAM,kBAAkB,GAAG,IAAI,eAAe,EAAE,CAAC;QACjD,MAAM,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;QAEvF,IAAI,SAAiB,CAAC;QAEtB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE;gBACpC,OAAO,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE;gBACpC,MAAM,EAAE,kBAAkB,CAAC,MAAM;aAClC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,aAAa,CAAC,IAAI,EAAE,QAAQ,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YAC3F,CAAC;YAED,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QACxD,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,eAAe,CAAC,CAAC;QAChC,CAAC;QAED,8BAA8B;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;QAC1E,MAAM,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAErC,IAAI,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;QACtC,IAAI,KAAK,GAAW,EAAE,CAAC;QAEvB,IAAI,CAAC;YACH,qBAAqB;YACrB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEvC,IAAI,CAAC;gBACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;oBAC9B,IAAI,KAAK,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;wBACpC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;wBAC5C,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;wBAC7C,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;oBACvC,CAAC;yBAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;wBAC1C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;wBAC5C,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;wBAC7C,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBACrC,CAAC;gBACH,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;YACpB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,qBAAqB;YACrB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,wBAAwB;YAC1B,CAAC;QACH,CAAC;QAED,gBAAgB;QAChB,MAAM,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAc;YACtB,YAAY,EAAE,kBAAkB;YAChC,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE;YAChC,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,SAAS,EAAE,KAAK,CAAC,MAAM;SACxB,CAAC;QAEF,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAEvC,OAAO;YACL,IAAI;YACJ,MAAM,EAAE,SAAS;YACjB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,SAAS,EAAE,KAAK,CAAC,MAAM;YACvB,YAAY,EAAE,kBAAkB;YAChC,QAAQ;SACT,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;YACnC,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,IAAI,aAAa,CACrB,IAAI,EACJ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EACxD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC3C,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,QAAiB;IACjE,MAAM,GAAG,GAAG,QAAQ,IAAI,kBAAkB,EAAE,CAAC;IAE7C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAE/C,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC"}
|