io / apollo-io-bridge
src/io/apolloIOBridge.ts is the main-thread → apolloIO.workerpromise gateway. It wraps the apolloIOProtocol round trips into four Promise-style methods, handling timeouts, worker respawn, entity-chunk reassembly, and the NEEDS_PROJECTION dialog hand-off.
Public surface
export interface ApolloImportWorkerResult {
info: ApolloMapImportInfo;
header: ApolloMapHeader | null;
bounds: ApolloMapBounds | null;
entities: MapEntity[];
stats: ApolloImportStats;
}
class ApolloIOBridge {
importBin(filename, bytes, onProgress?): Promise<ApolloImportWorkerResult>;
importText(filename, bytes, onProgress?): Promise<ApolloImportWorkerResult>;
exportBin(entities, projString, onProgress?): Promise<Uint8Array>;
exportText(entities, projString, onProgress?): Promise<Uint8Array>;
clear(): Promise<void>;
}
export const apolloIOBridge: ApolloIOBridge; // module singleton2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Source:
src/io/apolloIOBridge.ts:59-309.
onProgress receives ApolloIOProgress = { label: string; detail?: string; progress: number | null }; callers usually forward to taskProgressStore.updateTask.
Internal state
type PendingEntry =
| { kind: 'import'; resolve; reject; timer; onProgress?; entities: MapEntity[] }
| { kind: 'exportBin'; resolve; reject; timer; onProgress? }
| { kind: 'exportText'; resolve; reject; timer; onProgress? }
| { kind: 'clear'; resolve; reject; timer; onProgress? };2
3
4
5
pending: Map<requestId, PendingEntry> plus a monotonic counter generating ids of the form ${prefix}_${++counter}. The worker is spawned lazily by ensureWorker() on the first post().
Flow
Import
importBin(filename, bytes, onProgress) →
register(requestId, { kind: 'import', ..., entities: [] })
post({ type: 'IMPORT_BIN', requestId, filename, bytes }, [bytes.buffer])2
3
bytes.buffer is transferred to avoid copy. The worker streams IMPORT_ENTITIES_CHUNK × N then IMPORT_RESULT; handleMessage accumulates chunks into entry.entities.
Export (chunked)
exportBin(entities, projString, onProgress) →
post({ type: 'BEGIN_EXPORT', requestId, format, projString, total })
for chunk of entities (size 2000):
post({ type: 'EXPORT_ENTITIES_CHUNK', requestId, entities, offset, total })
onProgress(...) // 0.02..0.10
await yieldToMain() // setTimeout(0)
post({ type: 'FINISH_EXPORT', requestId })
← EXPORT_BIN_RESULT { bytes } // transferable2
3
4
5
6
7
8
yieldToMain() is new Promise(resolve => setTimeout(resolve, 0)) to keep the React commit phase responsive during a 500k-entity serialise loop.
NEEDS_PROJECTION
if (msg.type === 'NEEDS_PROJECTION') {
const picked = await useProjDialogStore.getState().request();
const projString = picked ?? FALLBACK_PROJ; // UTM_PRESETS.beijing
this.post({ type: 'RESOLVE_PROJECTION', requestId: msg.requestId, projString });
return;
}2
3
4
5
6
useProjDialogStore.request() returns Promise<string | null>. The bridge collapses null to UTM_PRESETS.beijing.
Timeouts & failures
registerschedulessetTimeout(..., DEFAULT_TIMEOUT_MS = 600_000); timeout rejects withApollo IO request timed out after 600000ms.- Worker
onerror→disposeWorker()then reject every pending entry. - Any reject path runs
clearTimeoutandpending.delete.
Coupling
- Depends on
apolloIOProtocolfor message shapes. - Depends on
useProjDialogStorefor the projection dialog. - Depends on
proto/projection.UTM_PRESETS.beijingas fallback.
See also
- io/apollo-io-protocol — message shapes.
- io/map-io — primary consumer.
- Import / Parse Base Map and Export / Base Map — orchestrating callers.