@kafitra/react-native-live-tracking 0.1.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.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +396 -0
  3. package/android/build.gradle +71 -0
  4. package/android/gradle.properties +7 -0
  5. package/android/src/main/AndroidManifest.xml +40 -0
  6. package/android/src/main/java/com/livetracking/LiveTrackingModuleImpl.kt +728 -0
  7. package/android/src/main/java/com/livetracking/LiveTrackingPackage.kt +16 -0
  8. package/android/src/main/java/com/livetracking/location/LocationEngine.kt +93 -0
  9. package/android/src/main/java/com/livetracking/network/NetworkListener.kt +127 -0
  10. package/android/src/main/java/com/livetracking/optimizer/ActivityRecognitionHandler.kt +248 -0
  11. package/android/src/main/java/com/livetracking/optimizer/MotionSleepManager.kt +130 -0
  12. package/android/src/main/java/com/livetracking/permissions/PermissionHandler.kt +145 -0
  13. package/android/src/main/java/com/livetracking/queue/QueueEngine.kt +167 -0
  14. package/android/src/main/java/com/livetracking/queue/QueuedLocation.kt +16 -0
  15. package/android/src/main/java/com/livetracking/queue/TrackingDatabase.kt +239 -0
  16. package/android/src/main/java/com/livetracking/receiver/BootReceiver.kt +53 -0
  17. package/android/src/main/java/com/livetracking/service/TrackingForegroundService.kt +145 -0
  18. package/android/src/main/java/com/livetracking/sync/FirebaseSyncEngine.kt +277 -0
  19. package/android/src/main/java/com/livetracking/sync/LocationDataPoint.kt +31 -0
  20. package/android/src/main/java/com/livetracking/sync/SyncEngineController.kt +220 -0
  21. package/android/src/main/java/com/livetracking/sync/SyncTargetConfig.kt +20 -0
  22. package/android/src/main/java/com/livetracking/sync/TargetHandler.kt +601 -0
  23. package/android/src/newarch/java/com/livetracking/LiveTrackingModule.kt +64 -0
  24. package/android/src/oldarch/java/com/livetracking/LiveTrackingModule.kt +70 -0
  25. package/android/src/test/java/com/livetracking/BackoffCalculationTest.kt +216 -0
  26. package/android/src/test/java/com/livetracking/BatchAccumulatorTest.kt +391 -0
  27. package/android/src/test/java/com/livetracking/BootReceiverTest.kt +247 -0
  28. package/android/src/test/java/com/livetracking/FirebaseSyncEngineTest.kt +337 -0
  29. package/android/src/test/java/com/livetracking/LocationEngineTest.kt +202 -0
  30. package/android/src/test/java/com/livetracking/MotionSleepManagerTest.kt +420 -0
  31. package/android/src/test/java/com/livetracking/OfflineQueueTest.kt +462 -0
  32. package/android/src/test/java/com/livetracking/PermissionHandlerTest.kt +200 -0
  33. package/android/src/test/java/com/livetracking/QueueEngineTest.kt +335 -0
  34. package/android/src/test/java/com/livetracking/SyncEngineControllerTest.kt +855 -0
  35. package/ios/ActivityRecognitionHandler.swift +196 -0
  36. package/ios/BackgroundModeHelper.swift +132 -0
  37. package/ios/FirebaseSyncEngine.swift +276 -0
  38. package/ios/LiveTracking-Bridging-Header.h +2 -0
  39. package/ios/LiveTracking.m +37 -0
  40. package/ios/LiveTracking.swift +773 -0
  41. package/ios/LocationDataPoint.swift +56 -0
  42. package/ios/LocationEngine.swift +160 -0
  43. package/ios/MotionSleepManager.swift +151 -0
  44. package/ios/NetworkListener.swift +105 -0
  45. package/ios/OfflineQueueManager.swift +503 -0
  46. package/ios/PermissionHandler.swift +148 -0
  47. package/ios/QueueEngine.swift +249 -0
  48. package/ios/SyncEngineController.swift +396 -0
  49. package/ios/SyncTargetConfig.swift +36 -0
  50. package/ios/TargetHandler.swift +715 -0
  51. package/ios/Tests/ActivityRecognitionHandlerTests.swift +259 -0
  52. package/ios/Tests/FirebaseSyncEngineTests.swift +303 -0
  53. package/ios/Tests/LocationEngineTests.swift +244 -0
  54. package/ios/Tests/MotionSleepManagerTests.swift +355 -0
  55. package/ios/Tests/NetworkListenerTests.swift +188 -0
  56. package/ios/Tests/OfflineQueueFlushTests.swift +375 -0
  57. package/ios/Tests/PermissionHandlerTests.swift +238 -0
  58. package/ios/Tests/QueueEngineTests.swift +346 -0
  59. package/ios/TrackingCleanup.swift +93 -0
  60. package/ios/TrackingNotificationManager.swift +187 -0
  61. package/lib/commonjs/EventEmitter.js +113 -0
  62. package/lib/commonjs/EventEmitter.js.map +1 -0
  63. package/lib/commonjs/LiveTracking.js +134 -0
  64. package/lib/commonjs/LiveTracking.js.map +1 -0
  65. package/lib/commonjs/NativeLiveTracking.js +21 -0
  66. package/lib/commonjs/NativeLiveTracking.js.map +1 -0
  67. package/lib/commonjs/filters/distanceTimeFilter.js +63 -0
  68. package/lib/commonjs/filters/distanceTimeFilter.js.map +1 -0
  69. package/lib/commonjs/index.js +103 -0
  70. package/lib/commonjs/index.js.map +1 -0
  71. package/lib/commonjs/serialization/locationSerializer.js +51 -0
  72. package/lib/commonjs/serialization/locationSerializer.js.map +1 -0
  73. package/lib/commonjs/types.js +77 -0
  74. package/lib/commonjs/types.js.map +1 -0
  75. package/lib/commonjs/utils/distance.js +63 -0
  76. package/lib/commonjs/utils/distance.js.map +1 -0
  77. package/lib/commonjs/utils/retry.js +80 -0
  78. package/lib/commonjs/utils/retry.js.map +1 -0
  79. package/lib/commonjs/validation.js +463 -0
  80. package/lib/commonjs/validation.js.map +1 -0
  81. package/lib/module/EventEmitter.js +105 -0
  82. package/lib/module/EventEmitter.js.map +1 -0
  83. package/lib/module/LiveTracking.js +127 -0
  84. package/lib/module/LiveTracking.js.map +1 -0
  85. package/lib/module/NativeLiveTracking.js +16 -0
  86. package/lib/module/NativeLiveTracking.js.map +1 -0
  87. package/lib/module/filters/distanceTimeFilter.js +58 -0
  88. package/lib/module/filters/distanceTimeFilter.js.map +1 -0
  89. package/lib/module/index.js +32 -0
  90. package/lib/module/index.js.map +1 -0
  91. package/lib/module/serialization/locationSerializer.js +45 -0
  92. package/lib/module/serialization/locationSerializer.js.map +1 -0
  93. package/lib/module/types.js +94 -0
  94. package/lib/module/types.js.map +1 -0
  95. package/lib/module/utils/distance.js +56 -0
  96. package/lib/module/utils/distance.js.map +1 -0
  97. package/lib/module/utils/retry.js +72 -0
  98. package/lib/module/utils/retry.js.map +1 -0
  99. package/lib/module/validation.js +456 -0
  100. package/lib/module/validation.js.map +1 -0
  101. package/lib/typescript/EventEmitter.d.ts +65 -0
  102. package/lib/typescript/EventEmitter.d.ts.map +1 -0
  103. package/lib/typescript/LiveTracking.d.ts +23 -0
  104. package/lib/typescript/LiveTracking.d.ts.map +1 -0
  105. package/lib/typescript/NativeLiveTracking.d.ts +25 -0
  106. package/lib/typescript/NativeLiveTracking.d.ts.map +1 -0
  107. package/lib/typescript/filters/distanceTimeFilter.d.ts +44 -0
  108. package/lib/typescript/filters/distanceTimeFilter.d.ts.map +1 -0
  109. package/lib/typescript/index.d.ts +21 -0
  110. package/lib/typescript/index.d.ts.map +1 -0
  111. package/lib/typescript/serialization/locationSerializer.d.ts +39 -0
  112. package/lib/typescript/serialization/locationSerializer.d.ts.map +1 -0
  113. package/lib/typescript/types.d.ts +217 -0
  114. package/lib/typescript/types.d.ts.map +1 -0
  115. package/lib/typescript/utils/distance.d.ts +38 -0
  116. package/lib/typescript/utils/distance.d.ts.map +1 -0
  117. package/lib/typescript/utils/retry.d.ts +60 -0
  118. package/lib/typescript/utils/retry.d.ts.map +1 -0
  119. package/lib/typescript/validation.d.ts +26 -0
  120. package/lib/typescript/validation.d.ts.map +1 -0
  121. package/package.json +126 -0
  122. package/react-native-live-tracking.podspec +47 -0
  123. package/src/EventEmitter.ts +118 -0
  124. package/src/LiveTracking.ts +159 -0
  125. package/src/NativeLiveTracking.ts +29 -0
  126. package/src/filters/distanceTimeFilter.ts +75 -0
  127. package/src/index.ts +51 -0
  128. package/src/serialization/locationSerializer.ts +57 -0
  129. package/src/types.ts +252 -0
  130. package/src/utils/distance.ts +68 -0
  131. package/src/utils/retry.ts +75 -0
  132. package/src/validation.ts +552 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Event listener system for react-native-live-tracking.
3
+ *
4
+ * Wraps NativeEventEmitter to provide typed event subscriptions
5
+ * for location updates and tracking errors.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import { DeviceEventEmitter } from 'react-native';
11
+ import type { LocationData, TrackingError, Subscription } from './types';
12
+
13
+ /**
14
+ * Directly use the global device event emitter.
15
+ *
16
+ * NativeEventEmitter's constructor accesses Platform.OS, which requires the
17
+ * PlatformConstants native module. In a broken or partially-initialised binary
18
+ * that module can be missing and crash the app before we can even report that
19
+ * LiveTracking is unavailable. DeviceEventEmitter avoids that dependency
20
+ * entirely while still delivering native events emitted by the LiveTracking
21
+ * RCTEventEmitter.
22
+ */
23
+
24
+ /**
25
+ * Register a callback for location updates.
26
+ *
27
+ * The callback is invoked each time a valid location update is emitted
28
+ * from the native layer after passing the Distance/Time Matrix filter.
29
+ *
30
+ * @param callback - Function called with LocationData on each update
31
+ * @returns Subscription with `remove()` method to unsubscribe
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const subscription = onLocationUpdate((location) => {
36
+ * console.log(location.latitude, location.longitude);
37
+ * });
38
+ * // Later: unsubscribe
39
+ * subscription.remove();
40
+ * ```
41
+ */
42
+ export function onLocationUpdate(
43
+ callback: (location: LocationData) => void
44
+ ): Subscription {
45
+ const nativeSubscription = DeviceEventEmitter.addListener(
46
+ 'onLocationUpdate',
47
+ (event: Record<string, unknown>) => {
48
+ const location: LocationData = {
49
+ latitude: event.latitude as number,
50
+ longitude: event.longitude as number,
51
+ timestamp: event.timestamp as number,
52
+ accuracy: event.accuracy as number,
53
+ speed: (event.speed as number | null) ?? null,
54
+ altitude: (event.altitude as number | null) ?? null,
55
+ bearing: (event.bearing as number | null) ?? null,
56
+ };
57
+ callback(location);
58
+ }
59
+ );
60
+
61
+ return {
62
+ remove: () => {
63
+ nativeSubscription.remove();
64
+ },
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Register a callback for tracking errors.
70
+ *
71
+ * The callback is invoked when an error occurs during tracking
72
+ * (e.g., permission denied, GPS disabled, Firebase write failure).
73
+ *
74
+ * @param callback - Function called with TrackingError on each error event
75
+ * @returns Subscription with `remove()` method to unsubscribe
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const subscription = onError((error) => {
80
+ * console.error(`[${error.code}] ${error.message}`);
81
+ * });
82
+ * // Later: unsubscribe
83
+ * subscription.remove();
84
+ * ```
85
+ */
86
+ export function onError(
87
+ callback: (error: TrackingError) => void
88
+ ): Subscription {
89
+ const nativeSubscription = DeviceEventEmitter.addListener(
90
+ 'onTrackingError',
91
+ (event: Record<string, unknown>) => {
92
+ const error: TrackingError = {
93
+ code: event.code as string,
94
+ message: event.message as string,
95
+ recoverable: (event.recoverable as boolean) ?? true,
96
+ details: (event.details as Record<string, unknown>) ?? undefined,
97
+ };
98
+ callback(error);
99
+ }
100
+ );
101
+
102
+ return {
103
+ remove: () => {
104
+ nativeSubscription.remove();
105
+ },
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Remove all event listeners registered through this module.
111
+ *
112
+ * Useful for cleanup when the tracking module is being torn down
113
+ * or when all subscriptions need to be cleared at once.
114
+ */
115
+ export function removeAllListeners(): void {
116
+ DeviceEventEmitter.removeAllListeners('onLocationUpdate');
117
+ DeviceEventEmitter.removeAllListeners('onTrackingError');
118
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * JavaScript LiveTracking module that wraps NativeModule calls.
3
+ *
4
+ * Provides the public API for configuring, starting, and stopping
5
+ * location tracking, with validation and state management.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import NativeLiveTracking from './NativeLiveTracking';
11
+ import {
12
+ onLocationUpdate as emitterOnLocationUpdate,
13
+ onError as emitterOnError,
14
+ } from './EventEmitter';
15
+ import type {
16
+ TrackingConfig,
17
+ TrackingStatus,
18
+ LocationData,
19
+ TrackingError,
20
+ Subscription,
21
+ LiveTrackingModule,
22
+ } from './types';
23
+ import { validateConfig, applyDefaults } from './validation';
24
+
25
+ /**
26
+ * Returns the native module or throws a descriptive error if not linked.
27
+ * Using a function accessor (rather than assertNative) keeps TypeScript
28
+ * happy since it can't narrow an imported binding via assertion functions.
29
+ */
30
+ function native(): NonNullable<typeof NativeLiveTracking> {
31
+ if (!NativeLiveTracking) {
32
+ throw new Error(
33
+ "[@kafitra/react-native-live-tracking] Native module 'LiveTracking' is not registered. " +
34
+ 'Make sure you ran `pod install` and rebuilt the app from Xcode.'
35
+ );
36
+ }
37
+ return NativeLiveTracking;
38
+ }
39
+
40
+ /**
41
+ * Tracks whether configure() has been successfully called.
42
+ * Used to guard queue methods that require configuration.
43
+ */
44
+ let isConfigured = false;
45
+
46
+ /**
47
+ * Resets the configured state. Only for use in tests.
48
+ * @internal
49
+ */
50
+ export function _resetConfiguredStateForTesting(): void {
51
+ isConfigured = false;
52
+ }
53
+
54
+ /**
55
+ * LiveTracking module implementation.
56
+ *
57
+ * Wraps native module calls with validation, serialization,
58
+ * and state management on the JavaScript side.
59
+ */
60
+ const LiveTracking: LiveTrackingModule = {
61
+ /**
62
+ * Configure the tracking library with the given parameters.
63
+ * Validates the config, applies defaults, and passes to native layer.
64
+ */
65
+ async configure(config: TrackingConfig): Promise<void> {
66
+ const validationResult = validateConfig(config);
67
+ if (!validationResult.valid) {
68
+ const errorMessages = validationResult.errors
69
+ .map((e) => `[${e.field}]: ${e.message}`)
70
+ .join('; ');
71
+ throw new Error(`Invalid configuration: ${errorMessages}`);
72
+ }
73
+
74
+ const finalConfig = applyDefaults(config);
75
+ const jsonString = JSON.stringify(finalConfig);
76
+
77
+ const MAX_PAYLOAD_BYTES = 1024 * 1024; // 1 MB
78
+ if (jsonString.length > MAX_PAYLOAD_BYTES) {
79
+ throw new Error(
80
+ 'Configuration payload is too large: serialized JSON exceeds 1 MB'
81
+ );
82
+ }
83
+
84
+ await native().configure(jsonString);
85
+ isConfigured = true;
86
+ },
87
+
88
+ /**
89
+ * Start location tracking.
90
+ * Requires `configure()` to have been called first.
91
+ */
92
+ async start(): Promise<void> {
93
+ await native().start();
94
+ },
95
+
96
+ /**
97
+ * Stop location tracking and clean up resources.
98
+ */
99
+ async stop(): Promise<void> {
100
+ await native().stop();
101
+ },
102
+
103
+ /**
104
+ * Register a callback for location updates.
105
+ */
106
+ onLocationUpdate(callback: (location: LocationData) => void): Subscription {
107
+ return emitterOnLocationUpdate(callback);
108
+ },
109
+
110
+ /**
111
+ * Register a callback for tracking errors.
112
+ */
113
+ onError(callback: (error: TrackingError) => void): Subscription {
114
+ return emitterOnError(callback);
115
+ },
116
+
117
+ /**
118
+ * Get the current tracking status.
119
+ */
120
+ async getStatus(): Promise<TrackingStatus> {
121
+ const jsonString = await native().getStatus();
122
+ return JSON.parse(jsonString) as TrackingStatus;
123
+ },
124
+
125
+ /**
126
+ * Get the total number of locations currently queued for sync.
127
+ *
128
+ * @throws Error with code NOT_CONFIGURED if called before configure()
129
+ */
130
+ async getQueuedLocations(): Promise<number> {
131
+ if (!isConfigured) {
132
+ const error = new Error(
133
+ 'LiveTracking is not configured. Call configure() before getQueuedLocations().'
134
+ );
135
+ (error as any).code = 'NOT_CONFIGURED';
136
+ throw error;
137
+ }
138
+ return native().getQueuedLocations();
139
+ },
140
+
141
+ /**
142
+ * Get the number of queued locations per target path.
143
+ *
144
+ * @throws Error with code NOT_CONFIGURED if called before configure()
145
+ */
146
+ async getQueuedLocationsByTarget(): Promise<Record<string, number>> {
147
+ if (!isConfigured) {
148
+ const error = new Error(
149
+ 'LiveTracking is not configured. Call configure() before getQueuedLocationsByTarget().'
150
+ );
151
+ (error as any).code = 'NOT_CONFIGURED';
152
+ throw error;
153
+ }
154
+ const jsonString = await native().getQueuedLocationsByTarget();
155
+ return JSON.parse(jsonString);
156
+ },
157
+ };
158
+
159
+ export default LiveTracking;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Codegen spec for react-native-live-tracking.
3
+ *
4
+ * Uses TurboModuleRegistry.get (not getEnforcing) so that a missing or
5
+ * not-yet-registered module returns null instead of throwing synchronously.
6
+ * A top-level throw would corrupt the module graph and leave upstream
7
+ * importers (TrackingProvider, AppNavigator) with undefined module records.
8
+ *
9
+ * File must be named Native*.ts and live in the codegenConfig.jsSrcsDir
10
+ * for RN's codegen to pick it up.
11
+ */
12
+
13
+ import type { TurboModule } from 'react-native';
14
+ import { TurboModuleRegistry, NativeModules } from 'react-native';
15
+
16
+ export interface Spec extends TurboModule {
17
+ configure(config: string): Promise<void>;
18
+ start(): Promise<void>;
19
+ stop(): Promise<void>;
20
+ getStatus(): Promise<string>;
21
+ getQueuedLocations(): Promise<number>;
22
+ getQueuedLocationsByTarget(): Promise<string>;
23
+ addListener(eventName: string): void;
24
+ removeListeners(count: number): void;
25
+ }
26
+
27
+ // Try TurboModule first (new arch with codegen), fall back to Bridge (old arch / interop)
28
+ export default (TurboModuleRegistry.get<Spec>('LiveTracking') ??
29
+ NativeModules.LiveTracking) as Spec | null;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Distance/Time Matrix filter for location updates.
3
+ *
4
+ * This filter implements the battery optimization strategy that accepts a new
5
+ * location update based on the selected mode:
6
+ * - 'both' (default): BOTH time and distance conditions must be met
7
+ * - 'interval': only the time condition must be met
8
+ * - 'distance': only the distance condition must be met
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+
13
+ import { calculateDistance } from '../utils/distance';
14
+ import type { LocationData, OptimizationConfig } from '../types';
15
+
16
+ /**
17
+ * Configuration subset required by the distance/time filter.
18
+ */
19
+ export type DistanceTimeFilterConfig = Required<
20
+ Pick<OptimizationConfig, 'intervalMs' | 'distanceFilterMeters'>
21
+ > & {
22
+ /** Filter strategy. Defaults to 'both' if not provided. */
23
+ mode?: 'interval' | 'distance' | 'both';
24
+ };
25
+
26
+ /**
27
+ * Determines whether a new location should be accepted based on the
28
+ * Distance/Time Matrix filter criteria.
29
+ *
30
+ * The filtering strategy depends on `config.mode`:
31
+ * - 'both': accepted only if both time and distance conditions are met
32
+ * - 'interval': accepted only if enough time has elapsed
33
+ * - 'distance': accepted only if enough distance has been covered
34
+ *
35
+ * @param lastLocation - The most recently accepted location
36
+ * @param newLocation - The candidate location to evaluate
37
+ * @param config - Filter configuration with intervalMs, distanceFilterMeters, and mode
38
+ * @returns `true` if the new location should be accepted, `false` otherwise
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const accepted = shouldAcceptLocation(
43
+ * { latitude: -6.2088, longitude: 106.8456, timestamp: 1700000000000, accuracy: 5, speed: null, altitude: null, bearing: null },
44
+ * { latitude: -6.2090, longitude: 106.8460, timestamp: 1700000015000, accuracy: 5, speed: 1.2, altitude: null, bearing: null },
45
+ * { intervalMs: 10000, distanceFilterMeters: 10, mode: 'both' }
46
+ * );
47
+ * ```
48
+ */
49
+ export function shouldAcceptLocation(
50
+ lastLocation: LocationData,
51
+ newLocation: LocationData,
52
+ config: DistanceTimeFilterConfig
53
+ ): boolean {
54
+ const mode = config.mode ?? 'both';
55
+ const timeDiff = newLocation.timestamp - lastLocation.timestamp;
56
+ const distance = calculateDistance(
57
+ lastLocation.latitude,
58
+ lastLocation.longitude,
59
+ newLocation.latitude,
60
+ newLocation.longitude
61
+ );
62
+
63
+ const timeMet = timeDiff >= config.intervalMs;
64
+ const distanceMet = distance >= config.distanceFilterMeters;
65
+
66
+ switch (mode) {
67
+ case 'interval':
68
+ return timeMet;
69
+ case 'distance':
70
+ return distanceMet;
71
+ case 'both':
72
+ default:
73
+ return timeMet && distanceMet;
74
+ }
75
+ }
package/src/index.ts ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * react-native-live-tracking
3
+ *
4
+ * Real-time location tracking library for React Native with Firebase synchronization.
5
+ * Supports both TurboModules (new architecture) and Bridge (old architecture).
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import LiveTracking from './LiveTracking';
11
+
12
+ export default LiveTracking;
13
+ export { default as LiveTracking } from './LiveTracking';
14
+
15
+ // Re-export all types
16
+ export { TrackingState } from './types';
17
+ export type {
18
+ SyncTarget,
19
+ OptimizationConfig,
20
+ OptimizationMode,
21
+ AndroidNotificationConfig,
22
+ IOSNotificationConfig,
23
+ FirebaseConfig,
24
+ TrackingConfig,
25
+ LocationData,
26
+ TrackingError,
27
+ TrackingStatus,
28
+ ConfigError,
29
+ ConfigValidationResult,
30
+ Subscription,
31
+ LiveTrackingModule,
32
+ } from './types';
33
+
34
+ // Event listener functions
35
+ export { onLocationUpdate, onError, removeAllListeners } from './EventEmitter';
36
+
37
+ // Configuration validation utilities
38
+ export { validateConfig, applyDefaults } from './validation';
39
+
40
+ // Distance calculation utility
41
+ export { calculateDistance } from './utils/distance';
42
+
43
+ // Distance/Time Matrix filter
44
+ export { shouldAcceptLocation } from './filters/distanceTimeFilter';
45
+
46
+ // Location serialization for Firebase
47
+ export { serializeLocationForTarget } from './serialization/locationSerializer';
48
+ export type { TargetLocationPayload } from './serialization/locationSerializer';
49
+
50
+ // Retry/backoff utilities
51
+ export { calculateBackoffDelay, shouldRetry } from './utils/retry';
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Location serialization for Firebase sync targets.
3
+ *
4
+ * Transforms LocationData into a generic payload suitable for any
5
+ * sync target, regardless of write method (set, push, update).
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import type { LocationData } from '../types';
11
+
12
+ // ─── Payload Interface ───────────────────────────────────────────────────────
13
+
14
+ /**
15
+ * Serialized location payload for any sync target.
16
+ *
17
+ * Contains required fields (latitude, longitude, timestamp, accuracy)
18
+ * and optional sensor fields (speed, heading, altitude) which are
19
+ * included as `null` when unavailable from the device sensor.
20
+ * Placeholder values (0, -1) are never used for unavailable fields.
21
+ */
22
+ export interface TargetLocationPayload {
23
+ latitude: number;
24
+ longitude: number;
25
+ timestamp: number;
26
+ accuracy: number;
27
+ speed: number | null;
28
+ heading: number | null;
29
+ altitude: number | null;
30
+ }
31
+
32
+ // ─── Serialization Function ──────────────────────────────────────────────────
33
+
34
+ /**
35
+ * Serialize a LocationData object for a generic Firebase sync target.
36
+ *
37
+ * Produces a payload containing all required location fields and optional
38
+ * sensor fields. When optional fields (speed, heading/bearing, altitude)
39
+ * are unavailable from the device sensor, they are included as `null`.
40
+ * Default placeholder values (0, -1) are never written for unavailable fields.
41
+ *
42
+ * @param location - The location data to serialize
43
+ * @returns Serialized payload for the sync target
44
+ */
45
+ export function serializeLocationForTarget(
46
+ location: LocationData
47
+ ): TargetLocationPayload {
48
+ return {
49
+ latitude: location.latitude,
50
+ longitude: location.longitude,
51
+ timestamp: location.timestamp,
52
+ accuracy: location.accuracy,
53
+ speed: location.speed,
54
+ heading: location.bearing,
55
+ altitude: location.altitude,
56
+ };
57
+ }