gps-plus-slam-app-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +222 -0
  3. package/dist/alignment-lerper-D9BeDwZX.d.ts +29 -0
  4. package/dist/app-selectors-DyMzxCEC.d.ts +140 -0
  5. package/dist/ar/camera-blit-capture.d.ts +2 -0
  6. package/dist/ar/camera-blit-capture.js +253 -0
  7. package/dist/ar/capture-failure-tracker.d.ts +2 -0
  8. package/dist/ar/capture-failure-tracker.js +45 -0
  9. package/dist/ar/chromium-camera-access-workaround.d.ts +2 -0
  10. package/dist/ar/chromium-camera-access-workaround.js +32 -0
  11. package/dist/ar/depth-sampler.d.ts +3 -0
  12. package/dist/ar/depth-sampler.js +147 -0
  13. package/dist/ar/image-capture.d.ts +2 -0
  14. package/dist/ar/image-capture.js +152 -0
  15. package/dist/ar/index.d.ts +12 -0
  16. package/dist/ar/index.js +12 -0
  17. package/dist/ar/replay-scene.d.ts +2 -0
  18. package/dist/ar/replay-scene.js +325 -0
  19. package/dist/ar/scene-node-names.d.ts +2 -0
  20. package/dist/ar/scene-node-names.js +16 -0
  21. package/dist/ar/tracking-state.d.ts +2 -0
  22. package/dist/ar/tracking-state.js +164 -0
  23. package/dist/ar/webxr-session.d.ts +4 -0
  24. package/dist/ar/webxr-session.js +890 -0
  25. package/dist/ar/xr-camera-texture.d.ts +2 -0
  26. package/dist/ar/xr-camera-texture.js +39 -0
  27. package/dist/ar/xr-error-handler.d.ts +2 -0
  28. package/dist/ar/xr-error-handler.js +37 -0
  29. package/dist/ar-types-B-ORgk6Z.d.ts +82 -0
  30. package/dist/camera-blit-capture-DHuyrrxr.d.ts +108 -0
  31. package/dist/camera-follower-BMqPddxx.d.ts +29 -0
  32. package/dist/capture-failure-tracker-2Q6oI2uQ.d.ts +52 -0
  33. package/dist/chromium-camera-access-workaround-CU5zSKNr.d.ts +61 -0
  34. package/dist/community-license-key-ChZLCJyO.d.ts +21 -0
  35. package/dist/concurrency-p6gB30fT.d.ts +24 -0
  36. package/dist/css3d-renderer-manager-rgLMVAvd.d.ts +23 -0
  37. package/dist/depth-sampler-BNCtnEoP.d.ts +108 -0
  38. package/dist/failure-tracker-1sQ_Cgyw.d.ts +49 -0
  39. package/dist/file-system-BsQgYCU3.d.ts +128 -0
  40. package/dist/file-system-utils-5cWO8Xyv.d.ts +24 -0
  41. package/dist/format-file-size-onGq0k1Q.d.ts +24 -0
  42. package/dist/fused-path-DtqCSqJh.d.ts +25 -0
  43. package/dist/geo-types-Xx-LVcnw.d.ts +29 -0
  44. package/dist/gps-Ddzr2KlX.d.ts +54 -0
  45. package/dist/gps-compass-cubes-C-6JEnpR.d.ts +22 -0
  46. package/dist/gps-error-handler-VPuNOuf8.d.ts +40 -0
  47. package/dist/gps-event-markers-DpEy2qRd.d.ts +67 -0
  48. package/dist/h3-ref-point-CFhayNSZ.d.ts +59 -0
  49. package/dist/image-capture-2pKjYUS6.d.ts +130 -0
  50. package/dist/index.d.ts +56 -0
  51. package/dist/index.js +61 -0
  52. package/dist/leaflet-map-overlay-CvfQXTye.d.ts +114 -0
  53. package/dist/lerp-utils-87NWjUOD.d.ts +20 -0
  54. package/dist/licensing/community-license-key.d.ts +2 -0
  55. package/dist/licensing/community-license-key.js +21 -0
  56. package/dist/licensing/index.d.ts +2 -0
  57. package/dist/licensing/index.js +2 -0
  58. package/dist/list-formatter-DKQQVLcl.d.ts +14 -0
  59. package/dist/logger-B81iwxx0.d.ts +74 -0
  60. package/dist/map-overlay-DuiZuCTx.d.ts +153 -0
  61. package/dist/null-storage-backend-CsWvQwN8.d.ts +11 -0
  62. package/dist/opfs-storage-ChOzF_Wp.d.ts +181 -0
  63. package/dist/opfs-storage-backend-CVcmfJHN.d.ts +11 -0
  64. package/dist/permission-checker-jO2ziCWC.d.ts +146 -0
  65. package/dist/persistence-middleware-DrptY02i.d.ts +31 -0
  66. package/dist/recorder-slice-fcwAXrsh.d.ts +35 -0
  67. package/dist/recording-coordinator-DzflP07_.d.ts +81 -0
  68. package/dist/recording-options-BYUFX8iU.d.ts +166 -0
  69. package/dist/recording-replayer-oOzPITrd.d.ts +32 -0
  70. package/dist/ref-point-importer-B_wn5ABk.d.ts +62 -0
  71. package/dist/ref-point-loader-Pt1smgHS.d.ts +117 -0
  72. package/dist/ref-points/h3-ref-point.d.ts +2 -0
  73. package/dist/ref-points/h3-ref-point.js +74 -0
  74. package/dist/ref-points/index.d.ts +2 -0
  75. package/dist/ref-points/index.js +2 -0
  76. package/dist/ref-points-slice-tC-Wd6XJ.d.ts +48 -0
  77. package/dist/reference-points-CFWy8_wj.d.ts +68 -0
  78. package/dist/replay-engine-DuX316ae.d.ts +103 -0
  79. package/dist/replay-scene-DtUm6bsr.d.ts +109 -0
  80. package/dist/routing-slice-BlHTM8eh.d.ts +23 -0
  81. package/dist/scene-node-names-DPsaE2z3.d.ts +14 -0
  82. package/dist/sensors/gps-error-handler.d.ts +2 -0
  83. package/dist/sensors/gps-error-handler.js +53 -0
  84. package/dist/sensors/gps.d.ts +2 -0
  85. package/dist/sensors/gps.js +93 -0
  86. package/dist/sensors/index.d.ts +4 -0
  87. package/dist/sensors/index.js +4 -0
  88. package/dist/sensors/permission-checker.d.ts +2 -0
  89. package/dist/sensors/permission-checker.js +397 -0
  90. package/dist/state/app-selectors.d.ts +2 -0
  91. package/dist/state/app-selectors.js +35 -0
  92. package/dist/state/index.d.ts +17 -0
  93. package/dist/state/index.js +13 -0
  94. package/dist/state/persistence-middleware.d.ts +2 -0
  95. package/dist/state/persistence-middleware.js +71 -0
  96. package/dist/state/recorder-slice.d.ts +2 -0
  97. package/dist/state/recorder-slice.js +47 -0
  98. package/dist/state/recording-coordinator.d.ts +2 -0
  99. package/dist/state/recording-coordinator.js +150 -0
  100. package/dist/state/recording-options.d.ts +2 -0
  101. package/dist/state/recording-options.js +192 -0
  102. package/dist/state/recording-replayer.d.ts +2 -0
  103. package/dist/state/recording-replayer.js +39 -0
  104. package/dist/state/ref-points-slice.d.ts +2 -0
  105. package/dist/state/ref-points-slice.js +44 -0
  106. package/dist/state/replay-engine.d.ts +2 -0
  107. package/dist/state/replay-engine.js +230 -0
  108. package/dist/state/routing-slice.d.ts +2 -0
  109. package/dist/state/routing-slice.js +13 -0
  110. package/dist/state/store-subscribers.d.ts +3 -0
  111. package/dist/state/store-subscribers.js +97 -0
  112. package/dist/state/store.d.ts +9 -0
  113. package/dist/state/store.js +71 -0
  114. package/dist/state/subscribe-to-selector.d.ts +2 -0
  115. package/dist/state/subscribe-to-selector.js +26 -0
  116. package/dist/storage/file-system-utils.d.ts +2 -0
  117. package/dist/storage/file-system-utils.js +31 -0
  118. package/dist/storage/file-system.d.ts +2 -0
  119. package/dist/storage/file-system.js +229 -0
  120. package/dist/storage/index.d.ts +11 -0
  121. package/dist/storage/index.js +10 -0
  122. package/dist/storage/null-storage-backend.d.ts +2 -0
  123. package/dist/storage/null-storage-backend.js +8 -0
  124. package/dist/storage/opfs-storage-backend.d.ts +2 -0
  125. package/dist/storage/opfs-storage-backend.js +15 -0
  126. package/dist/storage/opfs-storage.d.ts +2 -0
  127. package/dist/storage/opfs-storage.js +242 -0
  128. package/dist/storage/ref-point-importer.d.ts +2 -0
  129. package/dist/storage/ref-point-importer.js +163 -0
  130. package/dist/storage/ref-point-loader.d.ts +2 -0
  131. package/dist/storage/ref-point-loader.js +248 -0
  132. package/dist/storage/ref-point-recovery.d.ts +28 -0
  133. package/dist/storage/ref-point-recovery.js +159 -0
  134. package/dist/storage/storage-backend.d.ts +2 -0
  135. package/dist/storage/storage-backend.js +1 -0
  136. package/dist/storage/zip-export.d.ts +2 -0
  137. package/dist/storage/zip-export.js +214 -0
  138. package/dist/storage/zip-reader.d.ts +2 -0
  139. package/dist/storage/zip-reader.js +190 -0
  140. package/dist/storage-backend-BcEItnFr.d.ts +20 -0
  141. package/dist/store-C7PKOj8S.d.ts +96 -0
  142. package/dist/store-subscribers-CuoIzB5T.d.ts +77 -0
  143. package/dist/subscribe-to-selector-UTZ0KkAc.d.ts +26 -0
  144. package/dist/test-utils/browser-mocks.d.ts +294 -0
  145. package/dist/test-utils/browser-mocks.js +568 -0
  146. package/dist/test-utils/zip-round-trip-helpers.d.ts +60 -0
  147. package/dist/test-utils/zip-round-trip-helpers.js +207 -0
  148. package/dist/three-dispose-CCBHjE_a.d.ts +30 -0
  149. package/dist/tracking-state-B9QfuZCg.d.ts +101 -0
  150. package/dist/types/ar-types.d.ts +2 -0
  151. package/dist/types/ar-types.js +1 -0
  152. package/dist/types/geo-types.d.ts +2 -0
  153. package/dist/types/geo-types.js +1 -0
  154. package/dist/types/index.d.ts +3 -0
  155. package/dist/types/index.js +1 -0
  156. package/dist/utils/concurrency.d.ts +2 -0
  157. package/dist/utils/concurrency.js +40 -0
  158. package/dist/utils/failure-tracker.d.ts +2 -0
  159. package/dist/utils/failure-tracker.js +48 -0
  160. package/dist/utils/format-file-size.d.ts +2 -0
  161. package/dist/utils/format-file-size.js +41 -0
  162. package/dist/utils/fused-path.d.ts +2 -0
  163. package/dist/utils/fused-path.js +44 -0
  164. package/dist/utils/index.d.ts +7 -0
  165. package/dist/utils/index.js +7 -0
  166. package/dist/utils/list-formatter.d.ts +2 -0
  167. package/dist/utils/list-formatter.js +17 -0
  168. package/dist/utils/logger.d.ts +2 -0
  169. package/dist/utils/logger.js +226 -0
  170. package/dist/vis-colors-Qi2vlRGt.d.ts +63 -0
  171. package/dist/visualization/alignment-lerper.d.ts +2 -0
  172. package/dist/visualization/alignment-lerper.js +68 -0
  173. package/dist/visualization/camera-follower.d.ts +2 -0
  174. package/dist/visualization/camera-follower.js +51 -0
  175. package/dist/visualization/css3d-renderer-manager.d.ts +2 -0
  176. package/dist/visualization/css3d-renderer-manager.js +41 -0
  177. package/dist/visualization/gps-compass-cubes.d.ts +2 -0
  178. package/dist/visualization/gps-compass-cubes.js +129 -0
  179. package/dist/visualization/gps-event-markers.d.ts +2 -0
  180. package/dist/visualization/gps-event-markers.js +159 -0
  181. package/dist/visualization/index.d.ts +12 -0
  182. package/dist/visualization/index.js +12 -0
  183. package/dist/visualization/leaflet-map-overlay.d.ts +2 -0
  184. package/dist/visualization/leaflet-map-overlay.js +313 -0
  185. package/dist/visualization/lerp-utils.d.ts +2 -0
  186. package/dist/visualization/lerp-utils.js +22 -0
  187. package/dist/visualization/map-overlay.d.ts +2 -0
  188. package/dist/visualization/map-overlay.js +241 -0
  189. package/dist/visualization/reference-points.d.ts +2 -0
  190. package/dist/visualization/reference-points.js +160 -0
  191. package/dist/visualization/three-dispose.d.ts +2 -0
  192. package/dist/visualization/three-dispose.js +55 -0
  193. package/dist/visualization/vis-colors.d.ts +2 -0
  194. package/dist/visualization/vis-colors.js +74 -0
  195. package/dist/webxr-session-COlSrXdL.d.ts +316 -0
  196. package/dist/xr-camera-texture-BbukNE79.d.ts +53 -0
  197. package/dist/xr-error-handler-DTHfmvrl.d.ts +24 -0
  198. package/dist/zip-export-Rtpi75JJ.d.ts +87 -0
  199. package/dist/zip-reader-D7pkpX8K.d.ts +97 -0
  200. package/package.json +104 -0
@@ -0,0 +1,207 @@
1
+ import { createSession, initOpfsStorage, resetOpfsStorage, writeAction, writeFrame, writeSessionMetadata } from "../storage/opfs-storage.js";
2
+ import { exportSessionAsZip } from "../storage/zip-export.js";
3
+ import { installOPFSMocks } from "./browser-mocks.js";
4
+ //#region ../src/test-utils/zip-round-trip-helpers.ts
5
+ /**
6
+ * Round-Trip Test Helpers — Produce realistic recording zips programmatically.
7
+ *
8
+ * Why this exists: Tests need recording zip files for the replay pipeline,
9
+ * zip-reader, and state verification. Rather than depending on static
10
+ * pre-recorded zips (which go stale as the app evolves), this helper
11
+ * produces zips using the real OPFS mock + export pipeline, ensuring the
12
+ * test data always matches the current production format.
13
+ *
14
+ * The helper writes actions via writeAction(), writes frames via writeFrame(),
15
+ * writes session metadata via writeSessionMetadata(), and exports via
16
+ * exportSessionAsZip(). OPFS mocks are installed and cleaned up internally
17
+ * — no side effects leak to the calling test.
18
+ *
19
+ * DESIGN BOUNDARY — VALID ZIPS ONLY
20
+ *
21
+ * This helper intentionally produces only valid, realistic zips. Do NOT add
22
+ * options to generate broken/incomplete zips (missing session.json, malformed
23
+ * JSON, missing actions, etc.). Its value comes from exercising the real
24
+ * production pipeline end-to-end — mixing in "intentionally broken" modes
25
+ * would undermine that contract.
26
+ *
27
+ * For error-handling / robustness tests, use hand-crafted zips via ZipWriter
28
+ * directly — see createZipWithActions() in zip-reader.test.ts for the
29
+ * established pattern. That approach gives precise byte-level control over
30
+ * exactly what's broken, which is strictly better for negative test cases.
31
+ */
32
+ const DEFAULTS = {
33
+ scenarioName: "TestScenario",
34
+ sessionTimestamp: /* @__PURE__ */ new Date("2026-03-01T09:00:00Z"),
35
+ gpsEventCount: 10,
36
+ imagesBeforeSetZero: 2,
37
+ imagesAfterSetZero: 5,
38
+ frameCount: 2,
39
+ deviceInfo: "TestDevice Android 14",
40
+ zeroPos: {
41
+ lat: 50,
42
+ lon: 8
43
+ }
44
+ };
45
+ /**
46
+ * Produce a realistic recording zip using the real OPFS mock + export pipeline.
47
+ *
48
+ * Action sequence produced (with defaults):
49
+ * 1. recorder/startSession (index 1)
50
+ * 2. gpsData/add2dImage × 2 (indices 2-3, before setZeroPos — dropped on replay)
51
+ * 3. gpsData/setZeroPos (index 4)
52
+ * 4. gpsData/recordGpsEvent × 10 (indices 5-14)
53
+ * 5. gpsData/add2dImage × 5 (indices 15-19, after setZeroPos — kept on replay)
54
+ *
55
+ * GPS events use varied odom/GPS positions that produce a non-identity
56
+ * alignment matrix when replayed through the library store.
57
+ *
58
+ * OPFS mocks are installed and cleaned up within this function.
59
+ */
60
+ async function produceTestZip(opts) {
61
+ const options = {
62
+ ...DEFAULTS,
63
+ ...opts
64
+ };
65
+ const { cleanup } = installOPFSMocks();
66
+ try {
67
+ await initOpfsStorage();
68
+ const { scenarioName, sessionName } = await createSession(options.scenarioName, options.sessionTimestamp);
69
+ const allActions = [];
70
+ let actionIndex = 0;
71
+ /** Increment index, persist to OPFS, and record for later assertions. */
72
+ const writeAndRecordAction = async (action) => {
73
+ actionIndex++;
74
+ await writeAction(action, actionIndex);
75
+ allActions.push(action);
76
+ };
77
+ await writeAndRecordAction({
78
+ type: "recorder/startSession",
79
+ payload: {
80
+ scenarioName: options.scenarioName,
81
+ sessionName,
82
+ startTime: options.sessionTimestamp.getTime(),
83
+ deviceInfo: options.deviceInfo
84
+ }
85
+ });
86
+ for (let i = 0; i < options.imagesBeforeSetZero; i++) await writeAndRecordAction({
87
+ type: "gpsData/add2dImage",
88
+ payload: {
89
+ imageFile: `frames/frame-${String(actionIndex + 1).padStart(6, "0")}.jpg`,
90
+ position: [
91
+ i * .5,
92
+ 0,
93
+ 0
94
+ ],
95
+ rotation: [
96
+ 0,
97
+ 0,
98
+ 0,
99
+ 1
100
+ ],
101
+ screenRotation: 0,
102
+ capturedAt: options.sessionTimestamp.getTime() + actionIndex * 1e3
103
+ }
104
+ });
105
+ await writeAndRecordAction({
106
+ type: "gpsData/setZeroPos",
107
+ payload: {
108
+ lat: options.zeroPos.lat,
109
+ lon: options.zeroPos.lon
110
+ }
111
+ });
112
+ for (let i = 0; i < options.gpsEventCount; i++) {
113
+ const t = options.sessionTimestamp.getTime() + (actionIndex + 1) * 1e3;
114
+ const angle = i / Math.max(options.gpsEventCount - 1, 1) * Math.PI * .5;
115
+ const radius = (i + 1) * 2;
116
+ await writeAndRecordAction({
117
+ type: "gpsData/recordGpsEvent",
118
+ payload: {
119
+ odomPosition: [
120
+ radius * Math.cos(angle),
121
+ 0,
122
+ radius * Math.sin(angle)
123
+ ],
124
+ odomRotation: [
125
+ 0,
126
+ 0,
127
+ 0,
128
+ 1
129
+ ],
130
+ rawGpsPoint: {
131
+ id: `gps-event-${i + 1}`,
132
+ latitude: options.zeroPos.lat + (i + 1) * 1e-4,
133
+ longitude: options.zeroPos.lon + (i + 1) * 1e-4,
134
+ timestamp: t
135
+ }
136
+ }
137
+ });
138
+ }
139
+ const postZeroImageOffset = options.imagesBeforeSetZero;
140
+ for (let i = 0; i < options.imagesAfterSetZero; i++) {
141
+ const imgIdx = postZeroImageOffset + i + 1;
142
+ await writeAndRecordAction({
143
+ type: "gpsData/add2dImage",
144
+ payload: {
145
+ imageFile: `frames/frame-${String(imgIdx).padStart(6, "0")}.jpg`,
146
+ position: [
147
+ (i + 1) * 1,
148
+ (i + 1) * .5,
149
+ 0
150
+ ],
151
+ rotation: [
152
+ 0,
153
+ 0,
154
+ 0,
155
+ 1
156
+ ],
157
+ screenRotation: i % 4,
158
+ capturedAt: options.sessionTimestamp.getTime() + actionIndex * 1e3
159
+ }
160
+ });
161
+ }
162
+ for (let i = 0; i < options.frameCount; i++) {
163
+ const jpegHeader = [
164
+ 255,
165
+ 216,
166
+ 255,
167
+ 224
168
+ ];
169
+ const body = Array.from({ length: 10 }, (_, j) => i * 10 + j & 255);
170
+ const frameBytes = new Uint8Array([...jpegHeader, ...body]);
171
+ await writeFrame(new Blob([frameBytes], { type: "image/jpeg" }), i + 1);
172
+ }
173
+ await writeSessionMetadata({
174
+ version: 1,
175
+ startedAt: options.sessionTimestamp.toISOString(),
176
+ endedAt: new Date(options.sessionTimestamp.getTime() + actionIndex * 1e3 + 5e3).toISOString(),
177
+ scenarioName: options.scenarioName,
178
+ actionCount: actionIndex,
179
+ frameCount: options.frameCount,
180
+ userAgent: options.deviceInfo
181
+ });
182
+ const { blob } = await exportSessionAsZip(scenarioName, sessionName);
183
+ return {
184
+ zipData: new Uint8Array(await blob.arrayBuffer()),
185
+ totalActionCount: actionIndex,
186
+ gpsEventCount: options.gpsEventCount,
187
+ imageActions: {
188
+ totalCount: options.imagesBeforeSetZero + options.imagesAfterSetZero,
189
+ beforeSetZero: options.imagesBeforeSetZero,
190
+ afterSetZero: options.imagesAfterSetZero
191
+ },
192
+ scenarioName: options.scenarioName,
193
+ sessionName,
194
+ startTime: options.sessionTimestamp.getTime(),
195
+ deviceInfo: options.deviceInfo,
196
+ hasSessionJson: true,
197
+ zeroPos: options.zeroPos,
198
+ actions: allActions,
199
+ frameCount: options.frameCount
200
+ };
201
+ } finally {
202
+ resetOpfsStorage();
203
+ cleanup();
204
+ }
205
+ }
206
+ //#endregion
207
+ export { produceTestZip };
@@ -0,0 +1,30 @@
1
+ import * as THREE from "three";
2
+
3
+ //#region ../src/visualization/three-dispose.d.ts
4
+ interface DisposeOptions {
5
+ /** When true, skip geometry.dispose() — useful for shared geometries. */
6
+ readonly skipGeometry?: boolean;
7
+ /** When true, skip material.dispose() (and its map texture) — useful for shared materials. */
8
+ readonly skipMaterial?: boolean;
9
+ }
10
+ /**
11
+ * Traverse an Object3D and all its descendants, disposing GPU resources:
12
+ * geometries (Mesh), materials (Mesh + Sprite), and material textures.
13
+ *
14
+ * Does NOT remove root from its parent — callers handle that.
15
+ *
16
+ * @param root The Object3D tree root to clean up.
17
+ * @param opts Optional flags (e.g. skipGeometry for shared geometries).
18
+ */
19
+ declare function disposeObject3D(root: THREE.Object3D, opts?: DisposeOptions): void;
20
+ /**
21
+ * Remove meshes from a parent, dispose their GPU resources, and clear
22
+ * the array in-place.
23
+ *
24
+ * @param meshes Array of meshes (mutated — emptied after disposal).
25
+ * @param parent The Object3D to remove meshes from. Pass null/undefined to skip removal.
26
+ * @param opts Optional flags (e.g. skipGeometry for shared geometries).
27
+ */
28
+ declare function disposeMeshArray(meshes: THREE.Mesh[], parent?: THREE.Object3D | null, opts?: DisposeOptions): void;
29
+ //#endregion
30
+ export { disposeMeshArray as n, disposeObject3D as r, DisposeOptions as t };
@@ -0,0 +1,101 @@
1
+ import { t as ARPose } from "./ar-types-B-ORgk6Z.js";
2
+ import { OdometryTrackingRestartedPayload, Quaternion, Vector3 } from "gps-plus-slam-js";
3
+
4
+ //#region ../src/ar/tracking-state.d.ts
5
+ /**
6
+ * Possible tracking states
7
+ */
8
+ declare enum TrackingState {
9
+ /** AR session started but no pose received yet */
10
+ INITIALIZING = "initializing",
11
+ /** Actively receiving valid poses */
12
+ TRACKING = "tracking",
13
+ /** Pose became unavailable (tracking lost) */
14
+ LOST = "lost"
15
+ }
16
+ /**
17
+ * Device orientation from sensors (for sensor rotation in restart payload)
18
+ */
19
+ interface DeviceOrientation {
20
+ alpha: number;
21
+ beta: number;
22
+ gamma: number;
23
+ absolute: boolean;
24
+ }
25
+ /**
26
+ * Serialized representation of the XRReferenceSpaceEvent.transform.
27
+ * The position and orientation of the new native origin expressed in
28
+ * the pre-reset coordinate system. `null` when the runtime cannot
29
+ * determine the delta.
30
+ */
31
+ interface ResetTransformData {
32
+ position: Vector3;
33
+ orientation: Quaternion;
34
+ }
35
+ /**
36
+ * Callbacks for tracking state changes
37
+ */
38
+ interface TrackingStateCallbacks {
39
+ /** Called when tracking is lost (pose became null) */
40
+ onTrackingLost: () => void;
41
+ /** Called when tracking resumes after a coordinate-frame reset (Case 2: relocalization) */
42
+ onTrackingRestarted: (payload: OdometryTrackingRestartedPayload) => void;
43
+ /** Called when tracking resumes seamlessly without origin reset (Case 1) */
44
+ onTrackingRecovered?: () => void;
45
+ /** Get current device orientation for sensor rotation */
46
+ getDeviceOrientation: () => DeviceOrientation;
47
+ }
48
+ /**
49
+ * Manages AR tracking state and detects loss/restart events.
50
+ */
51
+ declare class TrackingStateManager {
52
+ private state;
53
+ private lastValidPose;
54
+ private lastSensorOrientation;
55
+ private lostFrameCount;
56
+ private originResetDuringLoss;
57
+ private resetTransform;
58
+ private callbacks;
59
+ constructor(callbacks: TrackingStateCallbacks);
60
+ /**
61
+ * Get current tracking state.
62
+ */
63
+ getState(): TrackingState;
64
+ /**
65
+ * Get the last valid AR pose (before tracking was lost).
66
+ */
67
+ getLastValidPose(): ARPose | null;
68
+ /**
69
+ * Get count of consecutive frames without pose (during LOST state).
70
+ */
71
+ getLostFrameCount(): number;
72
+ /**
73
+ * Flag that an XRReferenceSpace reset event occurred during tracking loss.
74
+ * Only meaningful while in LOST state — ignored otherwise.
75
+ *
76
+ * @param transform — Serialized XRReferenceSpaceEvent.transform (post-reset
77
+ * origin in pre-reset coordinates). Pass `null` when the event's
78
+ * transform property is null (runtime can't determine delta).
79
+ * Omit entirely for backwards-compat with callers that don't have it.
80
+ */
81
+ markOriginReset(transform?: ResetTransformData | null): void;
82
+ /**
83
+ * Called when a valid pose is received from WebXR.
84
+ */
85
+ onPoseReceived(pose: ARPose): void;
86
+ /**
87
+ * Called when pose is null/unavailable from WebXR.
88
+ */
89
+ onPoseLost(): void;
90
+ /**
91
+ * Reset the manager to initial state.
92
+ * Call this when starting a new session.
93
+ */
94
+ reset(): void;
95
+ /**
96
+ * Handle the transition from LOST to TRACKING (restart detected).
97
+ */
98
+ private handleTrackingRestarted;
99
+ }
100
+ //#endregion
101
+ export { TrackingStateManager as a, TrackingStateCallbacks as i, ResetTransformData as n, TrackingState as r, DeviceOrientation as t };
@@ -0,0 +1,2 @@
1
+ import { a as WebXRQuaternion, i as DepthSample, n as ArPoseTuples, o as WebXRVec3, r as DepthPoint, t as ARPose } from "../ar-types-B-ORgk6Z.js";
2
+ export { ARPose, ArPoseTuples, DepthPoint, DepthSample, WebXRQuaternion, WebXRVec3 };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { n as RefPointMarker, t as GpsCoord } from "../geo-types-Xx-LVcnw.js";
2
+ export { GpsCoord, RefPointMarker };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { a as WebXRQuaternion, i as DepthSample, n as ArPoseTuples, o as WebXRVec3, r as DepthPoint, t as ARPose } from "../ar-types-B-ORgk6Z.js";
2
+ import { n as RefPointMarker, t as GpsCoord } from "../geo-types-Xx-LVcnw.js";
3
+ export { ARPose, ArPoseTuples, DepthPoint, DepthSample, GpsCoord, RefPointMarker, WebXRQuaternion, WebXRVec3 };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { t as mapWithConcurrencyLimit } from "../concurrency-p6gB30fT.js";
2
+ export { mapWithConcurrencyLimit };
@@ -0,0 +1,40 @@
1
+ //#region ../src/utils/concurrency.ts
2
+ /**
3
+ * Concurrency Utilities
4
+ *
5
+ * Provides helpers for limiting the number of concurrent async operations.
6
+ * Used to prevent excessive memory consumption when scanning many files
7
+ * in parallel (e.g., reading zip metadata during scenario discovery).
8
+ */
9
+ /**
10
+ * Map over an array with a concurrency limit on the async mapper function.
11
+ *
12
+ * Behaves like `Promise.all(items.map(fn))` but limits how many mapper
13
+ * invocations run simultaneously. Results are returned in the same order
14
+ * as the input items.
15
+ *
16
+ * @param items - Array of items to process
17
+ * @param limit - Maximum number of concurrent mapper invocations
18
+ * @param mapper - Async function to apply to each item
19
+ * @returns Array of results in the same order as input items
20
+ * @throws Re-throws the first error from any mapper invocation (fail-fast)
21
+ */
22
+ async function mapWithConcurrencyLimit(items, limit, mapper) {
23
+ if (limit < 1) throw new RangeError(`Concurrency limit must be >= 1, got ${limit}`);
24
+ const results = new Array(items.length);
25
+ if (items.length === 0) return results;
26
+ let nextIndex = 0;
27
+ const worker = async () => {
28
+ while (nextIndex < items.length) {
29
+ const currentIndex = nextIndex++;
30
+ results[currentIndex] = await mapper(items[currentIndex], currentIndex);
31
+ }
32
+ };
33
+ const workerCount = Math.min(limit, items.length);
34
+ const workers = [];
35
+ for (let i = 0; i < workerCount; i++) workers.push(worker());
36
+ await Promise.all(workers);
37
+ return results;
38
+ }
39
+ //#endregion
40
+ export { mapWithConcurrencyLimit };
@@ -0,0 +1,2 @@
1
+ import { n as FailureTrackerConfig, r as createFailureTracker, t as FailureTracker } from "../failure-tracker-1sQ_Cgyw.js";
2
+ export { FailureTracker, FailureTrackerConfig, createFailureTracker };
@@ -0,0 +1,48 @@
1
+ import { createLogger } from "./logger.js";
2
+ //#region ../src/utils/failure-tracker.ts
3
+ /**
4
+ * Generic Failure Tracker Factory
5
+ *
6
+ * Shared pattern extracted from capture-failure-tracker and write-failure-tracker.
7
+ * Tracks consecutive failures, warns the user once when a threshold is exceeded,
8
+ * and supports session-level reset.
9
+ */
10
+ /**
11
+ * Create a generic failure tracker.
12
+ *
13
+ * @param config - Tracker configuration
14
+ * @returns A FailureTracker instance
15
+ */
16
+ function createFailureTracker(config) {
17
+ const { label, warningMessage, defaultThreshold, onWarning, logLevel = "warn" } = config;
18
+ const threshold = config.failureThreshold ?? defaultThreshold;
19
+ const log = createLogger(label);
20
+ let consecutiveFailures = 0;
21
+ let warningShown = false;
22
+ return {
23
+ recordSuccess() {
24
+ consecutiveFailures = 0;
25
+ },
26
+ recordFailure(error) {
27
+ consecutiveFailures++;
28
+ if (logLevel === "error") log.error(`${label} failure #${consecutiveFailures}:`, error);
29
+ else log.warn(`${label} failure #${consecutiveFailures}`);
30
+ if (consecutiveFailures >= threshold && !warningShown) {
31
+ warningShown = true;
32
+ onWarning(warningMessage);
33
+ }
34
+ },
35
+ getFailureCount() {
36
+ return consecutiveFailures;
37
+ },
38
+ hasWarned() {
39
+ return warningShown;
40
+ },
41
+ reset() {
42
+ consecutiveFailures = 0;
43
+ warningShown = false;
44
+ }
45
+ };
46
+ }
47
+ //#endregion
48
+ export { createFailureTracker };
@@ -0,0 +1,2 @@
1
+ import { t as formatFileSize } from "../format-file-size-onGq0k1Q.js";
2
+ export { formatFileSize };
@@ -0,0 +1,41 @@
1
+ //#region ../src/utils/format-file-size.ts
2
+ /**
3
+ * Format File Size Utility
4
+ *
5
+ * Converts byte counts to human-readable file size strings (B, KB, MB, GB).
6
+ * Used by session summary to display ZIP file statistics.
7
+ *
8
+ * @see 2026-02-06 User Feedback Issue #3: ZIP stats on summary screen
9
+ */
10
+ const UNITS = [
11
+ "B",
12
+ "KB",
13
+ "MB",
14
+ "GB"
15
+ ];
16
+ const THRESHOLD = 1024;
17
+ /**
18
+ * Format a byte count into a human-readable string.
19
+ *
20
+ * @param bytes - Number of bytes (non-negative integer expected)
21
+ * @returns Formatted string like "512 B", "1.5 KB", "23.4 MB", "1.0 GB"
22
+ *
23
+ * @example
24
+ * formatFileSize(0) // "0 B"
25
+ * formatFileSize(512) // "512 B"
26
+ * formatFileSize(1048576) // "1.0 MB"
27
+ * formatFileSize(2500000000) // "2.3 GB"
28
+ */
29
+ function formatFileSize(bytes) {
30
+ if (bytes <= 0) return "0 B";
31
+ let value = bytes;
32
+ let unitIndex = 0;
33
+ while (value >= THRESHOLD && unitIndex < UNITS.length - 1) {
34
+ value /= THRESHOLD;
35
+ unitIndex++;
36
+ }
37
+ if (unitIndex === 0) return `${Math.round(value)} B`;
38
+ return `${value.toFixed(1)} ${UNITS[unitIndex]}`;
39
+ }
40
+ //#endregion
41
+ export { formatFileSize };
@@ -0,0 +1,2 @@
1
+ import { n as computeFusedPath, r as fusedGpsFromOdom, t as FusedPathInput } from "../fused-path-DtqCSqJh.js";
2
+ export { FusedPathInput, computeFusedPath, fusedGpsFromOdom };
@@ -0,0 +1,44 @@
1
+ import { calcGpsCoords, fusedGpsFromOdom } from "gps-plus-slam-js";
2
+ import { mat4, vec3 } from "gl-matrix";
3
+ //#region ../src/utils/fused-path.ts
4
+ /**
5
+ * Fused Path Calculation
6
+ *
7
+ * Transforms AR odometry positions into GPS coordinates using the alignment matrix.
8
+ * This enables displaying the "fused" SLAM+GPS trajectory on the session summary map.
9
+ *
10
+ * The alignment matrix (computed by the alignment solver in the library) transforms
11
+ * AR-local odometry positions into NUE (North-Up-East) meters relative to a
12
+ * GPS zero reference point. We then convert NUE to lat/lng for Leaflet.
13
+ *
14
+ * Internal coordinate convention: X=North, Y=Up, Z=East (right-handed).
15
+ *
16
+ * @see docs/2026-01-27-user-feedback.md Issue #4b
17
+ */
18
+ /**
19
+ * Compute the fused GPS path from odometry positions using the alignment matrix.
20
+ *
21
+ * Each odometry position is transformed by the alignment matrix to get ENU
22
+ * coordinates (meters relative to zeroRef), then converted to GPS lat/lng.
23
+ *
24
+ * @param input - Odometry positions, alignment matrix, and GPS zero reference
25
+ * @returns Array of GPS coordinates suitable for Leaflet polyline
26
+ */
27
+ function computeFusedPath(input) {
28
+ const { odometryPositions, alignmentMatrix, zeroRef } = input;
29
+ if (!alignmentMatrix || !zeroRef || odometryPositions.length === 0) return [];
30
+ const matrix = mat4.fromValues(...alignmentMatrix);
31
+ const odomVec = vec3.create();
32
+ const alignedVec = vec3.create();
33
+ return odometryPositions.map((odomPos) => {
34
+ vec3.set(odomVec, odomPos[0], odomPos[1], odomPos[2]);
35
+ vec3.transformMat4(alignedVec, odomVec, matrix);
36
+ const gpsCoord = calcGpsCoords(zeroRef, alignedVec);
37
+ return {
38
+ lat: gpsCoord.lat,
39
+ lng: gpsCoord.lon
40
+ };
41
+ });
42
+ }
43
+ //#endregion
44
+ export { computeFusedPath, fusedGpsFromOdom };
@@ -0,0 +1,7 @@
1
+ import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-B81iwxx0.js";
2
+ import { n as computeFusedPath, r as fusedGpsFromOdom, t as FusedPathInput } from "../fused-path-DtqCSqJh.js";
3
+ import { n as FailureTrackerConfig, r as createFailureTracker, t as FailureTracker } from "../failure-tracker-1sQ_Cgyw.js";
4
+ import { t as mapWithConcurrencyLimit } from "../concurrency-p6gB30fT.js";
5
+ import { t as formatFileSize } from "../format-file-size-onGq0k1Q.js";
6
+ import { t as listFormatter } from "../list-formatter-DKQQVLcl.js";
7
+ export { type FailureTracker, type FailureTrackerConfig, type FusedPathInput, type LogEntry, LogLevel, type Logger, clearLogBuffer, computeFusedPath, createFailureTracker, createLogger, formatFileSize, fusedGpsFromOdom, getGlobalLogLevel, getLogBuffer, listFormatter, mapWithConcurrencyLimit, setGlobalLogLevel, subscribeToLogs };
@@ -0,0 +1,7 @@
1
+ import { LogLevel, clearLogBuffer, createLogger, getGlobalLogLevel, getLogBuffer, setGlobalLogLevel, subscribeToLogs } from "./logger.js";
2
+ import { createFailureTracker } from "./failure-tracker.js";
3
+ import { computeFusedPath, fusedGpsFromOdom } from "./fused-path.js";
4
+ import { mapWithConcurrencyLimit } from "./concurrency.js";
5
+ import { formatFileSize } from "./format-file-size.js";
6
+ import { listFormatter } from "./list-formatter.js";
7
+ export { LogLevel, clearLogBuffer, computeFusedPath, createFailureTracker, createLogger, formatFileSize, fusedGpsFromOdom, getGlobalLogLevel, getLogBuffer, listFormatter, mapWithConcurrencyLimit, setGlobalLogLevel, subscribeToLogs };
@@ -0,0 +1,2 @@
1
+ import { t as listFormatter } from "../list-formatter-DKQQVLcl.js";
2
+ export { listFormatter };
@@ -0,0 +1,17 @@
1
+ //#region ../src/utils/list-formatter.ts
2
+ /**
3
+ * Shared list formatter for displaying comma-separated lists with "and".
4
+ *
5
+ * Uses English locale to match the app's UI language. If i18n support is added
6
+ * in the future, this should be updated to use the user's preferred locale.
7
+ *
8
+ * @example
9
+ * listFormatter.format(['Camera', 'Location']) // "Camera and Location"
10
+ * listFormatter.format(['A', 'B', 'C']) // "A, B, and C"
11
+ */
12
+ const listFormatter = new Intl.ListFormat("en", {
13
+ style: "long",
14
+ type: "conjunction"
15
+ });
16
+ //#endregion
17
+ export { listFormatter };
@@ -0,0 +1,2 @@
1
+ import { a as createLogger, c as setGlobalLogLevel, i as clearLogBuffer, l as subscribeToLogs, n as LogLevel, o as getGlobalLogLevel, r as Logger, s as getLogBuffer, t as LogEntry } from "../logger-B81iwxx0.js";
2
+ export { LogEntry, LogLevel, Logger, clearLogBuffer, createLogger, getGlobalLogLevel, getLogBuffer, setGlobalLogLevel, subscribeToLogs };