@ingissa/navcore-react-native 1.0.3 → 1.0.7

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.
Files changed (123) hide show
  1. package/LICENSE +3 -0
  2. package/dist/core/src/NavCore.d.ts +142 -0
  3. package/dist/core/src/NavCore.d.ts.map +1 -0
  4. package/dist/core/src/NavCore.js +390 -0
  5. package/dist/core/src/bearing/BearingEngine.d.ts +24 -0
  6. package/dist/core/src/bearing/BearingEngine.d.ts.map +1 -0
  7. package/dist/core/src/bearing/BearingEngine.js +50 -0
  8. package/dist/core/src/constants.d.ts +34 -0
  9. package/dist/core/src/constants.d.ts.map +1 -0
  10. package/dist/core/src/constants.js +41 -0
  11. package/dist/core/src/corridor/DeviationDetector.d.ts +33 -0
  12. package/dist/core/src/corridor/DeviationDetector.d.ts.map +1 -0
  13. package/dist/core/src/corridor/DeviationDetector.js +83 -0
  14. package/dist/core/src/corridor/DynamicCorridor.d.ts +13 -0
  15. package/dist/core/src/corridor/DynamicCorridor.d.ts.map +1 -0
  16. package/dist/core/src/corridor/DynamicCorridor.js +26 -0
  17. package/dist/core/src/dead-reckoning/DeadReckoning.d.ts +35 -0
  18. package/dist/core/src/dead-reckoning/DeadReckoning.d.ts.map +1 -0
  19. package/dist/core/src/dead-reckoning/DeadReckoning.js +49 -0
  20. package/dist/core/src/directions/DirectionsProvider.d.ts +70 -0
  21. package/dist/core/src/directions/DirectionsProvider.d.ts.map +1 -0
  22. package/dist/core/src/directions/DirectionsProvider.js +7 -0
  23. package/dist/core/src/directions/OSRMDirectionsProvider.d.ts +26 -0
  24. package/dist/core/src/directions/OSRMDirectionsProvider.d.ts.map +1 -0
  25. package/dist/core/src/directions/OSRMDirectionsProvider.js +90 -0
  26. package/dist/core/src/directions/OpenRouteServiceProvider.d.ts +30 -0
  27. package/dist/core/src/directions/OpenRouteServiceProvider.d.ts.map +1 -0
  28. package/dist/core/src/directions/OpenRouteServiceProvider.js +111 -0
  29. package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts +33 -0
  30. package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts.map +1 -0
  31. package/dist/core/src/directions/ValhallaDirectionsProvider.js +155 -0
  32. package/dist/core/src/eta/ETAEngine.d.ts +61 -0
  33. package/dist/core/src/eta/ETAEngine.d.ts.map +1 -0
  34. package/dist/core/src/eta/ETAEngine.js +74 -0
  35. package/dist/core/src/features.d.ts +76 -0
  36. package/dist/core/src/features.d.ts.map +1 -0
  37. package/dist/core/src/features.js +76 -0
  38. package/dist/core/src/geo/bearing.d.ts +11 -0
  39. package/dist/core/src/geo/bearing.d.ts.map +1 -0
  40. package/dist/core/src/geo/bearing.js +25 -0
  41. package/dist/core/src/geo/cumulative.d.ts +9 -0
  42. package/dist/core/src/geo/cumulative.d.ts.map +1 -0
  43. package/dist/core/src/geo/cumulative.js +20 -0
  44. package/dist/core/src/geo/haversine.d.ts +13 -0
  45. package/dist/core/src/geo/haversine.d.ts.map +1 -0
  46. package/dist/core/src/geo/haversine.js +39 -0
  47. package/dist/core/src/geo/projection.d.ts +7 -0
  48. package/dist/core/src/geo/projection.d.ts.map +1 -0
  49. package/dist/core/src/geo/projection.js +17 -0
  50. package/dist/core/src/geofencing/GeofencingEngine.d.ts +93 -0
  51. package/dist/core/src/geofencing/GeofencingEngine.d.ts.map +1 -0
  52. package/dist/core/src/geofencing/GeofencingEngine.js +154 -0
  53. package/dist/core/src/index.d.ts +66 -0
  54. package/dist/core/src/index.d.ts.map +1 -0
  55. package/dist/core/src/index.js +62 -0
  56. package/dist/core/src/kalman/KalmanFilter2D.d.ts +46 -0
  57. package/dist/core/src/kalman/KalmanFilter2D.d.ts.map +1 -0
  58. package/dist/core/src/kalman/KalmanFilter2D.js +77 -0
  59. package/dist/core/src/license/LicenseManager.d.ts +40 -0
  60. package/dist/core/src/license/LicenseManager.d.ts.map +1 -0
  61. package/dist/core/src/license/LicenseManager.js +119 -0
  62. package/dist/core/src/license/verifyP256Signature.d.ts +2 -0
  63. package/dist/core/src/license/verifyP256Signature.d.ts.map +1 -0
  64. package/dist/core/src/license/verifyP256Signature.js +26 -0
  65. package/dist/core/src/off-route/NavigationFSM.d.ts +42 -0
  66. package/dist/core/src/off-route/NavigationFSM.d.ts.map +1 -0
  67. package/dist/core/src/off-route/NavigationFSM.js +56 -0
  68. package/dist/core/src/off-route/OffRouteFSM.d.ts +70 -0
  69. package/dist/core/src/off-route/OffRouteFSM.d.ts.map +1 -0
  70. package/dist/core/src/off-route/OffRouteFSM.js +151 -0
  71. package/dist/core/src/predictor/RouteOffsetPredictor.d.ts +24 -0
  72. package/dist/core/src/predictor/RouteOffsetPredictor.d.ts.map +1 -0
  73. package/dist/core/src/predictor/RouteOffsetPredictor.js +55 -0
  74. package/dist/core/src/progress/InstructionEditor.d.ts +77 -0
  75. package/dist/core/src/progress/InstructionEditor.d.ts.map +1 -0
  76. package/dist/core/src/progress/InstructionEditor.js +151 -0
  77. package/dist/core/src/progress/InstructionResolver.d.ts +25 -0
  78. package/dist/core/src/progress/InstructionResolver.d.ts.map +1 -0
  79. package/dist/core/src/progress/InstructionResolver.js +78 -0
  80. package/dist/core/src/progress/ProgressTracker.d.ts +12 -0
  81. package/dist/core/src/progress/ProgressTracker.d.ts.map +1 -0
  82. package/dist/core/src/progress/ProgressTracker.js +22 -0
  83. package/dist/core/src/renderer/MapRendererAdapter.d.ts +72 -0
  84. package/dist/core/src/renderer/MapRendererAdapter.d.ts.map +1 -0
  85. package/dist/core/src/renderer/MapRendererAdapter.js +19 -0
  86. package/dist/core/src/route-builder/CustomRouteBuilder.d.ts +83 -0
  87. package/dist/core/src/route-builder/CustomRouteBuilder.d.ts.map +1 -0
  88. package/dist/core/src/route-builder/CustomRouteBuilder.js +160 -0
  89. package/dist/core/src/route-plan/RoutePlan.d.ts +38 -0
  90. package/dist/core/src/route-plan/RoutePlan.d.ts.map +1 -0
  91. package/dist/core/src/route-plan/RoutePlan.js +102 -0
  92. package/dist/core/src/session/NavigationSession.d.ts +81 -0
  93. package/dist/core/src/session/NavigationSession.d.ts.map +1 -0
  94. package/dist/core/src/session/NavigationSession.js +115 -0
  95. package/dist/core/src/snapper/ParallelRoadResolver.d.ts +86 -0
  96. package/dist/core/src/snapper/ParallelRoadResolver.d.ts.map +1 -0
  97. package/dist/core/src/snapper/ParallelRoadResolver.js +83 -0
  98. package/dist/core/src/snapper/RouteSnapper.d.ts +38 -0
  99. package/dist/core/src/snapper/RouteSnapper.d.ts.map +1 -0
  100. package/dist/core/src/snapper/RouteSnapper.js +80 -0
  101. package/dist/core/src/voice/VoiceTriggerEngine.d.ts +80 -0
  102. package/dist/core/src/voice/VoiceTriggerEngine.d.ts.map +1 -0
  103. package/dist/core/src/voice/VoiceTriggerEngine.js +108 -0
  104. package/dist/react-native/src/NavCoreProvider.d.ts.map +1 -0
  105. package/dist/react-native/src/index.d.ts +10 -0
  106. package/dist/react-native/src/index.d.ts.map +1 -0
  107. package/dist/{index.js → react-native/src/index.js} +1 -0
  108. package/dist/{useNavCore.d.ts → react-native/src/useNavCore.d.ts} +7 -5
  109. package/dist/react-native/src/useNavCore.d.ts.map +1 -0
  110. package/dist/{useNavCore.js → react-native/src/useNavCore.js} +21 -47
  111. package/dist/react-native/src/useNavSelectors.d.ts +42 -0
  112. package/dist/react-native/src/useNavSelectors.d.ts.map +1 -0
  113. package/dist/react-native/src/useNavSelectors.js +103 -0
  114. package/package.json +17 -9
  115. package/src/NavCoreProvider.tsx +59 -0
  116. package/{dist/index.d.ts → src/index.ts} +18 -9
  117. package/src/useNavCore.ts +208 -0
  118. package/src/useNavSelectors.ts +143 -0
  119. package/dist/NavCoreProvider.d.ts.map +0 -1
  120. package/dist/index.d.ts.map +0 -1
  121. package/dist/useNavCore.d.ts.map +0 -1
  122. /package/dist/{NavCoreProvider.d.ts → react-native/src/NavCoreProvider.d.ts} +0 -0
  123. /package/dist/{NavCoreProvider.js → react-native/src/NavCoreProvider.js} +0 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * VoiceTriggerEngine
3
+ *
4
+ * Converts NavCore instruction events into timed voice cues.
5
+ * Handles deduplication, look-ahead windows, and priority levels.
6
+ * Consumer calls TTS — this engine only computes WHEN to speak.
7
+ *
8
+ * FEATURE: VOICE_TRIGGERS
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const voice = new VoiceTriggerEngine({ earlyTriggerMeters: 150 });
13
+ * voice.setInstructions(instructions);
14
+ *
15
+ * engine.on('instruction', (instr) => {
16
+ * const cue = voice.onInstruction(instr, state);
17
+ * if (cue) speakText(cue.text);
18
+ * });
19
+ *
20
+ * // Or poll each GPS tick:
21
+ * const cue = voice.update(state);
22
+ * if (cue) speakText(cue.text);
23
+ * ```
24
+ */
25
+ import type { NavInstruction } from '../progress/InstructionResolver';
26
+ import type { NavCoreState } from '../NavCore';
27
+ export type VoicePriority = 'low' | 'normal' | 'high' | 'urgent';
28
+ export interface VoiceCue {
29
+ /** The instruction that triggered this cue */
30
+ instruction: NavInstruction;
31
+ /** Text to pass to TTS */
32
+ text: string;
33
+ /** Distance to the instruction point in metres */
34
+ distanceMetres: number;
35
+ /** Priority hint for TTS queue management */
36
+ priority: VoicePriority;
37
+ /** Monotonic trigger ID — use this to deduplicate */
38
+ triggerId: string;
39
+ }
40
+ export interface VoiceTriggerOptions {
41
+ /**
42
+ * Trigger voice this many metres before the instruction point.
43
+ * Default: 120m (comfortable at 50 km/h).
44
+ */
45
+ earlyTriggerMeters?: number;
46
+ /**
47
+ * Stop trying to trigger if vehicle is already past this many metres.
48
+ * Default: 20m.
49
+ */
50
+ lateTriggerMeters?: number;
51
+ /**
52
+ * Re-announce if user is still approaching after this distance.
53
+ * Set to 0 to disable re-announcement. Default: 300m.
54
+ */
55
+ reannounceMeters?: number;
56
+ }
57
+ export declare class VoiceTriggerEngine {
58
+ private instructions;
59
+ private triggeredIds;
60
+ private reannouncedIds;
61
+ private readonly options;
62
+ constructor(options?: VoiceTriggerOptions);
63
+ /** Load the instruction set (call after engine.setRoute) */
64
+ setInstructions(instructions: NavInstruction[]): void;
65
+ /**
66
+ * Poll on every GPS state update.
67
+ * Returns a `VoiceCue` when an instruction should be spoken, or null.
68
+ */
69
+ update(state: NavCoreState): VoiceCue | null;
70
+ /**
71
+ * Called directly from engine's 'instruction' event.
72
+ * Always fires immediately regardless of distance window.
73
+ */
74
+ onInstruction(instr: NavInstruction, state: NavCoreState): VoiceCue;
75
+ /** Reset triggered state (call on setRoute / new journey) */
76
+ reset(): void;
77
+ private instrId;
78
+ private makeCue;
79
+ }
80
+ //# sourceMappingURL=VoiceTriggerEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VoiceTriggerEngine.d.ts","sourceRoot":"","sources":["../../../../../core/src/voice/VoiceTriggerEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,WAAW,EAAE,cAAc,CAAC;IAC5B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,QAAQ,EAAE,aAAa,CAAC;IACxB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAQD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;gBAE5C,OAAO,CAAC,EAAE,mBAAmB;IAIzC,4DAA4D;IAC5D,eAAe,CAAC,YAAY,EAAE,cAAc,EAAE,GAAG,IAAI;IAMrD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI;IA6C5C;;;OAGG;IACH,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,QAAQ;IAKnE,6DAA6D;IAC7D,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,OAAO;CAShB"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * VoiceTriggerEngine
3
+ *
4
+ * Converts NavCore instruction events into timed voice cues.
5
+ * Handles deduplication, look-ahead windows, and priority levels.
6
+ * Consumer calls TTS — this engine only computes WHEN to speak.
7
+ *
8
+ * FEATURE: VOICE_TRIGGERS
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const voice = new VoiceTriggerEngine({ earlyTriggerMeters: 150 });
13
+ * voice.setInstructions(instructions);
14
+ *
15
+ * engine.on('instruction', (instr) => {
16
+ * const cue = voice.onInstruction(instr, state);
17
+ * if (cue) speakText(cue.text);
18
+ * });
19
+ *
20
+ * // Or poll each GPS tick:
21
+ * const cue = voice.update(state);
22
+ * if (cue) speakText(cue.text);
23
+ * ```
24
+ */
25
+ const DEFAULTS = {
26
+ earlyTriggerMeters: 120,
27
+ lateTriggerMeters: 20,
28
+ reannounceMeters: 300,
29
+ };
30
+ export class VoiceTriggerEngine {
31
+ constructor(options) {
32
+ this.instructions = [];
33
+ this.triggeredIds = new Set();
34
+ this.reannouncedIds = new Set();
35
+ this.options = { ...DEFAULTS, ...options };
36
+ }
37
+ /** Load the instruction set (call after engine.setRoute) */
38
+ setInstructions(instructions) {
39
+ this.instructions = [...instructions];
40
+ this.triggeredIds.clear();
41
+ this.reannouncedIds.clear();
42
+ }
43
+ /**
44
+ * Poll on every GPS state update.
45
+ * Returns a `VoiceCue` when an instruction should be spoken, or null.
46
+ */
47
+ update(state) {
48
+ const { distanceToNextInstruction, nextInstructionIndex } = state;
49
+ if (nextInstructionIndex === null ||
50
+ distanceToNextInstruction === null ||
51
+ nextInstructionIndex >= this.instructions.length) {
52
+ return null;
53
+ }
54
+ const instr = this.instructions[nextInstructionIndex];
55
+ const id = this.instrId(nextInstructionIndex);
56
+ const dist = distanceToNextInstruction;
57
+ // Already fully triggered — don't repeat
58
+ if (this.triggeredIds.has(id)) {
59
+ // Check re-announce window
60
+ if (this.options.reannounceMeters > 0 &&
61
+ !this.reannouncedIds.has(id) &&
62
+ dist <= this.options.reannounceMeters &&
63
+ dist > this.options.earlyTriggerMeters) {
64
+ this.reannouncedIds.add(id);
65
+ return this.makeCue(instr, dist, 'low');
66
+ }
67
+ return null;
68
+ }
69
+ // Too far — not yet
70
+ if (dist > this.options.earlyTriggerMeters)
71
+ return null;
72
+ // Already passed — too late
73
+ if (dist < -this.options.lateTriggerMeters) {
74
+ this.triggeredIds.add(id); // mark so we skip
75
+ return null;
76
+ }
77
+ // In the trigger window — fire!
78
+ this.triggeredIds.add(id);
79
+ const priority = dist < 30 ? 'urgent' : dist < 60 ? 'high' : 'normal';
80
+ return this.makeCue(instr, dist, priority);
81
+ }
82
+ /**
83
+ * Called directly from engine's 'instruction' event.
84
+ * Always fires immediately regardless of distance window.
85
+ */
86
+ onInstruction(instr, state) {
87
+ const dist = state.distanceToNextInstruction ?? 0;
88
+ return this.makeCue(instr, dist, 'high');
89
+ }
90
+ /** Reset triggered state (call on setRoute / new journey) */
91
+ reset() {
92
+ this.triggeredIds.clear();
93
+ this.reannouncedIds.clear();
94
+ }
95
+ instrId(index) {
96
+ const instr = this.instructions[index];
97
+ return `${index}_${instr?.geometryIndex ?? 0}`;
98
+ }
99
+ makeCue(instr, dist, priority) {
100
+ return {
101
+ instruction: instr,
102
+ text: instr.text ?? instr.instruction ?? '',
103
+ distanceMetres: Math.max(0, dist),
104
+ priority,
105
+ triggerId: `${instr.geometryIndex}_${dist.toFixed(0)}`,
106
+ };
107
+ }
108
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavCoreProvider.d.ts","sourceRoot":"","sources":["../../../src/NavCoreProvider.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnF,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAErE,UAAU,mBAAmB;IAC3B,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,oBAAoB,2CAe1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,mBAAmB,CAMvD"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @navcore/react-native — React Native bindings
3
+ *
4
+ * Provides hooks for integrating NavCore with React Native apps.
5
+ */
6
+ export { useNavCore } from './useNavCore';
7
+ export type { UseNavCoreProps, UseNavCoreReturn } from './useNavCore';
8
+ export { NavCoreProvider, useNavCoreContext } from './NavCoreProvider';
9
+ export { useNavSelector, useNavInstruction, useNavAlert, useNavProgress, useNavIsOffRoute, useNavSpeed, type NavProgressInfo, } from './useNavSelectors';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC"}
@@ -5,3 +5,4 @@
5
5
  */
6
6
  export { useNavCore } from './useNavCore';
7
7
  export { NavCoreProvider, useNavCoreContext } from './NavCoreProvider';
8
+ export { useNavSelector, useNavInstruction, useNavAlert, useNavProgress, useNavIsOffRoute, useNavSpeed, } from './useNavSelectors';
@@ -29,11 +29,13 @@ export interface UseNavCoreProps {
29
29
  /** Resume navigation at a specific route index (after rejoin) */
30
30
  resumeAtIndex?: number | null;
31
31
  /** Ref for external camera output (map camera integration) */
32
- outputRef?: React.MutableRefObject<{
33
- coord: Coordinate;
34
- bearing: number;
35
- speed: number;
36
- } | null>;
32
+ outputRef?: {
33
+ current: {
34
+ coord: Coordinate;
35
+ bearing: number;
36
+ speed: number;
37
+ } | null;
38
+ };
37
39
  /** Called when deviation is confirmed */
38
40
  onDeviation?: (event: {
39
41
  anchorIndex: number;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavCore.d.ts","sourceRoot":"","sources":["../../../src/useNavCore.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,OAAO,EACP,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAI/B,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,aAAa,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACnC,8BAA8B;IAC9B,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,sDAAsD;IACtD,YAAY,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8DAA8D;IAC9D,SAAS,CAAC,EAAE;QACV,OAAO,EAAE;YACP,KAAK,EAAE,UAAU,CAAC;YAClB,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;SACf,GAAG,IAAI,CAAC;KACV,CAAC;IACF,yCAAyC;IACzC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/E,mCAAmC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD,0CAA0C;IAC1C,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,cAAc,KAAK,IAAI,CAAC;IACtD,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,2DAA2D;IAC3D,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,UAAU,EACV,WAAW,EACX,YAAY,EACZ,KAAK,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,aAAa,EACb,SAAS,EACT,WAAW,EACX,QAAQ,EACR,aAAa,EACb,SAAS,EACT,UAAe,GAChB,EAAE,eAAe,GAAG,gBAAgB,CAyHpC"}
@@ -7,7 +7,7 @@
7
7
  * Drop-in replacement for the existing Go4Exams useNavCore hook.
8
8
  */
9
9
  import { useState, useEffect, useRef, useMemo } from 'react';
10
- import { NavCore } from '@ingissa/navcore-core';
10
+ import { NavCore, } from '@ingissa/navcore-core';
11
11
  // ── Hook ──────────────────────────────────────────────────────
12
12
  export function useNavCore({ gpsCoord, gpsBearing, gpsAccuracy, gpsTimestamp, speed, routeGeometry, instructions, isNavigating, options, resumeAtIndex, outputRef, onDeviation, onRejoin, onInstruction, onArrival, pollRateHz = 30, }) {
13
13
  // ── 1. Engine singleton ───────────────────────────────────────
@@ -73,63 +73,37 @@ export function useNavCore({ gpsCoord, gpsBearing, gpsAccuracy, gpsTimestamp, sp
73
73
  speed,
74
74
  timestamp: gpsTimestamp ?? Date.now(),
75
75
  };
76
- engine.update(gpsUpdate);
77
- }, [engine, gpsCoord, gpsAccuracy, gpsBearing, gpsTimestamp, speed, isNavigating]);
76
+ const nextState = engine.update(gpsUpdate);
77
+ // Synchronous external camera/puck output with ZERO React re-renders
78
+ if (outputRef) {
79
+ outputRef.current = {
80
+ coord: nextState.snappedCoord ?? gpsCoord,
81
+ bearing: nextState.bearing,
82
+ speed: nextState.currentSpeed,
83
+ };
84
+ }
85
+ }, [engine, gpsCoord, gpsAccuracy, gpsBearing, gpsTimestamp, speed, isNavigating, outputRef]);
78
86
  // ── 6. Start navigation ───────────────────────────────────────
79
87
  useEffect(() => {
80
88
  if (isNavigating) {
81
89
  engine.startNavigation();
82
90
  }
83
91
  }, [engine, isNavigating]);
84
- // ── 7. Optimized state polling ────────────────────────────────
85
- // Polls at pollRateHz to batch state reads and minimize re-renders.
86
- // Only triggers setState when positional properties actually change.
92
+ // ── 7. Push-based reactive state subscription ─────────────────
93
+ // Eliminates the 30 Hz setInterval loop. Updates state reactively
94
+ // only when new engine state is computed.
87
95
  useEffect(() => {
88
96
  if (!isNavigating)
89
97
  return;
90
- const lastRef = {
91
- snapped: [0, 0],
92
- bearing: 0,
93
- raw: [0, 0],
98
+ const handleUpdate = (updatedState) => {
99
+ setState(updatedState);
94
100
  };
95
- const intervalMs = Math.round(1000 / pollRateHz);
96
- const poll = () => {
97
- const live = engine.getState();
98
- let changed = false;
99
- // Bearing delta
100
- if (Math.abs(live.bearing - lastRef.bearing) > 0.1) {
101
- lastRef.bearing = live.bearing;
102
- changed = true;
103
- }
104
- // Snapped coordinate delta
105
- if (live.snappedCoord &&
106
- (live.snappedCoord[0] !== lastRef.snapped[0] ||
107
- live.snappedCoord[1] !== lastRef.snapped[1])) {
108
- lastRef.snapped = [live.snappedCoord[0], live.snappedCoord[1]];
109
- changed = true;
110
- }
111
- // Raw GPS delta
112
- if (live.rawGpsCoord &&
113
- (live.rawGpsCoord[0] !== lastRef.raw[0] ||
114
- live.rawGpsCoord[1] !== lastRef.raw[1])) {
115
- lastRef.raw = [live.rawGpsCoord[0], live.rawGpsCoord[1]];
116
- changed = true;
117
- }
118
- if (changed) {
119
- setState(live);
120
- }
121
- // Camera output ref
122
- if (outputRef?.current !== undefined) {
123
- outputRef.current = {
124
- coord: live.snappedCoord ?? lastRef.snapped,
125
- bearing: live.bearing,
126
- speed: live.currentSpeed,
127
- };
128
- }
101
+ engine.on('update', handleUpdate);
102
+ setState(engine.getState());
103
+ return () => {
104
+ engine.off('update', handleUpdate);
129
105
  };
130
- const id = setInterval(poll, intervalMs);
131
- return () => clearInterval(id);
132
- }, [engine, isNavigating, pollRateHz, outputRef]);
106
+ }, [engine, isNavigating]);
133
107
  // ── 8. Cleanup ────────────────────────────────────────────────
134
108
  useEffect(() => {
135
109
  return () => {
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Granular selector hooks for NavCore.
3
+ *
4
+ * Built with React 18 `useSyncExternalStore` for flicker-free, tearing-free
5
+ * state subscriptions. Components only re-render when the selected slice changes.
6
+ */
7
+ import type { NavCore, NavCoreState, NavInstruction } from '@ingissa/navcore-core';
8
+ /**
9
+ * Generic reactive selector subscribing to NavCore state updates.
10
+ * Only triggers re-renders when the selected value actually changes.
11
+ */
12
+ export declare function useNavSelector<T>(selector: (state: NavCoreState) => T, customEngine?: NavCore, isEqual?: (a: T, b: T) => boolean): T;
13
+ /**
14
+ * Hook to select the current upcoming turn-by-turn navigation instruction.
15
+ * Only re-renders when the instruction changes or triggers.
16
+ */
17
+ export declare function useNavInstruction(customEngine?: NavCore): NavInstruction | null;
18
+ /**
19
+ * Hook to select current alert/deviation state ('NONE' | 'DEVIATING' | 'RECALCULATING' | 'REJOINING' | 'REJOINED').
20
+ * Only re-renders on alert state transitions.
21
+ */
22
+ export declare function useNavAlert(customEngine?: NavCore): NavCoreState['alertState'];
23
+ export interface NavProgressInfo {
24
+ distanceToDestination: number | null;
25
+ distanceToNextInstruction: number | null;
26
+ progressIndex: number;
27
+ routeIndex: number;
28
+ hasArrived: boolean;
29
+ }
30
+ /**
31
+ * Hook to select navigation progress and remaining distance metrics.
32
+ */
33
+ export declare function useNavProgress(customEngine?: NavCore): NavProgressInfo;
34
+ /**
35
+ * Hook to select whether the vehicle is currently off-route.
36
+ */
37
+ export declare function useNavIsOffRoute(customEngine?: NavCore): boolean;
38
+ /**
39
+ * Hook to select current speed in m/s.
40
+ */
41
+ export declare function useNavSpeed(customEngine?: NavCore): number;
42
+ //# sourceMappingURL=useNavSelectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useNavSelectors.d.ts","sourceRoot":"","sources":["../../../src/useNavSelectors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGnF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,CAAC,EACpC,YAAY,CAAC,EAAE,OAAO,EACtB,OAAO,GAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAmB,GAC3C,CAAC,CAsCH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAe/E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,CAM9E;AAED,MAAM,WAAW,eAAe;IAC9B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,eAAe,CAiBtE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAMhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAM1D"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Granular selector hooks for NavCore.
3
+ *
4
+ * Built with React 18 `useSyncExternalStore` for flicker-free, tearing-free
5
+ * state subscriptions. Components only re-render when the selected slice changes.
6
+ */
7
+ import { useSyncExternalStore, useRef, useCallback } from 'react';
8
+ import { useNavCoreContext } from './NavCoreProvider';
9
+ /**
10
+ * Generic reactive selector subscribing to NavCore state updates.
11
+ * Only triggers re-renders when the selected value actually changes.
12
+ */
13
+ export function useNavSelector(selector, customEngine, isEqual = Object.is) {
14
+ let contextEngine;
15
+ try {
16
+ contextEngine = useNavCoreContext().engine;
17
+ }
18
+ catch {
19
+ contextEngine = undefined;
20
+ }
21
+ const engine = customEngine ?? contextEngine;
22
+ if (!engine) {
23
+ throw new Error('useNavSelector must be provided an engine or used within a NavCoreProvider');
24
+ }
25
+ const lastSelectedRef = useRef(undefined);
26
+ const lastStateRef = useRef(undefined);
27
+ const getSnapshot = useCallback(() => {
28
+ const currentState = engine.getState();
29
+ if (lastStateRef.current === currentState && lastSelectedRef.current !== undefined) {
30
+ return lastSelectedRef.current;
31
+ }
32
+ const nextSelected = selector(currentState);
33
+ if (lastSelectedRef.current !== undefined && isEqual(lastSelectedRef.current, nextSelected)) {
34
+ return lastSelectedRef.current;
35
+ }
36
+ lastStateRef.current = currentState;
37
+ lastSelectedRef.current = nextSelected;
38
+ return nextSelected;
39
+ }, [engine, selector, isEqual]);
40
+ const subscribe = useCallback((onStoreChange) => {
41
+ engine.on('update', onStoreChange);
42
+ return () => {
43
+ engine.off('update', onStoreChange);
44
+ };
45
+ }, [engine]);
46
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
47
+ }
48
+ /**
49
+ * Hook to select the current upcoming turn-by-turn navigation instruction.
50
+ * Only re-renders when the instruction changes or triggers.
51
+ */
52
+ export function useNavInstruction(customEngine) {
53
+ return useNavSelector((state) => {
54
+ if (state.nextInstructionIndex === null)
55
+ return null;
56
+ let engine = customEngine;
57
+ if (!engine) {
58
+ try {
59
+ engine = useNavCoreContext().engine;
60
+ }
61
+ catch { /* ignore */ }
62
+ }
63
+ if (!engine)
64
+ return null;
65
+ const list = engine.getInstructions();
66
+ return list[state.nextInstructionIndex] ?? null;
67
+ }, customEngine, (a, b) => a === b || (a?.geometryIndex === b?.geometryIndex && a?.instruction === b?.instruction));
68
+ }
69
+ /**
70
+ * Hook to select current alert/deviation state ('NONE' | 'DEVIATING' | 'RECALCULATING' | 'REJOINING' | 'REJOINED').
71
+ * Only re-renders on alert state transitions.
72
+ */
73
+ export function useNavAlert(customEngine) {
74
+ return useNavSelector((state) => state.alertState, customEngine, Object.is);
75
+ }
76
+ /**
77
+ * Hook to select navigation progress and remaining distance metrics.
78
+ */
79
+ export function useNavProgress(customEngine) {
80
+ return useNavSelector((state) => ({
81
+ distanceToDestination: state.distanceToDestination,
82
+ distanceToNextInstruction: state.distanceToNextInstruction,
83
+ progressIndex: state.progressIndex,
84
+ routeIndex: state.routeIndex,
85
+ hasArrived: state.hasArrived,
86
+ }), customEngine, (a, b) => a.distanceToDestination === b.distanceToDestination &&
87
+ a.distanceToNextInstruction === b.distanceToNextInstruction &&
88
+ a.progressIndex === b.progressIndex &&
89
+ a.routeIndex === b.routeIndex &&
90
+ a.hasArrived === b.hasArrived);
91
+ }
92
+ /**
93
+ * Hook to select whether the vehicle is currently off-route.
94
+ */
95
+ export function useNavIsOffRoute(customEngine) {
96
+ return useNavSelector((state) => state.isOffRoute, customEngine, Object.is);
97
+ }
98
+ /**
99
+ * Hook to select current speed in m/s.
100
+ */
101
+ export function useNavSpeed(customEngine) {
102
+ return useNavSelector((state) => state.currentSpeed, customEngine, Object.is);
103
+ }
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@ingissa/navcore-react-native",
3
- "version": "1.0.3",
3
+ "version": "1.0.7",
4
4
  "description": "React Native bindings for the NavCore navigation engine.",
5
5
  "author": "Ingissa <8abnav@gmail.com>",
6
- "license": "Commercial",
7
- "homepage": "https://navcore-web.vercel.app/",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "homepage": "https://navcore.dev",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/ingissa/ingissa-navcore-examples.git"
10
+ "url": "git+https://github.com/ingissa/navcore-sdk.git"
11
11
  },
12
12
  "bugs": {
13
- "url": "https://github.com/ingissa/ingissa-navcore-examples/issues"
13
+ "url": "https://github.com/ingissa/navcore-sdk/issues"
14
14
  },
15
15
  "main": "dist/index.js",
16
16
  "module": "dist/index.mjs",
@@ -22,25 +22,33 @@
22
22
  "types": "./dist/index.d.ts"
23
23
  }
24
24
  },
25
- "files": ["dist"],
25
+ "files": [
26
+ "dist",
27
+ "src",
28
+ "LICENSE"
29
+ ],
26
30
  "sideEffects": false,
27
31
  "publishConfig": {
28
32
  "access": "public"
29
33
  },
30
- "engines": { "node": ">=18" },
34
+ "engines": {
35
+ "node": ">=16"
36
+ },
31
37
  "scripts": {
32
38
  "build": "tsc -p tsconfig.build.json",
33
39
  "lint": "tsc --noEmit",
34
40
  "prepublishOnly": "npm run build"
35
41
  },
36
42
  "peerDependencies": {
37
- "@ingissa/navcore-core": "^1.0.3",
43
+ "@ingissa/navcore-core": "^1.0.7",
38
44
  "react": ">=18.0.0",
39
45
  "react-native": ">=0.72.0",
40
46
  "react-native-maps": ">=1.0.0"
41
47
  },
42
48
  "peerDependenciesMeta": {
43
- "react-native-maps": { "optional": true }
49
+ "react-native-maps": {
50
+ "optional": true
51
+ }
44
52
  },
45
53
  "dependencies": {}
46
54
  }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * NavCoreProvider — React Context for shared NavCore engine access.
3
+ *
4
+ * Use when multiple components need access to the same engine instance
5
+ * (e.g. map view, instruction banner, debug panel).
6
+ */
7
+
8
+ import { createContext, useContext, useRef, useMemo, type ReactNode } from 'react';
9
+ import { NavCore, type NavCoreOptions } from '@ingissa/navcore-core';
10
+
11
+ interface NavCoreContextValue {
12
+ engine: NavCore;
13
+ }
14
+
15
+ const NavCoreContext = createContext<NavCoreContextValue | null>(null);
16
+
17
+ interface NavCoreProviderProps {
18
+ children: ReactNode;
19
+ options?: NavCoreOptions;
20
+ }
21
+
22
+ /**
23
+ * Wraps children with a shared NavCore engine instance.
24
+ *
25
+ * ```tsx
26
+ * <NavCoreProvider options={{ licenseKey: '...' }}>
27
+ * <MapScreen />
28
+ * <InstructionBanner />
29
+ * </NavCoreProvider>
30
+ * ```
31
+ */
32
+ export function NavCoreProvider({ children, options }: NavCoreProviderProps) {
33
+ const engineRef = useRef<NavCore | null>(null);
34
+ if (!engineRef.current) {
35
+ engineRef.current = new NavCore(options);
36
+ }
37
+
38
+ const value = useMemo(() => ({
39
+ engine: engineRef.current!,
40
+ }), []);
41
+
42
+ return (
43
+ <NavCoreContext.Provider value={value}>
44
+ {children}
45
+ </NavCoreContext.Provider>
46
+ );
47
+ }
48
+
49
+ /**
50
+ * Access the shared NavCore engine from context.
51
+ * Must be used within a <NavCoreProvider>.
52
+ */
53
+ export function useNavCoreContext(): NavCoreContextValue {
54
+ const ctx = useContext(NavCoreContext);
55
+ if (!ctx) {
56
+ throw new Error('useNavCoreContext must be used within a <NavCoreProvider>');
57
+ }
58
+ return ctx;
59
+ }
@@ -1,9 +1,18 @@
1
- /**
2
- * @navcore/react-native — React Native bindings
3
- *
4
- * Provides hooks for integrating NavCore with React Native apps.
5
- */
6
- export { useNavCore } from './useNavCore';
7
- export type { UseNavCoreProps, UseNavCoreReturn } from './useNavCore';
8
- export { NavCoreProvider, useNavCoreContext } from './NavCoreProvider';
9
- //# sourceMappingURL=index.d.ts.map
1
+ /**
2
+ * @navcore/react-native — React Native bindings
3
+ *
4
+ * Provides hooks for integrating NavCore with React Native apps.
5
+ */
6
+
7
+ export { useNavCore } from './useNavCore';
8
+ export type { UseNavCoreProps, UseNavCoreReturn } from './useNavCore';
9
+ export { NavCoreProvider, useNavCoreContext } from './NavCoreProvider';
10
+ export {
11
+ useNavSelector,
12
+ useNavInstruction,
13
+ useNavAlert,
14
+ useNavProgress,
15
+ useNavIsOffRoute,
16
+ useNavSpeed,
17
+ type NavProgressInfo,
18
+ } from './useNavSelectors';