mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
refactor: separate transit sources and request service
This commit is contained in:
1 parent
e284a9cd34
commit
3155e4e0bf
26 files changed
+1444
-1157
No files matched your search
@@ -1,3 +1,9 @@
|
||||
import { validTransitIdentifier } from '../sources/transitHistory.js';
|
||||
export {
|
||||
validTransitIdentifier,
|
||||
fetchTransitHistory,
|
||||
} from '../sources/transitHistory.js';
|
||||
|
||||
/**
|
||||
* @module transitProxy
|
||||
* @description Pure server-side mechanics for the `/api/transit` proxy.
|
||||
@@ -67,18 +73,6 @@ export const TRANSIT_BACKOFF_LADDER_MS = Object.freeze([
|
||||
* @param {string} url Request URL relative to the mount point.
|
||||
* @returns {{ route: 'feeds' } | { route: 'vehicles', feed: object } | null}
|
||||
*/
|
||||
/** Validate the entire decoded identifier, including UTF-8 length. */
|
||||
export function validTransitIdentifier(value) {
|
||||
return (
|
||||
typeof value === 'string' &&
|
||||
value.length > 0 &&
|
||||
!/[\u0000-\u001f\u007f/\\]/u.test(value) &&
|
||||
value !== '.' &&
|
||||
value !== '..' &&
|
||||
new TextEncoder().encode(value).length <= 256 &&
|
||||
!/[\uD800-\uDFFF]/u.test(value)
|
||||
);
|
||||
}
|
||||
export function resolveTransitRoute(url) {
|
||||
const pathname = String(url || '').split('?')[0];
|
||||
if (pathname === '/feeds' || pathname === '/feeds/')
|
||||
@@ -101,91 +95,6 @@ export function resolveTransitRoute(url) {
|
||||
: null;
|
||||
}
|
||||
|
||||
/** Selection reads are bounded and cancellable through body parsing. */
|
||||
export async function fetchTransitHistory(
|
||||
feedId,
|
||||
vehicleId,
|
||||
signal,
|
||||
fetchImpl = fetch,
|
||||
) {
|
||||
if (!validTransitIdentifier(vehicleId))
|
||||
throw new TypeError('Invalid transit vehicle identifier');
|
||||
const response = await fetchImpl(
|
||||
`/api/transit/trail/${encodeURIComponent(feedId)}/${encodeURIComponent(vehicleId)}`,
|
||||
{ signal, headers: { Accept: 'application/json' } },
|
||||
);
|
||||
if (!response.ok) throw new Error(`Transit history HTTP ${response.status}`);
|
||||
const reader = response.body?.getReader?.();
|
||||
let text = '';
|
||||
if (reader) {
|
||||
const decoder = new TextDecoder();
|
||||
let bytes = 0;
|
||||
try {
|
||||
while (true) {
|
||||
signal?.throwIfAborted();
|
||||
const chunk = await reader.read();
|
||||
if (chunk.done) break;
|
||||
bytes += chunk.value.byteLength;
|
||||
if (bytes > 32768)
|
||||
throw new RangeError('Transit history exceeds response limit');
|
||||
text += decoder.decode(chunk.value, { stream: true });
|
||||
}
|
||||
text += decoder.decode();
|
||||
} finally {
|
||||
await reader.cancel().catch(() => {});
|
||||
}
|
||||
} else {
|
||||
text = await response.text();
|
||||
if (new TextEncoder().encode(text).length > 32768)
|
||||
throw new RangeError('Transit history exceeds response limit');
|
||||
}
|
||||
signal?.throwIfAborted();
|
||||
const payload = JSON.parse(text);
|
||||
if (
|
||||
payload.version !== 1 ||
|
||||
payload.feedId !== feedId ||
|
||||
payload.vehicleId !== vehicleId ||
|
||||
!Array.isArray(payload.fixes) ||
|
||||
payload.fixes.length > 128 ||
|
||||
!Array.isArray(payload.epochs) ||
|
||||
payload.epochs.length > 8
|
||||
)
|
||||
throw new TypeError('Invalid transit history response');
|
||||
let previous = -Infinity;
|
||||
const epochs = new Set();
|
||||
for (const epoch of payload.epochs) {
|
||||
if (
|
||||
!Number.isInteger(epoch.id) ||
|
||||
epoch.id < 1 ||
|
||||
epoch.id > 0xffffffff ||
|
||||
epochs.has(epoch.id) ||
|
||||
!['trip', 'route', 'mode'].every(
|
||||
(key) =>
|
||||
typeof epoch[key] === 'string' &&
|
||||
new TextEncoder().encode(epoch[key]).length <= 256,
|
||||
)
|
||||
)
|
||||
throw new TypeError('Invalid transit history epoch');
|
||||
epochs.add(epoch.id);
|
||||
}
|
||||
for (const fix of payload.fixes) {
|
||||
if (
|
||||
!Array.isArray(fix) ||
|
||||
fix.length !== 5 ||
|
||||
!fix.every(Number.isFinite) ||
|
||||
fix[0] < 0 ||
|
||||
fix[0] <= previous ||
|
||||
Math.abs(fix[1]) > 90 ||
|
||||
Math.abs(fix[2]) > 180 ||
|
||||
!Number.isInteger(fix[3]) ||
|
||||
!epochs.has(fix[4])
|
||||
)
|
||||
throw new TypeError('Invalid transit history fix');
|
||||
previous = fix[0];
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request headers for one upstream fetch.
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user