@ingissa/navcore-simulator 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 (170) 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/{core → simulator/src/core}/NoiseEngine.d.ts +1 -1
  105. package/dist/simulator/src/core/NoiseEngine.d.ts.map +1 -0
  106. package/dist/{core → simulator/src/core}/RouteWalker.d.ts +4 -1
  107. package/dist/simulator/src/core/RouteWalker.d.ts.map +1 -0
  108. package/dist/{core → simulator/src/core}/RouteWalker.js +3 -0
  109. package/dist/simulator/src/core/SimulatorEngine.d.ts.map +1 -0
  110. package/dist/{core → simulator/src/core}/SimulatorEngine.js +23 -8
  111. package/dist/{core → simulator/src/core}/types.d.ts +30 -2
  112. package/dist/simulator/src/core/types.d.ts.map +1 -0
  113. package/dist/simulator/src/fleet/FleetSimulator.d.ts.map +1 -0
  114. package/dist/{index.d.ts → simulator/src/index.d.ts} +1 -1
  115. package/dist/simulator/src/index.d.ts.map +1 -0
  116. package/dist/{node.d.ts → simulator/src/node.d.ts} +1 -1
  117. package/dist/simulator/src/node.d.ts.map +1 -0
  118. package/dist/simulator/src/react-native/SimulatorControls.d.ts.map +1 -0
  119. package/dist/{react-native → simulator/src/react-native}/useSimulator.d.ts +6 -2
  120. package/dist/simulator/src/react-native/useSimulator.d.ts.map +1 -0
  121. package/dist/{react-native → simulator/src/react-native}/useSimulator.js +3 -0
  122. package/dist/{replay → simulator/src/replay}/GpxParser.d.ts +1 -1
  123. package/dist/simulator/src/replay/GpxParser.d.ts.map +1 -0
  124. package/dist/simulator/src/replay/SessionRecorder.d.ts.map +1 -0
  125. package/dist/simulator/src/scenarios/ScenarioInjector.d.ts.map +1 -0
  126. package/package.json +20 -10
  127. package/src/__tests__/integration/simulator-pipeline.test.ts +233 -0
  128. package/src/core/NoiseEngine.ts +137 -0
  129. package/src/core/RouteWalker.ts +227 -0
  130. package/src/core/SimulatorEngine.ts +364 -0
  131. package/src/core/__tests__/NoiseEngine.test.ts +131 -0
  132. package/src/core/__tests__/RouteWalker.test.ts +139 -0
  133. package/src/core/__tests__/SimulatorEngine.test.ts +317 -0
  134. package/src/core/types.ts +191 -0
  135. package/src/fleet/FleetSimulator.ts +162 -0
  136. package/src/index.ts +54 -0
  137. package/src/node.ts +48 -0
  138. package/src/react-native/SimulatorControls.tsx +473 -0
  139. package/src/react-native/useSimulator.ts +155 -0
  140. package/src/replay/GpxParser.ts +122 -0
  141. package/src/replay/SessionRecorder.ts +138 -0
  142. package/src/replay/__tests__/GpxParser.test.ts +136 -0
  143. package/src/scenarios/ScenarioInjector.ts +170 -0
  144. package/src/scenarios/__tests__/ScenarioInjector.test.ts +171 -0
  145. package/dist/core/NoiseEngine.d.ts.map +0 -1
  146. package/dist/core/RouteWalker.d.ts.map +0 -1
  147. package/dist/core/SimulatorEngine.d.ts.map +0 -1
  148. package/dist/core/types.d.ts.map +0 -1
  149. package/dist/fleet/FleetSimulator.d.ts.map +0 -1
  150. package/dist/index.d.ts.map +0 -1
  151. package/dist/node.d.ts.map +0 -1
  152. package/dist/react-native/SimulatorControls.d.ts.map +0 -1
  153. package/dist/react-native/useSimulator.d.ts.map +0 -1
  154. package/dist/replay/GpxParser.d.ts.map +0 -1
  155. package/dist/replay/SessionRecorder.d.ts.map +0 -1
  156. package/dist/scenarios/ScenarioInjector.d.ts.map +0 -1
  157. /package/dist/{core → simulator/src/core}/NoiseEngine.js +0 -0
  158. /package/dist/{core → simulator/src/core}/SimulatorEngine.d.ts +0 -0
  159. /package/dist/{core → simulator/src/core}/types.js +0 -0
  160. /package/dist/{fleet → simulator/src/fleet}/FleetSimulator.d.ts +0 -0
  161. /package/dist/{fleet → simulator/src/fleet}/FleetSimulator.js +0 -0
  162. /package/dist/{index.js → simulator/src/index.js} +0 -0
  163. /package/dist/{node.js → simulator/src/node.js} +0 -0
  164. /package/dist/{react-native → simulator/src/react-native}/SimulatorControls.d.ts +0 -0
  165. /package/dist/{react-native → simulator/src/react-native}/SimulatorControls.js +0 -0
  166. /package/dist/{replay → simulator/src/replay}/GpxParser.js +0 -0
  167. /package/dist/{replay → simulator/src/replay}/SessionRecorder.d.ts +0 -0
  168. /package/dist/{replay → simulator/src/replay}/SessionRecorder.js +0 -0
  169. /package/dist/{scenarios → simulator/src/scenarios}/ScenarioInjector.d.ts +0 -0
  170. /package/dist/{scenarios → simulator/src/scenarios}/ScenarioInjector.js +0 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * LicenseManager — Handles offline asymmetric license verification
3
+ * using P-256 + granular feature licensing.
4
+ *
5
+ * Matching nav-sdk/src/shared/LicenseManager.ts contract exactly.
6
+ */
7
+ import { FEATURES } from '../features';
8
+ import { verifyP256Signature } from './verifyP256Signature';
9
+ export class LicenseManager {
10
+ constructor(licenseKey, isDev) {
11
+ this.status = 'MISSING';
12
+ this.payload = null;
13
+ this.enabledFeatures = new Set();
14
+ this.warnedFeatures = new Set();
15
+ this.isDev = false;
16
+ this.isDev = isDev ?? false;
17
+ if (licenseKey) {
18
+ this.validate(licenseKey);
19
+ }
20
+ }
21
+ // ── Validation ──────────────────────────────────────────────────────────────
22
+ async validate(key) {
23
+ try {
24
+ if (this.status === 'VALID' && this.payload?.bid === 'dev.bypass') {
25
+ return 'VALID'; // Already bypassed synchronously
26
+ }
27
+ const [payloadB64, signatureB64] = key.split('.');
28
+ if (!payloadB64 || !signatureB64) {
29
+ this.status = 'INVALID';
30
+ return this.status;
31
+ }
32
+ const payloadStr = atob(payloadB64);
33
+ this.payload = JSON.parse(payloadStr);
34
+ // Expiration check
35
+ if (!this.payload || this.payload.exp < Date.now()) {
36
+ this.status = 'EXPIRED';
37
+ return this.status;
38
+ }
39
+ // Signature check
40
+ const isValid = await this.verifySignature(payloadB64, signatureB64);
41
+ if (!isValid) {
42
+ this.status = 'INVALID';
43
+ return this.status;
44
+ }
45
+ this.status = 'VALID';
46
+ this.extractFeatures();
47
+ console.log(`🔑 [NavCore] License validation complete. Plan: ${this.isProEnabled() ? 'pro' : 'core'}`);
48
+ }
49
+ catch {
50
+ this.status = 'INVALID';
51
+ }
52
+ return this.status;
53
+ }
54
+ // ── Feature system ──────────────────────────────────────────────────────────
55
+ extractFeatures() {
56
+ this.enabledFeatures.clear();
57
+ if (!this.payload)
58
+ return;
59
+ // New granular feature flags
60
+ if (Array.isArray(this.payload.f)) {
61
+ for (const f of this.payload.f) {
62
+ this.enabledFeatures.add(f);
63
+ }
64
+ }
65
+ // Backward compatibility: pro tier = all features
66
+ if (this.payload.t === 'pro') {
67
+ this.enabledFeatures.add('*');
68
+ }
69
+ }
70
+ hasFeature(feature) {
71
+ if (this.isDev)
72
+ return true;
73
+ if (this.status !== 'VALID')
74
+ return false;
75
+ if (this.enabledFeatures.has('*'))
76
+ return true;
77
+ return this.enabledFeatures.has(feature);
78
+ }
79
+ /** Warn once per feature when a consumer tries to use a gated feature. */
80
+ warnOnce(feature) {
81
+ if (this.warnedFeatures.has(feature))
82
+ return;
83
+ console.warn(`[NavCore] Feature '${feature}' requires a Pro license`);
84
+ this.warnedFeatures.add(feature);
85
+ }
86
+ // ── Legacy / convenience helpers ────────────────────────────────────────────
87
+ /** Returns true if the license grants the full Pro tier (all features). */
88
+ isProEnabled() {
89
+ // Matches nav-sdk: checks for the wildcard '*' grant, not a specific feature
90
+ return this.hasFeature('*');
91
+ }
92
+ isAIEnabled() {
93
+ return this.hasFeature(FEATURES.NAV_AI);
94
+ }
95
+ getStatus() {
96
+ return this.status;
97
+ }
98
+ getPayload() {
99
+ return this.payload;
100
+ }
101
+ getEnabledFeatures() {
102
+ return Array.from(this.enabledFeatures);
103
+ }
104
+ // ── Crypto ──────────────────────────────────────────────────────────────────
105
+ async verifySignature(data, signature) {
106
+ try {
107
+ const encoder = new TextEncoder();
108
+ const encodedData = encoder.encode(data);
109
+ const signatureBin = Uint8Array.from(atob(signature), c => c.charCodeAt(0));
110
+ const publicKeyBin = Uint8Array.from(atob(LicenseManager.PUBLIC_KEY_RAW), c => c.charCodeAt(0));
111
+ return await verifyP256Signature(encodedData, signatureBin, publicKeyBin);
112
+ }
113
+ catch {
114
+ return false;
115
+ }
116
+ }
117
+ }
118
+ // SEC1 uncompressed form of the shared ECDSA P-256 public key.
119
+ LicenseManager.PUBLIC_KEY_RAW = 'BJOqJyZiZFrURbrIkkuvXXHTjV+MS9kp5uAGTvnWI0sp3yYi+Me7axMvNDCJi37ZJwVk/6iNM+6MeEO1TS97Xug=';
@@ -0,0 +1,2 @@
1
+ export declare function verifyP256Signature(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): Promise<boolean>;
2
+ //# sourceMappingURL=verifyP256Signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyP256Signature.d.ts","sourceRoot":"","sources":["../../../../../core/src/license/verifyP256Signature.ts"],"names":[],"mappings":"AAQA,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,OAAO,CAAC,CA+BlB"}
@@ -0,0 +1,26 @@
1
+ import { p256 } from '@noble/curves/nist.js';
2
+ function toArrayBuffer(bytes) {
3
+ const buffer = new ArrayBuffer(bytes.byteLength);
4
+ new Uint8Array(buffer).set(bytes);
5
+ return buffer;
6
+ }
7
+ export async function verifyP256Signature(message, signature, publicKey) {
8
+ if (signature.length !== 64 || publicKey.length !== 65 || publicKey[0] !== 4) {
9
+ return false;
10
+ }
11
+ try {
12
+ const subtle = typeof crypto !== 'undefined' ? crypto.subtle : undefined;
13
+ if (subtle) {
14
+ const importedKey = await subtle.importKey('raw', toArrayBuffer(publicKey), { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);
15
+ return await subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-256' } }, importedKey, toArrayBuffer(signature), toArrayBuffer(message));
16
+ }
17
+ return p256.verify(signature, message, publicKey, {
18
+ format: 'compact',
19
+ lowS: false,
20
+ prehash: true,
21
+ });
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Module 9a: NavigationFSM
3
+ * Navigation lifecycle state machine. Pure deterministic transitions.
4
+ */
5
+ export type NavLifecycleState = 'IDLE' | 'OVERVIEW' | 'APPROACHING_ROUTE' | 'ON_ROUTE';
6
+ export type LifecycleEvent = {
7
+ type: 'ROUTE_LOADED';
8
+ } | {
9
+ type: 'USER_START_NAVIGATION';
10
+ } | {
11
+ type: 'ROUTE_JOINED';
12
+ data: {
13
+ index: number;
14
+ };
15
+ } | {
16
+ type: 'NAVIGATION_FINISHED';
17
+ } | {
18
+ type: 'CANCEL_NAVIGATION';
19
+ } | {
20
+ type: 'RESET';
21
+ };
22
+ export interface LifecycleContext {
23
+ resumeIndex: number | null;
24
+ }
25
+ export interface LifecycleOutput {
26
+ state: NavLifecycleState;
27
+ context: LifecycleContext;
28
+ }
29
+ /**
30
+ * Pure lifecycle FSM — no React, no side effects.
31
+ */
32
+ export declare class NavigationFSM {
33
+ private _state;
34
+ private _context;
35
+ get state(): NavLifecycleState;
36
+ get context(): LifecycleContext;
37
+ /** Dispatch an event and return the new output */
38
+ dispatch(event: LifecycleEvent): LifecycleOutput;
39
+ /** Reset to initial state */
40
+ reset(): void;
41
+ }
42
+ //# sourceMappingURL=NavigationFSM.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigationFSM.d.ts","sourceRoot":"","sources":["../../../../../core/src/off-route/NavigationFSM.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,UAAU,GACV,mBAAmB,GACnB,UAAU,CAAC;AAEf,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,uBAAuB,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,EAAE,gBAAgB,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAA2C;IAE3D,IAAI,KAAK,IAAI,iBAAiB,CAAwB;IACtD,IAAI,OAAO,IAAI,gBAAgB,CAAiC;IAEhE,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe;IAqChD,6BAA6B;IAC7B,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Module 9a: NavigationFSM
3
+ * Navigation lifecycle state machine. Pure deterministic transitions.
4
+ */
5
+ /**
6
+ * Pure lifecycle FSM — no React, no side effects.
7
+ */
8
+ export class NavigationFSM {
9
+ constructor() {
10
+ this._state = 'IDLE';
11
+ this._context = { resumeIndex: null };
12
+ }
13
+ get state() { return this._state; }
14
+ get context() { return { ...this._context }; }
15
+ /** Dispatch an event and return the new output */
16
+ dispatch(event) {
17
+ const prev = this._state;
18
+ switch (prev) {
19
+ case 'IDLE':
20
+ if (event.type === 'ROUTE_LOADED')
21
+ this._state = 'OVERVIEW';
22
+ if (event.type === 'USER_START_NAVIGATION')
23
+ this._state = 'APPROACHING_ROUTE';
24
+ break;
25
+ case 'OVERVIEW':
26
+ if (event.type === 'USER_START_NAVIGATION')
27
+ this._state = 'APPROACHING_ROUTE';
28
+ break;
29
+ case 'APPROACHING_ROUTE':
30
+ if (event.type === 'ROUTE_JOINED') {
31
+ this._state = 'ON_ROUTE';
32
+ this._context.resumeIndex = event.data.index;
33
+ }
34
+ if (event.type === 'NAVIGATION_FINISHED')
35
+ this._state = 'IDLE';
36
+ break;
37
+ case 'ON_ROUTE':
38
+ if (event.type === 'NAVIGATION_FINISHED')
39
+ this._state = 'IDLE';
40
+ if (event.type === 'ROUTE_JOINED') {
41
+ this._context.resumeIndex = event.data.index;
42
+ }
43
+ break;
44
+ }
45
+ if (event.type === 'RESET' || event.type === 'CANCEL_NAVIGATION') {
46
+ this._state = 'IDLE';
47
+ this._context = { resumeIndex: null };
48
+ }
49
+ return { state: this._state, context: { ...this._context } };
50
+ }
51
+ /** Reset to initial state */
52
+ reset() {
53
+ this._state = 'IDLE';
54
+ this._context = { resumeIndex: null };
55
+ }
56
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Module 9b: OffRouteFSM
3
+ * Deviation → Reroute → Rejoin state machine. Pure deterministic transitions.
4
+ * Extracted from useOffRouteSystem / NavigationStateManager (stripped of React).
5
+ */
6
+ import type { Coordinate } from '../geo/haversine';
7
+ export type OffRouteState = 'ON_ROUTE' | 'DEVIATING' | 'OFF_ROUTE' | 'REROUTING' | 'REJOINING' | 'REJOINED' | 'FAILED';
8
+ export interface TempRoute {
9
+ geometry: Coordinate[];
10
+ distance: number;
11
+ duration: number;
12
+ }
13
+ export interface RejoinTarget {
14
+ coordinates: Coordinate;
15
+ index: number;
16
+ }
17
+ export interface OffRouteContext {
18
+ deviationSegmentIdx: number | null;
19
+ deviationStartMs: number | null;
20
+ tempRoute: TempRoute | null;
21
+ rejoinTarget: RejoinTarget | null;
22
+ lastRerouteMs: number | null;
23
+ rejoinedAtMs: number | null;
24
+ rejoinDwellFrames: number;
25
+ failureReason: string | null;
26
+ }
27
+ export type OffRouteEvent = {
28
+ type: 'DEVIATION_SUSPECTED';
29
+ deviationSegmentIdx: number;
30
+ } | {
31
+ type: 'DEVIATION_CONFIRMED';
32
+ deviationSegmentIdx: number;
33
+ } | {
34
+ type: 'REROUTE_STARTED';
35
+ } | {
36
+ type: 'REROUTE_SUCCESS';
37
+ tempRoute: TempRoute;
38
+ rejoinTarget: RejoinTarget;
39
+ } | {
40
+ type: 'REROUTE_FAILED';
41
+ reason: string;
42
+ } | {
43
+ type: 'REJOIN_DETECTED';
44
+ } | {
45
+ type: 'REJOIN_CONFIRMED';
46
+ } | {
47
+ type: 'RESUME';
48
+ } | {
49
+ type: 'RESET';
50
+ };
51
+ export interface OffRouteOutput {
52
+ state: OffRouteState;
53
+ context: OffRouteContext;
54
+ }
55
+ /**
56
+ * Pure off-route FSM — no React, no side effects.
57
+ */
58
+ export declare class OffRouteFSM {
59
+ private _state;
60
+ private _context;
61
+ get state(): OffRouteState;
62
+ get context(): OffRouteContext;
63
+ /** Dispatch an event and return the new output */
64
+ dispatch(event: OffRouteEvent, nowMs?: number): OffRouteOutput;
65
+ /** Full reset */
66
+ reset(): void;
67
+ /** Check if deviation confirmation time has elapsed */
68
+ isDeviationConfirmed(nowMs?: number): boolean;
69
+ }
70
+ //# sourceMappingURL=OffRouteFSM.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OffRouteFSM.d.ts","sourceRoot":"","sources":["../../../../../core/src/off-route/OffRouteFSM.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,QAAQ,CAAC;AAEb,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,UAAU,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAE,GAC7E;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAC5B;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,eAAe,CAAC;CAC1B;AAgBD;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAA2C;IAE3D,IAAI,KAAK,IAAI,aAAa,CAAwB;IAClD,IAAI,OAAO,IAAI,eAAe,CAAiC;IAE/D,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,cAAc;IAwH9D,iBAAiB;IACjB,KAAK,IAAI,IAAI;IAKb,uDAAuD;IACvD,oBAAoB,CAAC,KAAK,GAAE,MAAmB,GAAG,OAAO;CAI1D"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Module 9b: OffRouteFSM
3
+ * Deviation → Reroute → Rejoin state machine. Pure deterministic transitions.
4
+ * Extracted from useOffRouteSystem / NavigationStateManager (stripped of React).
5
+ */
6
+ const INITIAL_CONTEXT = {
7
+ deviationSegmentIdx: null,
8
+ deviationStartMs: null,
9
+ tempRoute: null,
10
+ rejoinTarget: null,
11
+ lastRerouteMs: null,
12
+ rejoinedAtMs: null,
13
+ rejoinDwellFrames: 0,
14
+ failureReason: null,
15
+ };
16
+ const DEVIATION_CONFIRM_MS = 2000;
17
+ const REJOIN_DWELL_THRESHOLD = 3;
18
+ /**
19
+ * Pure off-route FSM — no React, no side effects.
20
+ */
21
+ export class OffRouteFSM {
22
+ constructor() {
23
+ this._state = 'ON_ROUTE';
24
+ this._context = { ...INITIAL_CONTEXT };
25
+ }
26
+ get state() { return this._state; }
27
+ get context() { return { ...this._context }; }
28
+ /** Dispatch an event and return the new output */
29
+ dispatch(event, nowMs) {
30
+ const now = nowMs ?? Date.now();
31
+ switch (this._state) {
32
+ case 'ON_ROUTE':
33
+ if (event.type === 'DEVIATION_SUSPECTED') {
34
+ this._state = 'DEVIATING';
35
+ this._context.deviationSegmentIdx = event.deviationSegmentIdx;
36
+ this._context.deviationStartMs = now;
37
+ }
38
+ break;
39
+ case 'DEVIATING':
40
+ if (event.type === 'DEVIATION_CONFIRMED') {
41
+ const elapsed = this._context.deviationStartMs !== null
42
+ ? now - this._context.deviationStartMs
43
+ : 0;
44
+ if (elapsed >= DEVIATION_CONFIRM_MS) {
45
+ this._state = 'OFF_ROUTE';
46
+ this._context.deviationSegmentIdx = event.deviationSegmentIdx;
47
+ }
48
+ }
49
+ if (event.type === 'RESUME' || event.type === 'RESET') {
50
+ this._state = 'ON_ROUTE';
51
+ // RESUME preserves lastRerouteMs (cooldown), RESET clears everything
52
+ if (event.type === 'RESET') {
53
+ this._context = { ...INITIAL_CONTEXT };
54
+ }
55
+ else {
56
+ this._context.deviationSegmentIdx = null;
57
+ this._context.deviationStartMs = null;
58
+ }
59
+ }
60
+ break;
61
+ case 'OFF_ROUTE':
62
+ if (event.type === 'REROUTE_STARTED') {
63
+ this._state = 'REROUTING';
64
+ this._context.lastRerouteMs = now;
65
+ }
66
+ if (event.type === 'RESUME') {
67
+ this._state = 'ON_ROUTE';
68
+ this._context.deviationSegmentIdx = null;
69
+ this._context.deviationStartMs = null;
70
+ }
71
+ if (event.type === 'RESET') {
72
+ this._state = 'ON_ROUTE';
73
+ this._context = { ...INITIAL_CONTEXT };
74
+ }
75
+ break;
76
+ case 'REROUTING':
77
+ if (event.type === 'REROUTE_SUCCESS') {
78
+ this._state = 'REJOINING';
79
+ this._context.tempRoute = event.tempRoute;
80
+ this._context.rejoinTarget = event.rejoinTarget;
81
+ }
82
+ if (event.type === 'REROUTE_FAILED') {
83
+ this._state = 'FAILED';
84
+ this._context.failureReason = event.reason;
85
+ }
86
+ break;
87
+ case 'REJOINING':
88
+ if (event.type === 'REJOIN_DETECTED') {
89
+ this._context.rejoinDwellFrames++;
90
+ }
91
+ if (event.type === 'REJOIN_CONFIRMED' ||
92
+ this._context.rejoinDwellFrames >= REJOIN_DWELL_THRESHOLD) {
93
+ this._state = 'REJOINED';
94
+ this._context.rejoinedAtMs = now;
95
+ this._context.rejoinDwellFrames = 0;
96
+ }
97
+ if (event.type === 'RESUME') {
98
+ this._state = 'ON_ROUTE';
99
+ this._context.tempRoute = null;
100
+ this._context.rejoinTarget = null;
101
+ this._context.rejoinedAtMs = null;
102
+ this._context.deviationSegmentIdx = null;
103
+ this._context.deviationStartMs = null;
104
+ this._context.rejoinDwellFrames = 0;
105
+ }
106
+ if (event.type === 'RESET') {
107
+ this._state = 'ON_ROUTE';
108
+ this._context = { ...INITIAL_CONTEXT };
109
+ }
110
+ break;
111
+ case 'REJOINED':
112
+ if (event.type === 'RESUME') {
113
+ this._state = 'ON_ROUTE';
114
+ this._context.tempRoute = null;
115
+ this._context.rejoinTarget = null;
116
+ this._context.rejoinedAtMs = null;
117
+ this._context.deviationSegmentIdx = null;
118
+ this._context.deviationStartMs = null;
119
+ }
120
+ if (event.type === 'RESET') {
121
+ this._state = 'ON_ROUTE';
122
+ this._context = { ...INITIAL_CONTEXT };
123
+ }
124
+ break;
125
+ case 'FAILED':
126
+ if (event.type === 'RESUME') {
127
+ this._state = 'ON_ROUTE';
128
+ this._context.failureReason = null;
129
+ this._context.deviationSegmentIdx = null;
130
+ this._context.deviationStartMs = null;
131
+ }
132
+ if (event.type === 'RESET') {
133
+ this._state = 'ON_ROUTE';
134
+ this._context = { ...INITIAL_CONTEXT };
135
+ }
136
+ break;
137
+ }
138
+ return { state: this._state, context: { ...this._context } };
139
+ }
140
+ /** Full reset */
141
+ reset() {
142
+ this._state = 'ON_ROUTE';
143
+ this._context = { ...INITIAL_CONTEXT };
144
+ }
145
+ /** Check if deviation confirmation time has elapsed */
146
+ isDeviationConfirmed(nowMs = Date.now()) {
147
+ if (!this._context.deviationStartMs)
148
+ return false;
149
+ return (nowMs - this._context.deviationStartMs) >= DEVIATION_CONFIRM_MS;
150
+ }
151
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Module 8: RouteOffsetPredictor
3
+ * Linear distance-based position predictor for smooth rendering between GPS fixes.
4
+ */
5
+ import { type Coordinate } from '../geo/haversine';
6
+ /**
7
+ * Advance the predictor's offset by speed * dt.
8
+ */
9
+ export declare function advancePredictor(currentOffset: number, speedMps: number, dtSec: number): number;
10
+ /**
11
+ * Correct the predictor when a new GPS fix arrives.
12
+ * Blend towards the actual measured offset using betaCorrection.
13
+ */
14
+ export declare function applyGpsCorrection(predictedOffset: number, measuredOffset: number, betaCorrection?: number): number;
15
+ /**
16
+ * Given a linear offset (metres from route start), find the [lng, lat] on the route.
17
+ *
18
+ * @param offset Linear distance from route start in metres
19
+ * @param cumulativeDistances Forward cumulative distance array
20
+ * @param route Route coordinates
21
+ * @returns Interpolated coordinate on the route
22
+ */
23
+ export declare function pointAtOffset(offset: number, cumulativeDistances: Float64Array, route: Coordinate[]): Coordinate;
24
+ //# sourceMappingURL=RouteOffsetPredictor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteOffsetPredictor.d.ts","sourceRoot":"","sources":["../../../../../core/src/predictor/RouteOffsetPredictor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,EACtB,cAAc,GAAE,MAAa,GAC5B,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,YAAY,EACjC,KAAK,EAAE,UAAU,EAAE,GAClB,UAAU,CA2BZ"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Module 8: RouteOffsetPredictor
3
+ * Linear distance-based position predictor for smooth rendering between GPS fixes.
4
+ */
5
+ import { interpolatePoint } from '../geo/bearing';
6
+ /**
7
+ * Advance the predictor's offset by speed * dt.
8
+ */
9
+ export function advancePredictor(currentOffset, speedMps, dtSec) {
10
+ return currentOffset + speedMps * dtSec;
11
+ }
12
+ /**
13
+ * Correct the predictor when a new GPS fix arrives.
14
+ * Blend towards the actual measured offset using betaCorrection.
15
+ */
16
+ export function applyGpsCorrection(predictedOffset, measuredOffset, betaCorrection = 0.15) {
17
+ const error = measuredOffset - predictedOffset;
18
+ return predictedOffset + betaCorrection * error;
19
+ }
20
+ /**
21
+ * Given a linear offset (metres from route start), find the [lng, lat] on the route.
22
+ *
23
+ * @param offset Linear distance from route start in metres
24
+ * @param cumulativeDistances Forward cumulative distance array
25
+ * @param route Route coordinates
26
+ * @returns Interpolated coordinate on the route
27
+ */
28
+ export function pointAtOffset(offset, cumulativeDistances, route) {
29
+ if (route.length === 0)
30
+ return [0, 0];
31
+ if (offset <= 0)
32
+ return route[0];
33
+ const totalLength = cumulativeDistances[cumulativeDistances.length - 1] ?? 0;
34
+ if (offset >= totalLength)
35
+ return route[route.length - 1];
36
+ // Binary search for the segment containing this offset
37
+ let lo = 0;
38
+ let hi = cumulativeDistances.length - 1;
39
+ while (lo < hi - 1) {
40
+ const mid = (lo + hi) >> 1;
41
+ if (cumulativeDistances[mid] <= offset) {
42
+ lo = mid;
43
+ }
44
+ else {
45
+ hi = mid;
46
+ }
47
+ }
48
+ const segStart = cumulativeDistances[lo];
49
+ const segEnd = cumulativeDistances[hi];
50
+ const segLength = segEnd - segStart;
51
+ if (segLength === 0)
52
+ return route[lo];
53
+ const t = (offset - segStart) / segLength;
54
+ return interpolatePoint(route[lo], route[hi], t);
55
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * InstructionEditor — Fluent builder for NavInstruction arrays.
3
+ *
4
+ * Create, edit, and annotate navigation instructions for custom routes.
5
+ * Supports standard turn instructions and exam-specific annotations.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const editor = new InstructionEditor();
10
+ * editor
11
+ * .addTurn([2.347, 48.859], 'Turn left onto Rue de Rivoli', 42)
12
+ * .addExamPoint([2.350, 48.860], 'Check mirrors before changing lane', 50)
13
+ * .addCheckpoint([2.355, 48.861], 65);
14
+ *
15
+ * editor.attachToCumulative(routeGeometry);
16
+ * engine.setRoute(routeGeometry, editor.toArray());
17
+ * ```
18
+ */
19
+ import type { Coordinate } from '../geo/haversine';
20
+ import type { NavInstruction } from './InstructionResolver';
21
+ export type InstructionType = 'turn' | 'exam_point' | 'checkpoint' | 'depart' | 'arrive' | 'custom';
22
+ export interface RichInstruction extends NavInstruction {
23
+ /** Instruction category */
24
+ type: InstructionType;
25
+ /** Unique id for referencing in edit operations */
26
+ id: string;
27
+ /** Display text (same as NavInstruction.text) */
28
+ text: string;
29
+ /** Whether to suppress voice read-out */
30
+ silent?: boolean;
31
+ /** Exam-specific severity level */
32
+ severity?: 'info' | 'warning' | 'critical';
33
+ /** Custom metadata for integrators */
34
+ meta?: Record<string, unknown>;
35
+ }
36
+ export declare class InstructionEditor {
37
+ private instructions;
38
+ /** Add a turn instruction at the given route geometry index */
39
+ addTurn(location: Coordinate, text: string, geometryIndex: number, opts?: {
40
+ silent?: boolean;
41
+ meta?: Record<string, unknown>;
42
+ }): this;
43
+ /** Add an exam-specific point (e.g. "Check mirrors", "Yield here") */
44
+ addExamPoint(location: Coordinate, text: string, geometryIndex: number, opts?: {
45
+ severity?: RichInstruction['severity'];
46
+ silent?: boolean;
47
+ meta?: Record<string, unknown>;
48
+ }): this;
49
+ /** Add a silent progress checkpoint (does not trigger voice) */
50
+ addCheckpoint(location: Coordinate, geometryIndex: number, meta?: Record<string, unknown>): this;
51
+ /** Add a departure instruction (auto-inserted at index 0) */
52
+ addDepart(location: Coordinate, text?: string): this;
53
+ /** Add an arrival instruction */
54
+ addArrive(location: Coordinate, text?: string): this;
55
+ /** Update any field of an instruction by id */
56
+ update(id: string, patch: Partial<Omit<RichInstruction, 'id'>>): this;
57
+ /** Remove an instruction by id */
58
+ remove(id: string): this;
59
+ /** Move an instruction to a new position in the list */
60
+ reorder(id: string, newIndex: number): this;
61
+ /** Sort instructions by geometryIndex ascending */
62
+ sort(): this;
63
+ /** Remove all instructions */
64
+ clear(): this;
65
+ /**
66
+ * Compute and attach `cumulativeOffset` and `bearingBefore`/`bearingAfter`
67
+ * from the route geometry. Call this after loading the route but before
68
+ * passing instructions to the engine.
69
+ */
70
+ attachToCumulative(route: Coordinate[]): this;
71
+ /** Export as plain NavInstruction[] for use with engine.setRoute() */
72
+ toArray(): NavInstruction[];
73
+ /** Export rich instructions with all metadata */
74
+ toRichArray(): ReadonlyArray<RichInstruction>;
75
+ get size(): number;
76
+ }
77
+ //# sourceMappingURL=InstructionEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InstructionEditor.d.ts","sourceRoot":"","sources":["../../../../../core/src/progress/InstructionEditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpG,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,2BAA2B;IAC3B,IAAI,EAAE,eAAe,CAAC;IACtB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC3C,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAOD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,YAAY,CAAyB;IAI7C,+DAA+D;IAC/D,OAAO,CACL,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAC1D,IAAI;IASP,sEAAsE;IACtE,YAAY,CACV,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAClG,IAAI;IAWP,gEAAgE;IAChE,aAAa,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAShG,6DAA6D;IAC7D,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,SAAqB,GAAG,IAAI;IAShE,iCAAiC;IACjC,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,SAAqB,GAAG,IAAI;IAWhE,+CAA+C;IAC/C,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI;IAOrE,kCAAkC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAOxB,wDAAwD;IACxD,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ3C,mDAAmD;IACnD,IAAI,IAAI,IAAI;IAKZ,8BAA8B;IAC9B,KAAK,IAAI,IAAI;IAOb;;;;OAIG;IACH,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI;IA0B7C,sEAAsE;IACtE,OAAO,IAAI,cAAc,EAAE;IAI3B,iDAAiD;IACjD,WAAW,IAAI,aAAa,CAAC,eAAe,CAAC;IAI7C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}