@ingissa/navcore-core 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.
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Haversine geographic math utilities.
3
+ * ZERO dependencies — pure math.
4
+ */
5
+ /** [longitude, latitude] coordinate */
6
+ type Coordinate = [number, number];
7
+ /** Haversine distance between two points in metres */
8
+ declare function haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number;
9
+ /** Forward Haversine: compute destination given start, bearing (°), distance (m) */
10
+ declare function destinationPoint(lat: number, lon: number, brng: number, dist: number): Coordinate;
11
+ /** Bearing between two [lng, lat] coordinates in degrees [0, 360) */
12
+ declare function getBearingBetweenPoints(c1: Coordinate, c2: Coordinate): number;
13
+
14
+ /**
15
+ * Module 7: InstructionResolver
16
+ * Resolves the next upcoming instruction and determines if it should be triggered.
17
+ */
18
+
19
+ /** Navigation instruction (mirrors @navcore/types NavInstruction) */
20
+ interface NavInstruction {
21
+ location: Coordinate;
22
+ geometryIndex: number;
23
+ text?: string;
24
+ instruction?: string;
25
+ cumulativeOffset?: number;
26
+ bearingBefore?: number;
27
+ bearingAfter?: number;
28
+ }
29
+ interface InstructionResult {
30
+ nextIdx: number;
31
+ instruction: NavInstruction | null;
32
+ wasTriggered: boolean;
33
+ }
34
+ /**
35
+ * Resolve which instruction is next and whether it should trigger now.
36
+ */
37
+ declare function resolveNextInstruction(currentGeometryIdx: number, instructions: NavInstruction[], currentInstrIdx: number, route: Coordinate[], currentCoord: Coordinate, vehicleBearing: number | null, vehicleDistance: number, speed?: number): InstructionResult;
38
+
39
+ /**
40
+ * Module 9a: NavigationFSM
41
+ * Navigation lifecycle state machine. Pure deterministic transitions.
42
+ */
43
+ type NavLifecycleState = 'IDLE' | 'OVERVIEW' | 'APPROACHING_ROUTE' | 'ON_ROUTE';
44
+ type LifecycleEvent = {
45
+ type: 'ROUTE_LOADED';
46
+ } | {
47
+ type: 'USER_START_NAVIGATION';
48
+ } | {
49
+ type: 'ROUTE_JOINED';
50
+ data: {
51
+ index: number;
52
+ };
53
+ } | {
54
+ type: 'NAVIGATION_FINISHED';
55
+ } | {
56
+ type: 'CANCEL_NAVIGATION';
57
+ } | {
58
+ type: 'RESET';
59
+ };
60
+ interface LifecycleContext {
61
+ resumeIndex: number | null;
62
+ }
63
+ interface LifecycleOutput {
64
+ state: NavLifecycleState;
65
+ context: LifecycleContext;
66
+ }
67
+ /**
68
+ * Pure lifecycle FSM — no React, no side effects.
69
+ */
70
+ declare class NavigationFSM {
71
+ private _state;
72
+ private _context;
73
+ get state(): NavLifecycleState;
74
+ get context(): LifecycleContext;
75
+ /** Dispatch an event and return the new output */
76
+ dispatch(event: LifecycleEvent): LifecycleOutput;
77
+ /** Reset to initial state */
78
+ reset(): void;
79
+ }
80
+
81
+ /**
82
+ * Module 9b: OffRouteFSM
83
+ * Deviation → Reroute → Rejoin state machine. Pure deterministic transitions.
84
+ * Extracted from useOffRouteSystem / NavigationStateManager (stripped of React).
85
+ */
86
+
87
+ type OffRouteState = 'ON_ROUTE' | 'DEVIATING' | 'OFF_ROUTE' | 'REROUTING' | 'REJOINING' | 'REJOINED' | 'FAILED';
88
+ interface TempRoute {
89
+ geometry: Coordinate[];
90
+ distance: number;
91
+ duration: number;
92
+ }
93
+ interface RejoinTarget {
94
+ coordinates: Coordinate;
95
+ index: number;
96
+ }
97
+ interface OffRouteContext {
98
+ deviationSegmentIdx: number | null;
99
+ deviationStartMs: number | null;
100
+ tempRoute: TempRoute | null;
101
+ rejoinTarget: RejoinTarget | null;
102
+ lastRerouteMs: number | null;
103
+ rejoinedAtMs: number | null;
104
+ rejoinDwellFrames: number;
105
+ failureReason: string | null;
106
+ }
107
+ type OffRouteEvent = {
108
+ type: 'DEVIATION_SUSPECTED';
109
+ deviationSegmentIdx: number;
110
+ } | {
111
+ type: 'DEVIATION_CONFIRMED';
112
+ deviationSegmentIdx: number;
113
+ } | {
114
+ type: 'REROUTE_STARTED';
115
+ } | {
116
+ type: 'REROUTE_SUCCESS';
117
+ tempRoute: TempRoute;
118
+ rejoinTarget: RejoinTarget;
119
+ } | {
120
+ type: 'REROUTE_FAILED';
121
+ reason: string;
122
+ } | {
123
+ type: 'REJOIN_DETECTED';
124
+ } | {
125
+ type: 'REJOIN_CONFIRMED';
126
+ } | {
127
+ type: 'RESUME';
128
+ } | {
129
+ type: 'RESET';
130
+ };
131
+ interface OffRouteOutput {
132
+ state: OffRouteState;
133
+ context: OffRouteContext;
134
+ }
135
+ /**
136
+ * Pure off-route FSM — no React, no side effects.
137
+ */
138
+ declare class OffRouteFSM {
139
+ private _state;
140
+ private _context;
141
+ get state(): OffRouteState;
142
+ get context(): OffRouteContext;
143
+ /** Dispatch an event and return the new output */
144
+ dispatch(event: OffRouteEvent, nowMs?: number): OffRouteOutput;
145
+ /** Full reset */
146
+ reset(): void;
147
+ /** Check if deviation confirmation time has elapsed */
148
+ isDeviationConfirmed(nowMs?: number): boolean;
149
+ }
150
+
151
+ /**
152
+ * Feature flag constants for SDK consumers.
153
+ *
154
+ * CORE features: available to all users (MIT license).
155
+ * PRO features: require a valid commercial license key.
156
+ */
157
+ declare const FEATURES: {
158
+ /**
159
+ * Basic Kalman filter GPS smoothing.
160
+ * Reduces GPS jitter for stable route snapping.
161
+ */
162
+ readonly GPS_SMOOTHING: "gps_smoothing";
163
+ /**
164
+ * Basic route snapping (nearest-point projection).
165
+ * Snaps GPS position to the closest route segment.
166
+ */
167
+ readonly BASIC_SNAPPING: "basic_snapping";
168
+ /**
169
+ * Advanced route snapping with bearing penalty + inertia.
170
+ * Prevents incorrect snaps on parallel roads and U-turns.
171
+ */
172
+ readonly ADVANCED_SNAPPING: "advanced_snapping";
173
+ /**
174
+ * Dead reckoning: continues navigation during GPS signal loss.
175
+ * Predicts position based on last known speed and bearing.
176
+ */
177
+ readonly DEAD_RECKONING: "dead_reckoning";
178
+ /**
179
+ * Smooth route offset predictor for jitter-free map rendering.
180
+ * Advances render position at frame rate between GPS fixes.
181
+ */
182
+ readonly ROUTE_OFFSET: "route_offset";
183
+ /**
184
+ * Progress anti-regression: prevents route index from jumping backward.
185
+ * Essential for circuit routes and motorway slip roads.
186
+ */
187
+ readonly PROGRESS_TRACKING: "progress_tracking";
188
+ /**
189
+ * Advanced bearing engine with look-ahead and speed-adaptive alpha.
190
+ * Produces stable heading for smooth map camera rotation.
191
+ */
192
+ readonly ADVANCED_BEARING: "advanced_bearing";
193
+ /**
194
+ * Compound deviation detection: combines distance, bearing, and speed signals.
195
+ * Eliminates false off-route triggers in tunnels and urban canyons.
196
+ */
197
+ readonly COMPOUND_DEVIATION: "compound_deviation";
198
+ /**
199
+ * Parallel road disambiguation + U-turn snap protection.
200
+ * Prevents snapping to the wrong carriageway when bearing mismatches route direction.
201
+ */
202
+ readonly PARALLEL_ROAD_RESOLUTION: "parallel_road_resolution";
203
+ /**
204
+ * U-turn protection: bearing penalty applied when vehicle heading opposes
205
+ * route segment direction by more than the configured threshold (default 60°).
206
+ */
207
+ readonly U_TURN_PROTECTION: "u_turn_protection";
208
+ /**
209
+ * Timed voice instruction cues with deduplication and priority levels.
210
+ */
211
+ readonly VOICE_TRIGGERS: "voice_triggers";
212
+ /**
213
+ * Local ETA computation using rolling speed average — zero API calls.
214
+ */
215
+ readonly ETA: "eta";
216
+ /**
217
+ * Circular and polygon geofencing with enter/exit/dwell events.
218
+ */
219
+ readonly GEOFENCING: "geofencing";
220
+ /**
221
+ * AI-powered navigation stability: oscillation suppression + turn anticipation.
222
+ */
223
+ readonly NAV_AI: "nav_ai";
224
+ };
225
+ type FeatureKey = typeof FEATURES[keyof typeof FEATURES];
226
+
227
+ /**
228
+ * LicenseManager — Handles offline asymmetric license verification
229
+ * using WebCrypto + granular feature licensing.
230
+ *
231
+ * Matching nav-sdk/src/shared/LicenseManager.ts contract exactly.
232
+ */
233
+
234
+ type LicenseStatus = 'VALID' | 'INVALID' | 'EXPIRED' | 'MISSING';
235
+ interface LicensePayload {
236
+ /** Legacy tier — 'pro' grants all features ('*') */
237
+ t?: 'core' | 'pro';
238
+ /** Expiration timestamp (ms since epoch) */
239
+ exp: number;
240
+ /** Bundle ID / domain */
241
+ bid: string;
242
+ /** Granular feature flags */
243
+ f?: string[];
244
+ }
245
+ declare class LicenseManager {
246
+ private status;
247
+ private payload;
248
+ private enabledFeatures;
249
+ private warnedFeatures;
250
+ private isDev;
251
+ private static PUBLIC_KEY_SPKI;
252
+ constructor(licenseKey?: string, isDev?: boolean);
253
+ validate(key: string): Promise<LicenseStatus>;
254
+ private extractFeatures;
255
+ hasFeature(feature: FeatureKey): boolean;
256
+ /** Warn once per feature when a consumer tries to use a gated feature. */
257
+ warnOnce(feature: FeatureKey): void;
258
+ /** Returns true if the license grants the full Pro tier (all features). */
259
+ isProEnabled(): boolean;
260
+ isAIEnabled(): boolean;
261
+ getStatus(): LicenseStatus;
262
+ getPayload(): LicensePayload | null;
263
+ getEnabledFeatures(): string[];
264
+ private verifySignature;
265
+ }
266
+
267
+ /**
268
+ * NavCore — Main Orchestrator
269
+ *
270
+ * Fuses all extracted modules into a single engine class.
271
+ * ZERO runtime dependencies. Framework-agnostic.
272
+ *
273
+ * Usage:
274
+ * const engine = new NavCore({ licenseKey: '...' });
275
+ * engine.setRoute(routeCoords, instructions);
276
+ * engine.update(gpsUpdate);
277
+ * const state = engine.getState();
278
+ */
279
+
280
+ interface NavGpsUpdate {
281
+ coord: Coordinate;
282
+ accuracy: number | null;
283
+ bearing: number | null;
284
+ speed: number;
285
+ timestamp: number;
286
+ }
287
+ interface NavCoreOptions {
288
+ licenseKey?: string;
289
+ isDev?: boolean;
290
+ isCircuit?: boolean;
291
+ kalmanQ?: number;
292
+ kalmanRBase?: number;
293
+ baseCorridorMeters?: number;
294
+ maxCorridorMeters?: number;
295
+ arrivalThresholdMeters?: number;
296
+ bearingSmoothingAlpha?: number;
297
+ }
298
+ interface NavCoreState {
299
+ snappedCoord: Coordinate | null;
300
+ rawGpsCoord: Coordinate | null;
301
+ bearing: number;
302
+ rawGpsBearing: number | null;
303
+ routeIndex: number;
304
+ distanceToRoute: number;
305
+ corridor: number;
306
+ isOffRoute: boolean;
307
+ progressIndex: number;
308
+ hasArrived: boolean;
309
+ offRouteState: string;
310
+ lifecycleState: string;
311
+ distanceToDestination: number | null;
312
+ distanceToNextInstruction: number | null;
313
+ nextInstructionIndex: number | null;
314
+ currentSpeed: number;
315
+ isGpsStale: boolean;
316
+ gpsAgeMs: number;
317
+ licenseStatus: string;
318
+ }
319
+ type EventHandler = (...args: unknown[]) => void;
320
+ declare class NavCore {
321
+ private kalman;
322
+ private snapper;
323
+ private deadReckoning;
324
+ private deviationDetector;
325
+ private lifecycleFSM;
326
+ private offRouteFSM;
327
+ private license;
328
+ private route;
329
+ private instructions;
330
+ private forwardCum;
331
+ private reverseCum;
332
+ private snappedCoord;
333
+ private rawGpsCoord;
334
+ private bearing;
335
+ private rawGpsBearing;
336
+ private routeIndex;
337
+ private maxProgressIdx;
338
+ private prevSegment;
339
+ private distanceToRoute;
340
+ private corridor;
341
+ private prevDistToNext;
342
+ private currentSpeed;
343
+ private lastGpsTimestamp;
344
+ private instrIdx;
345
+ private linearOffset;
346
+ private hasArrived;
347
+ private readonly options;
348
+ private listeners;
349
+ constructor(options?: NavCoreOptions);
350
+ /**
351
+ * Returns true if the feature is unlocked (dev mode OR valid license).
352
+ * Emits a one-time console warning when a Pro feature is blocked.
353
+ */
354
+ private has;
355
+ /** Load route geometry and instructions */
356
+ setRoute(route: Coordinate[], instructions?: NavInstruction[]): void;
357
+ /** Start navigation */
358
+ startNavigation(): void;
359
+ /** Process a GPS update — the main engine tick */
360
+ update(gps: NavGpsUpdate): NavCoreState;
361
+ /** Time-advance tick for dead reckoning (call at RAF rate) */
362
+ tick(nowMs: number): NavCoreState;
363
+ /** Get the current engine state snapshot */
364
+ getState(): NavCoreState;
365
+ /** Destroy the engine */
366
+ destroy(): void;
367
+ on(event: string, handler: EventHandler): void;
368
+ off(event: string, handler: EventHandler): void;
369
+ private emit;
370
+ getLicenseManager(): LicenseManager;
371
+ getRoute(): ReadonlyArray<Coordinate>;
372
+ getInstructions(): ReadonlyArray<NavInstruction>;
373
+ getOffRouteFSM(): OffRouteFSM;
374
+ getLifecycleFSM(): NavigationFSM;
375
+ }
376
+
377
+ export { type Coordinate as C, FEATURES as F, LicenseManager as L, NavCore as N, type OffRouteContext as O, type RejoinTarget as R, type TempRoute as T, type FeatureKey as a, type LicensePayload as b, type LicenseStatus as c, type LifecycleContext as d, type LifecycleEvent as e, type NavCoreOptions as f, type NavCoreState as g, type NavGpsUpdate as h, type NavInstruction as i, type NavLifecycleState as j, NavigationFSM as k, type OffRouteEvent as l, OffRouteFSM as m, type OffRouteState as n, destinationPoint as o, getBearingBetweenPoints as p, haversineDistance as q, resolveNextInstruction as r };