@mongrov/analytics 0.1.0-alpha.1 → 0.1.0-alpha.10

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 (347) hide show
  1. package/README.md +472 -0
  2. package/dist/core/context.d.ts +24 -0
  3. package/dist/core/context.d.ts.map +1 -0
  4. package/dist/core/context.js +30 -0
  5. package/dist/core/context.js.map +1 -0
  6. package/dist/core/engine.d.ts +56 -0
  7. package/dist/core/engine.d.ts.map +1 -0
  8. package/dist/core/engine.js +119 -0
  9. package/dist/core/engine.js.map +1 -0
  10. package/dist/core/errors.d.ts +1 -1
  11. package/dist/core/errors.d.ts.map +1 -1
  12. package/dist/core/errors.js.map +1 -1
  13. package/dist/core/extensions.d.ts +33 -0
  14. package/dist/core/extensions.d.ts.map +1 -0
  15. package/dist/core/extensions.js +58 -0
  16. package/dist/core/extensions.js.map +1 -0
  17. package/dist/core/factory.d.ts +22 -5
  18. package/dist/core/factory.d.ts.map +1 -1
  19. package/dist/core/factory.js +231 -6
  20. package/dist/core/factory.js.map +1 -1
  21. package/dist/core/hooks.d.ts +32 -13
  22. package/dist/core/hooks.d.ts.map +1 -1
  23. package/dist/core/hooks.js +137 -15
  24. package/dist/core/hooks.js.map +1 -1
  25. package/dist/core/index.d.ts +3 -0
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/index.js +3 -1
  28. package/dist/core/index.js.map +1 -1
  29. package/dist/core/logger.d.ts +25 -0
  30. package/dist/core/logger.d.ts.map +1 -0
  31. package/dist/core/logger.js +33 -0
  32. package/dist/core/logger.js.map +1 -0
  33. package/dist/core/machine.d.ts +174 -0
  34. package/dist/core/machine.d.ts.map +1 -0
  35. package/dist/core/machine.js +228 -0
  36. package/dist/core/machine.js.map +1 -0
  37. package/dist/core/metric_metadata.d.ts +128 -0
  38. package/dist/core/metric_metadata.d.ts.map +1 -0
  39. package/dist/core/metric_metadata.js +47 -0
  40. package/dist/core/metric_metadata.js.map +1 -0
  41. package/dist/core/migrations.d.ts +52 -0
  42. package/dist/core/migrations.d.ts.map +1 -0
  43. package/dist/core/migrations.js +66 -0
  44. package/dist/core/migrations.js.map +1 -0
  45. package/dist/core/persistence.d.ts +50 -0
  46. package/dist/core/persistence.d.ts.map +1 -0
  47. package/dist/core/persistence.js +80 -0
  48. package/dist/core/persistence.js.map +1 -0
  49. package/dist/core/retention.d.ts +90 -0
  50. package/dist/core/retention.d.ts.map +1 -0
  51. package/dist/core/retention.js +119 -0
  52. package/dist/core/retention.js.map +1 -0
  53. package/dist/core/schemas.d.ts +35 -0
  54. package/dist/core/schemas.d.ts.map +1 -0
  55. package/dist/core/schemas.js +209 -0
  56. package/dist/core/schemas.js.map +1 -0
  57. package/dist/core/types.d.ts +10 -0
  58. package/dist/core/types.d.ts.map +1 -1
  59. package/dist/core/warehouse.d.ts +58 -0
  60. package/dist/core/warehouse.d.ts.map +1 -0
  61. package/dist/core/warehouse.js +116 -0
  62. package/dist/core/warehouse.js.map +1 -0
  63. package/dist/rules/__fakes__/fakeClock.d.ts +7 -0
  64. package/dist/rules/__fakes__/fakeClock.d.ts.map +1 -0
  65. package/dist/rules/__fakes__/fakeClock.js +13 -0
  66. package/dist/rules/__fakes__/fakeClock.js.map +1 -0
  67. package/dist/rules/__fakes__/fakeEngine.d.ts +12 -0
  68. package/dist/rules/__fakes__/fakeEngine.d.ts.map +1 -0
  69. package/dist/rules/__fakes__/fakeEngine.js +33 -0
  70. package/dist/rules/__fakes__/fakeEngine.js.map +1 -0
  71. package/dist/rules/__fakes__/fakeStorage.d.ts +7 -0
  72. package/dist/rules/__fakes__/fakeStorage.d.ts.map +1 -0
  73. package/dist/rules/__fakes__/fakeStorage.js +22 -0
  74. package/dist/rules/__fakes__/fakeStorage.js.map +1 -0
  75. package/dist/rules/compiler-cache.d.ts +16 -0
  76. package/dist/rules/compiler-cache.d.ts.map +1 -0
  77. package/dist/rules/compiler-cache.js +29 -0
  78. package/dist/rules/compiler-cache.js.map +1 -0
  79. package/dist/rules/compiler.d.ts +27 -0
  80. package/dist/rules/compiler.d.ts.map +1 -0
  81. package/dist/rules/compiler.js +210 -0
  82. package/dist/rules/compiler.js.map +1 -0
  83. package/dist/rules/defaults/index.d.ts +13 -0
  84. package/dist/rules/defaults/index.d.ts.map +1 -0
  85. package/dist/rules/defaults/index.js +13 -0
  86. package/dist/rules/defaults/index.js.map +1 -0
  87. package/dist/rules/defaults/loader.d.ts +18 -0
  88. package/dist/rules/defaults/loader.d.ts.map +1 -0
  89. package/dist/rules/defaults/loader.js +37 -0
  90. package/dist/rules/defaults/loader.js.map +1 -0
  91. package/dist/rules/defaults/luminx.d.ts +6 -0
  92. package/dist/rules/defaults/luminx.d.ts.map +1 -0
  93. package/dist/rules/defaults/luminx.js +64 -0
  94. package/dist/rules/defaults/luminx.js.map +1 -0
  95. package/dist/rules/defaults/viva.d.ts +6 -0
  96. package/dist/rules/defaults/viva.d.ts.map +1 -0
  97. package/dist/rules/defaults/viva.js +45 -0
  98. package/dist/rules/defaults/viva.js.map +1 -0
  99. package/dist/rules/defaults/yogaring.d.ts +6 -0
  100. package/dist/rules/defaults/yogaring.d.ts.map +1 -0
  101. package/dist/rules/defaults/yogaring.js +45 -0
  102. package/dist/rules/defaults/yogaring.js.map +1 -0
  103. package/dist/rules/defaults/ziva.d.ts +10 -0
  104. package/dist/rules/defaults/ziva.d.ts.map +1 -0
  105. package/dist/rules/defaults/ziva.js +87 -0
  106. package/dist/rules/defaults/ziva.js.map +1 -0
  107. package/dist/rules/emitter.d.ts +20 -0
  108. package/dist/rules/emitter.d.ts.map +1 -0
  109. package/dist/rules/emitter.js +32 -0
  110. package/dist/rules/emitter.js.map +1 -0
  111. package/dist/rules/evaluator.d.ts +44 -0
  112. package/dist/rules/evaluator.d.ts.map +1 -0
  113. package/dist/rules/evaluator.js +127 -0
  114. package/dist/rules/evaluator.js.map +1 -0
  115. package/dist/rules/factory.d.ts +13 -0
  116. package/dist/rules/factory.d.ts.map +1 -0
  117. package/dist/rules/factory.js +56 -0
  118. package/dist/rules/factory.js.map +1 -0
  119. package/dist/rules/hooks/useRuleRegistry.d.ts +16 -0
  120. package/dist/rules/hooks/useRuleRegistry.d.ts.map +1 -0
  121. package/dist/rules/hooks/useRuleRegistry.js +26 -0
  122. package/dist/rules/hooks/useRuleRegistry.js.map +1 -0
  123. package/dist/rules/hooks/useRuleViolations.d.ts +21 -0
  124. package/dist/rules/hooks/useRuleViolations.d.ts.map +1 -0
  125. package/dist/rules/hooks/useRuleViolations.js +32 -0
  126. package/dist/rules/hooks/useRuleViolations.js.map +1 -0
  127. package/dist/rules/index.d.ts +30 -1
  128. package/dist/rules/index.d.ts.map +1 -1
  129. package/dist/rules/index.js +36 -1
  130. package/dist/rules/index.js.map +1 -1
  131. package/dist/rules/registry.d.ts +32 -0
  132. package/dist/rules/registry.d.ts.map +1 -0
  133. package/dist/rules/registry.js +98 -0
  134. package/dist/rules/registry.js.map +1 -0
  135. package/dist/rules/schema.d.ts +221 -0
  136. package/dist/rules/schema.d.ts.map +1 -0
  137. package/dist/rules/schema.js +80 -0
  138. package/dist/rules/schema.js.map +1 -0
  139. package/dist/rules/throttle.d.ts +27 -0
  140. package/dist/rules/throttle.d.ts.map +1 -0
  141. package/dist/rules/throttle.js +76 -0
  142. package/dist/rules/throttle.js.map +1 -0
  143. package/dist/rules/types.d.ts +88 -0
  144. package/dist/rules/types.d.ts.map +1 -0
  145. package/dist/rules/types.js +9 -0
  146. package/dist/rules/types.js.map +1 -0
  147. package/dist/rules/validator.d.ts +27 -0
  148. package/dist/rules/validator.d.ts.map +1 -0
  149. package/dist/rules/validator.js +97 -0
  150. package/dist/rules/validator.js.map +1 -0
  151. package/dist/sync/buffer.d.ts +51 -0
  152. package/dist/sync/buffer.d.ts.map +1 -0
  153. package/dist/sync/buffer.js +188 -0
  154. package/dist/sync/buffer.js.map +1 -0
  155. package/dist/sync/context.d.ts +17 -0
  156. package/dist/sync/context.d.ts.map +1 -0
  157. package/dist/sync/context.js +23 -0
  158. package/dist/sync/context.js.map +1 -0
  159. package/dist/sync/errors.d.ts +19 -0
  160. package/dist/sync/errors.d.ts.map +1 -0
  161. package/dist/sync/errors.js +21 -0
  162. package/dist/sync/errors.js.map +1 -0
  163. package/dist/sync/events.d.ts +49 -0
  164. package/dist/sync/events.d.ts.map +1 -0
  165. package/dist/sync/events.js +46 -0
  166. package/dist/sync/events.js.map +1 -0
  167. package/dist/sync/factory.d.ts +65 -0
  168. package/dist/sync/factory.d.ts.map +1 -0
  169. package/dist/sync/factory.js +279 -0
  170. package/dist/sync/factory.js.map +1 -0
  171. package/dist/sync/fetcher.d.ts +74 -0
  172. package/dist/sync/fetcher.d.ts.map +1 -0
  173. package/dist/sync/fetcher.js +166 -0
  174. package/dist/sync/fetcher.js.map +1 -0
  175. package/dist/sync/flusher.d.ts +99 -0
  176. package/dist/sync/flusher.d.ts.map +1 -0
  177. package/dist/sync/flusher.js +246 -0
  178. package/dist/sync/flusher.js.map +1 -0
  179. package/dist/sync/hooks.d.ts +17 -0
  180. package/dist/sync/hooks.d.ts.map +1 -0
  181. package/dist/sync/hooks.js +48 -0
  182. package/dist/sync/hooks.js.map +1 -0
  183. package/dist/sync/index.d.ts +32 -1
  184. package/dist/sync/index.d.ts.map +1 -1
  185. package/dist/sync/index.js +36 -1
  186. package/dist/sync/index.js.map +1 -1
  187. package/dist/sync/manager.d.ts +48 -0
  188. package/dist/sync/manager.d.ts.map +1 -0
  189. package/dist/sync/manager.js +9 -0
  190. package/dist/sync/manager.js.map +1 -0
  191. package/dist/sync/mapper/activity.d.ts +31 -0
  192. package/dist/sync/mapper/activity.d.ts.map +1 -0
  193. package/dist/sync/mapper/activity.js +56 -0
  194. package/dist/sync/mapper/activity.js.map +1 -0
  195. package/dist/sync/mapper/battery.d.ts +12 -0
  196. package/dist/sync/mapper/battery.d.ts.map +1 -0
  197. package/dist/sync/mapper/battery.js +22 -0
  198. package/dist/sync/mapper/battery.js.map +1 -0
  199. package/dist/sync/mapper/firmware.d.ts +28 -0
  200. package/dist/sync/mapper/firmware.d.ts.map +1 -0
  201. package/dist/sync/mapper/firmware.js +43 -0
  202. package/dist/sync/mapper/firmware.js.map +1 -0
  203. package/dist/sync/mapper/heart-rate.d.ts +10 -0
  204. package/dist/sync/mapper/heart-rate.d.ts.map +1 -0
  205. package/dist/sync/mapper/heart-rate.js +19 -0
  206. package/dist/sync/mapper/heart-rate.js.map +1 -0
  207. package/dist/sync/mapper/hrv.d.ts +22 -0
  208. package/dist/sync/mapper/hrv.d.ts.map +1 -0
  209. package/dist/sync/mapper/hrv.js +56 -0
  210. package/dist/sync/mapper/hrv.js.map +1 -0
  211. package/dist/sync/mapper/ring-config.d.ts +33 -0
  212. package/dist/sync/mapper/ring-config.d.ts.map +1 -0
  213. package/dist/sync/mapper/ring-config.js +48 -0
  214. package/dist/sync/mapper/ring-config.js.map +1 -0
  215. package/dist/sync/mapper/sleep.d.ts +32 -0
  216. package/dist/sync/mapper/sleep.d.ts.map +1 -0
  217. package/dist/sync/mapper/sleep.js +103 -0
  218. package/dist/sync/mapper/sleep.js.map +1 -0
  219. package/dist/sync/mapper/spo2.d.ts +9 -0
  220. package/dist/sync/mapper/spo2.d.ts.map +1 -0
  221. package/dist/sync/mapper/spo2.js +18 -0
  222. package/dist/sync/mapper/spo2.js.map +1 -0
  223. package/dist/sync/mapper/temperature.d.ts +10 -0
  224. package/dist/sync/mapper/temperature.d.ts.map +1 -0
  225. package/dist/sync/mapper/temperature.js +19 -0
  226. package/dist/sync/mapper/temperature.js.map +1 -0
  227. package/dist/sync/mapper/time.d.ts +49 -0
  228. package/dist/sync/mapper/time.d.ts.map +1 -0
  229. package/dist/sync/mapper/time.js +107 -0
  230. package/dist/sync/mapper/time.js.map +1 -0
  231. package/dist/sync/mapper/types.d.ts +152 -0
  232. package/dist/sync/mapper/types.d.ts.map +1 -0
  233. package/dist/sync/mapper/types.js +15 -0
  234. package/dist/sync/mapper/types.js.map +1 -0
  235. package/dist/sync/overflow.d.ts +30 -0
  236. package/dist/sync/overflow.d.ts.map +1 -0
  237. package/dist/sync/overflow.js +114 -0
  238. package/dist/sync/overflow.js.map +1 -0
  239. package/dist/sync/pusher.d.ts +59 -0
  240. package/dist/sync/pusher.d.ts.map +1 -0
  241. package/dist/sync/pusher.js +115 -0
  242. package/dist/sync/pusher.js.map +1 -0
  243. package/dist/sync/scheduler.d.ts +84 -0
  244. package/dist/sync/scheduler.d.ts.map +1 -0
  245. package/dist/sync/scheduler.js +132 -0
  246. package/dist/sync/scheduler.js.map +1 -0
  247. package/dist/sync/triggers.d.ts +64 -0
  248. package/dist/sync/triggers.d.ts.map +1 -0
  249. package/dist/sync/triggers.js +113 -0
  250. package/dist/sync/triggers.js.map +1 -0
  251. package/dist/sync/types.d.ts +50 -0
  252. package/dist/sync/types.d.ts.map +1 -0
  253. package/dist/sync/types.js +8 -0
  254. package/dist/sync/types.js.map +1 -0
  255. package/dist/sync/watermark.d.ts +32 -0
  256. package/dist/sync/watermark.d.ts.map +1 -0
  257. package/dist/sync/watermark.js +61 -0
  258. package/dist/sync/watermark.js.map +1 -0
  259. package/dist/tools/__fakes__/engine.d.ts +25 -0
  260. package/dist/tools/__fakes__/engine.d.ts.map +1 -0
  261. package/dist/tools/__fakes__/engine.js +37 -0
  262. package/dist/tools/__fakes__/engine.js.map +1 -0
  263. package/dist/tools/audit.d.ts +31 -0
  264. package/dist/tools/audit.d.ts.map +1 -0
  265. package/dist/tools/audit.js +157 -0
  266. package/dist/tools/audit.js.map +1 -0
  267. package/dist/tools/authorize.d.ts +36 -0
  268. package/dist/tools/authorize.d.ts.map +1 -0
  269. package/dist/tools/authorize.js +87 -0
  270. package/dist/tools/authorize.js.map +1 -0
  271. package/dist/tools/budget.d.ts +15 -0
  272. package/dist/tools/budget.d.ts.map +1 -0
  273. package/dist/tools/budget.js +64 -0
  274. package/dist/tools/budget.js.map +1 -0
  275. package/dist/tools/factory.d.ts +43 -0
  276. package/dist/tools/factory.d.ts.map +1 -0
  277. package/dist/tools/factory.js +137 -0
  278. package/dist/tools/factory.js.map +1 -0
  279. package/dist/tools/formatters.d.ts +17 -0
  280. package/dist/tools/formatters.d.ts.map +1 -0
  281. package/dist/tools/formatters.js +30 -0
  282. package/dist/tools/formatters.js.map +1 -0
  283. package/dist/tools/impls/activity.d.ts +15 -0
  284. package/dist/tools/impls/activity.d.ts.map +1 -0
  285. package/dist/tools/impls/activity.js +61 -0
  286. package/dist/tools/impls/activity.js.map +1 -0
  287. package/dist/tools/impls/anomaly.d.ts +21 -0
  288. package/dist/tools/impls/anomaly.d.ts.map +1 -0
  289. package/dist/tools/impls/anomaly.js +70 -0
  290. package/dist/tools/impls/anomaly.js.map +1 -0
  291. package/dist/tools/impls/compare.d.ts +21 -0
  292. package/dist/tools/impls/compare.d.ts.map +1 -0
  293. package/dist/tools/impls/compare.js +68 -0
  294. package/dist/tools/impls/compare.js.map +1 -0
  295. package/dist/tools/impls/hrv.d.ts +15 -0
  296. package/dist/tools/impls/hrv.d.ts.map +1 -0
  297. package/dist/tools/impls/hrv.js +36 -0
  298. package/dist/tools/impls/hrv.js.map +1 -0
  299. package/dist/tools/impls/insights.d.ts +18 -0
  300. package/dist/tools/impls/insights.d.ts.map +1 -0
  301. package/dist/tools/impls/insights.js +41 -0
  302. package/dist/tools/impls/insights.js.map +1 -0
  303. package/dist/tools/impls/sleep.d.ts +15 -0
  304. package/dist/tools/impls/sleep.d.ts.map +1 -0
  305. package/dist/tools/impls/sleep.js +43 -0
  306. package/dist/tools/impls/sleep.js.map +1 -0
  307. package/dist/tools/index.d.ts +13 -1
  308. package/dist/tools/index.d.ts.map +1 -1
  309. package/dist/tools/index.js +21 -1
  310. package/dist/tools/index.js.map +1 -1
  311. package/dist/tools/mcp/adapter.d.ts +33 -0
  312. package/dist/tools/mcp/adapter.d.ts.map +1 -0
  313. package/dist/tools/mcp/adapter.js +56 -0
  314. package/dist/tools/mcp/adapter.js.map +1 -0
  315. package/dist/tools/mcp/guard.d.ts +17 -0
  316. package/dist/tools/mcp/guard.d.ts.map +1 -0
  317. package/dist/tools/mcp/guard.js +22 -0
  318. package/dist/tools/mcp/guard.js.map +1 -0
  319. package/dist/tools/mcp/index.d.ts +20 -0
  320. package/dist/tools/mcp/index.d.ts.map +1 -0
  321. package/dist/tools/mcp/index.js +16 -0
  322. package/dist/tools/mcp/index.js.map +1 -0
  323. package/dist/tools/mcp/server.d.ts +39 -0
  324. package/dist/tools/mcp/server.d.ts.map +1 -0
  325. package/dist/tools/mcp/server.js +70 -0
  326. package/dist/tools/mcp/server.js.map +1 -0
  327. package/dist/tools/mcp/transports/http.d.ts +54 -0
  328. package/dist/tools/mcp/transports/http.d.ts.map +1 -0
  329. package/dist/tools/mcp/transports/http.js +133 -0
  330. package/dist/tools/mcp/transports/http.js.map +1 -0
  331. package/dist/tools/mcp/transports/stdio.d.ts +22 -0
  332. package/dist/tools/mcp/transports/stdio.d.ts.map +1 -0
  333. package/dist/tools/mcp/transports/stdio.js +19 -0
  334. package/dist/tools/mcp/transports/stdio.js.map +1 -0
  335. package/dist/tools/rate-limit.d.ts +26 -0
  336. package/dist/tools/rate-limit.d.ts.map +1 -0
  337. package/dist/tools/rate-limit.js +67 -0
  338. package/dist/tools/rate-limit.js.map +1 -0
  339. package/dist/tools/types.d.ts +121 -0
  340. package/dist/tools/types.d.ts.map +1 -0
  341. package/dist/tools/types.js +16 -0
  342. package/dist/tools/types.js.map +1 -0
  343. package/dist/tools/wrap.d.ts +53 -0
  344. package/dist/tools/wrap.d.ts.map +1 -0
  345. package/dist/tools/wrap.js +135 -0
  346. package/dist/tools/wrap.js.map +1 -0
  347. package/package.json +28 -2
package/README.md ADDED
@@ -0,0 +1,472 @@
1
+ # @mongrov/analytics
2
+
3
+ DuckDB + R2 Iceberg local analytics for React Native / Expo apps in the Mongrov platform.
4
+
5
+ Per-family (or per-tenant) warehouses attached as DuckDB catalogs. Zero-copy
6
+ reads against R2 Iceberg tables. Headless core with optional React hooks and
7
+ UI subpaths.
8
+
9
+ Status: **0.1.0-alpha.7** — Phases 1 → 7 landed on the core engine plus the
10
+ full `@mongrov/analytics/sync` subpath (mapper → buffer → flusher → watermark
11
+ → pusher → fetcher → scheduler → factory + hooks). MinIO integration test
12
+ (T-18 / T-28) and app-kill e2e (T-29) still pending; `0.1.0` publish (T-20)
13
+ follows once integration coverage lands.
14
+
15
+ ## Features
16
+
17
+ - **Adapter-friendly DuckDB engine** — `HybridDuckDB` wrapper with pluggable
18
+ `DuckDBFactory` so tests can substitute an in-memory fake.
19
+ - **Per-tenant Iceberg warehouse** — `CREATE SECRET` + `ATTACH ... (TYPE ICEBERG)`
20
+ for a single `zone_<tenantId>` catalog per attach.
21
+ - **Migration runner** — versioned per `(brand, tenantId)`, idempotent, KV-tracked.
22
+ - **Retention scheduler** — `max(userOverride, familySetting, brandDefault)`
23
+ precedence; sensor tables use effective days, `insight` 90d, `tool_call_audit`
24
+ 30d; DELETE bounded by `sync_watermark` so unsynced rows never drop.
25
+ - **KV persistence** — last-attach ctx restored across restarts (24h TTL);
26
+ per-user retention override.
27
+ - **XState v5 state machine** — `idle → opening → ready → attaching → attached
28
+ → detaching → error` with token-refresh actor at 75% TTL.
29
+ - **React hooks + provider** — `useAnalytics`, `useTimeseries`, `useInsight`,
30
+ `AnalyticsProvider`.
31
+ - **Structured logger** — supply an `AnalyticsLogger` via config; falls back
32
+ to a no-op sink. No logs on the hot query path.
33
+ - **Typed error taxonomy** — `AnalyticsError` with a code union covering every
34
+ failure mode (`engine_open_failed`, `attach_failed`, `detach_failed`,
35
+ `token_vendor_failed`, `migration_failed`, `retention_failed`, `query_failed`,
36
+ `not_attached`, `not_ready`, `not_implemented`, `extension_load_failed`).
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pnpm add @mongrov/analytics
42
+ # Peer deps
43
+ pnpm add react-native-duckdb @mongrov/db @mongrov/types
44
+ ```
45
+
46
+ `react-native-duckdb` is declared as an **optional peer** — the package boots
47
+ lazily so importing `createAnalytics` on a Node/CI host without the native
48
+ module does not throw. Failure to resolve it surfaces on `open()` as
49
+ `AnalyticsError('engine_open_failed')`.
50
+
51
+ ## Quick Start
52
+
53
+ ```tsx
54
+ import { AnalyticsProvider, createAnalytics, useAnalytics } from '@mongrov/analytics'
55
+ import type { AnalyticsConfig } from '@mongrov/analytics'
56
+
57
+ const config: AnalyticsConfig = {
58
+ storage: kv, // @mongrov/db KVStore or any structural equivalent
59
+ warehouseUriBuilder: (brand, scope, tenantId) =>
60
+ `s3://mongrov-analytics/${brand}/${tenantId}/warehouse`,
61
+ catalogEndpoint: 'https://catalog.mongrov.example',
62
+ tokenVendor: {
63
+ async fetch({ brand, tenantScope, tenantId }) {
64
+ const response = await api.getWarehouseToken({ brand, tenantScope, tenantId })
65
+ return { token: response.token, expiresAt: new Date(response.expiresAt) }
66
+ },
67
+ },
68
+ familyMembersProvider: async ({ brand, familyId }) => {
69
+ return api.listFamilyMembers({ brand, familyId })
70
+ },
71
+ retention: {
72
+ ziva: { days: 180 },
73
+ luminx: { days: 90 },
74
+ },
75
+ logger: appLogger, // optional; defaults to a no-op
76
+ }
77
+
78
+ const engine = createAnalytics(config)
79
+
80
+ function App() {
81
+ return (
82
+ <AnalyticsProvider engine={engine}>
83
+ <MyDashboard />
84
+ </AnalyticsProvider>
85
+ )
86
+ }
87
+
88
+ function MyDashboard() {
89
+ const { state, attach, isAttached } = useAnalytics()
90
+
91
+ useEffect(() => {
92
+ if (!isAttached) {
93
+ attach({
94
+ brand: 'ziva',
95
+ tenantScope: 'family',
96
+ tenantId: 'fam_123',
97
+ userId: 'user_1',
98
+ })
99
+ }
100
+ }, [isAttached, attach])
101
+
102
+ return <Text>State: {state}</Text>
103
+ }
104
+ ```
105
+
106
+ ## Public API
107
+
108
+ ### `createAnalytics(config: AnalyticsConfig): AnalyticsEngine`
109
+
110
+ Wires config + engine + XState machine into an `AnalyticsEngine`. Kicks
111
+ `OPEN` synchronously; the returned engine is `opening` on the next tick.
112
+
113
+ ### `AnalyticsProvider`
114
+
115
+ React context provider. Wrap once at the app root with a shared engine
116
+ instance so hooks can consume it.
117
+
118
+ ### `useAnalytics(): UseAnalyticsResult`
119
+
120
+ Subscribes to state transitions via `useSyncExternalStore`. Returns:
121
+
122
+ ```ts
123
+ type UseAnalyticsResult = {
124
+ state: AnalyticsState
125
+ isReady: boolean
126
+ isAttached: boolean
127
+ error: Error | null
128
+ attach: (ctx: AttachContext) => Promise<void>
129
+ detach: () => Promise<void>
130
+ execute: AnalyticsEngine['execute']
131
+ stream: AnalyticsEngine['stream']
132
+ setRetention: (days: number) => Promise<void>
133
+ }
134
+ ```
135
+
136
+ ### `useTimeseries<T>(key, params?)`
137
+
138
+ Reactive query hook. Runs on mount when engine is attached; re-runs on ctx or
139
+ key change; supports manual `refetch`; exposes loading + error state.
140
+
141
+ ### `useInsight<T>(id)`
142
+
143
+ Reads a single row from the `insight` table by `id`. Returns `undefined`
144
+ if not found.
145
+
146
+ ### `AnalyticsEngine`
147
+
148
+ Full method surface:
149
+
150
+ - `attach(ctx: AttachContext): Promise<void>`
151
+ - `detach(): Promise<void>`
152
+ - `execute(sql, params?): Promise<Row[]>`
153
+ - `stream(sql, params?): AsyncIterable<Row>`
154
+ - `createAppender(table): AnalyticsAppender`
155
+ - `setRetention(days: number): Promise<void>` — persists user override and
156
+ re-runs sweep with the effective value
157
+ - `getLastAttach(brand: string): Promise<AttachContext | null>` — restore
158
+ ctx from KV (null when older than 24h)
159
+ - `subscribe(listener): Unsubscribe`
160
+ - `close(): Promise<void>`
161
+ - getters: `state`, `lastError`, `catalog`
162
+
163
+ ## Config Reference
164
+
165
+ | Field | Type | Notes |
166
+ |---|---|---|
167
+ | `storage` | `KVStore` | 3-method structural type; supply `@mongrov/db` KVStore. |
168
+ | `warehouseUriBuilder` | `(brand, scope, tenantId) => string` | Returns the S3/Iceberg URI. |
169
+ | `catalogEndpoint` | `string` | Iceberg REST endpoint used by `CREATE SECRET`. |
170
+ | `tokenVendor` | `TokenVendor` | `.fetch({ brand, tenantScope, tenantId })` returns bearer token + `expiresAt`. |
171
+ | `familyMembersProvider` | `FamilyMembersProvider` | Called only when `tenantScope === 'family'`; primes retention math. |
172
+ | `retention` | `Record<string, { days: number }>` | Brand-keyed defaults. Sensor tables use `max(userOverride, familySetting, brandDefault)`; `insight` = 90d fixed; `tool_call_audit` = 30d fixed. |
173
+ | `logger?` | `AnalyticsLogger` | 4 levels (`debug` / `info` / `warn` / `error`); defaults to a no-op sink. |
174
+
175
+ ## State Machine
176
+
177
+ ```
178
+ idle → opening → ready → attaching → attached → detaching → ready
179
+ ↘ error ↙
180
+ ```
181
+
182
+ - `opening` runs `bootstrapExtensions()` (httpfs + iceberg + parquet).
183
+ - `attaching` runs `attachWarehouse` (URI builder → token fetch → CREATE
184
+ SECRET → ATTACH → optional family-members prime) then `ensureMigrations`.
185
+ - `attached` self-schedules a token refresh at 75% TTL via
186
+ `CREATE OR REPLACE SECRET`.
187
+ - `error` is recoverable — re-issue `OPEN` (or `attach()` from userland).
188
+
189
+ ## Retention
190
+
191
+ Precedence per spec (any layer that preserves data wins):
192
+
193
+ ```ts
194
+ effectiveDays = max(userOverride ?? 0, familySetting ?? 0, brandDefault)
195
+ ```
196
+
197
+ Per-table SQL:
198
+
199
+ ```sql
200
+ DELETE FROM {catalog}.{table}
201
+ WHERE {tsCol} < LEAST(
202
+ now() - INTERVAL '{days} days',
203
+ (SELECT MAX(cursor_ts) FROM {catalog}.sync_watermark WHERE table_name = '{table}')
204
+ );
205
+ ```
206
+
207
+ - `MAX(cursor_ts)` returns NULL when nothing has synced → `LEAST(_, NULL) = NULL`
208
+ → `ts < NULL` is FALSE → unsynced rows preserved.
209
+ - Sensor tables (hrv, heart_rate, spo2, temperature, activity, activity_bucket,
210
+ sleep_session, sleep_stage, sleep_raw, device_event) use `effectiveDays`.
211
+ - `sleep_session` sweeps by `ts_end` (session close), not `ts_start`.
212
+ - `insight` sweeps at 90d fixed.
213
+ - `tool_call_audit` sweeps at 30d fixed.
214
+ - `device_config` + `sync_watermark` are skipped.
215
+
216
+ Retention sweep runs automatically on every successful `attach()` and on every
217
+ `setRetention()` call. Failures raise `AnalyticsError('retention_failed', …)`;
218
+ the attach-time sweep swallows and logs via `warn` so a DELETE failure never
219
+ rejects a good attach.
220
+
221
+ ## Persistence
222
+
223
+ - **Last-attach:** `analytics:last-attach:{brand}` → `{ ctx, savedAt }`.
224
+ Expires after 24h; `loadLastAttach` deletes stale entries as a side effect.
225
+ - **Retention override:** `analytics:retention:override:{brand}:{tenantId}:{userId}`
226
+ → `number`. Overwrites cleanly on `setRetention()`.
227
+
228
+ Both writes inside `attach()` and `setRetention()` are best-effort; failure
229
+ surfaces as a `logger.warn(...)` call, never a rejected promise on the caller.
230
+
231
+ ## Logging
232
+
233
+ The library only emits at these levels:
234
+
235
+ - **debug** — state transitions on every distinct value change
236
+ (`analytics.state { value }`)
237
+ - **info** — lifecycle milestones (`analytics.attached`, `analytics.detached`,
238
+ `analytics.closed`)
239
+ - **warn** — best-effort catches (`analytics.persist.last_attach_failed`,
240
+ `analytics.persist.clear_last_attach_failed`, `analytics.retention.sweep_failed`)
241
+ - **error** — reserved for callers who want to surface an `AnalyticsError`
242
+ before re-throwing; not used by the library today.
243
+
244
+ Nothing is emitted on the hot query path (`execute`, `stream`, `createAppender`).
245
+ Supply your own logger to trace at your integration layer if needed.
246
+
247
+ ## Error Taxonomy
248
+
249
+ Every failure surfaces as an `AnalyticsError` with a discriminating `code`:
250
+
251
+ | Code | Origin |
252
+ |---|---|
253
+ | `engine_open_failed` | DuckDB factory or `open()` threw. |
254
+ | `extension_load_failed` | httpfs / iceberg / parquet install/load failed. |
255
+ | `attach_failed` | Any step of the attach protocol (URI, secret, ATTACH, family). |
256
+ | `detach_failed` | `DETACH` or `DROP SECRET`. |
257
+ | `token_vendor_failed` | `tokenVendor.fetch(...)` or refresh CREATE OR REPLACE SECRET. |
258
+ | `migration_failed` | Any migration step; KV rolled back to `n-1`. |
259
+ | `retention_failed` | `runRetentionSweep` DELETE threw. |
260
+ | `query_failed` | `execute` / `stream` native error. |
261
+ | `not_attached` | Caller invoked `setRetention` before attach. |
262
+ | `not_ready` | Query issued on a closed / non-open engine. |
263
+ | `not_implemented` | Stub not yet wired (rare in 0.1.0-alpha.5). |
264
+
265
+ ## Testing
266
+
267
+ The package ships with 125 unit tests across 14 files (Vitest). Fakes for the
268
+ DuckDB instance, KV store, machine actors, and attach dependencies live in
269
+ `src/core/__tests__/__fakes__/`.
270
+
271
+ ```bash
272
+ pnpm test # single run
273
+ pnpm test --watch # watch mode
274
+ pnpm typecheck # tsc --noEmit
275
+ pnpm build # tsc -p tsconfig.build.json
276
+ ```
277
+
278
+ **Fake DuckDB** — captures every issued SQL for assertions and scripts return
279
+ values or errors:
280
+
281
+ ```ts
282
+ import { createFakeDuckDB } from '@mongrov/analytics/__tests__/__fakes__/fake-duckdb'
283
+
284
+ const fake = createFakeDuckDB()
285
+ fake.failNextExecute(new Error('disk full'))
286
+ fake.failExecuteMatching(/^DELETE FROM /, new Error('retention sad'))
287
+ ```
288
+
289
+ **Fake KV** — in-memory `KVStore` implementation with the backing map
290
+ exposed for direct assertions.
291
+
292
+ ## Integration testing (T-18 — pending)
293
+
294
+ Full end-to-end coverage against a real MinIO + Iceberg REST catalog is
295
+ planned for T-18 in Phase 8. The intended shape:
296
+
297
+ 1. Boot MinIO + Iceberg REST via testcontainers.
298
+ 2. `createAnalytics` with a real `duckdbFactory` and pointing at the test
299
+ endpoint.
300
+ 3. `attach → INSERT (via appender) → SELECT → detach → reattach` — assert
301
+ no leakage.
302
+ 4. Brand-switch scenario: attach `brandA`, detach, attach `brandB`, confirm
303
+ no cross-brand catalog visibility.
304
+
305
+ This suite is deferred until testcontainers infra lands.
306
+
307
+ ## Boundaries
308
+
309
+ - **No** direct dependencies on `react-native-ble-plx`, `@mongrov/device`,
310
+ `@mongrov/collab`, or any UI-only package.
311
+ - **No** value-level import from `@mongrov/db` — the `KVStore` contract is a
312
+ local structural type so this package can be consumed alone.
313
+ - **No** logs on the hot query path.
314
+ - **No** rejection of a successful attach for persistence / retention failures.
315
+
316
+ ## Sync (`@mongrov/analytics/sync`)
317
+
318
+ The `/sync` subpath layers the write-side pipeline on top of the core engine:
319
+ firmware/live rows → mapper → in-memory ring buffer → BatchFlusher (p-queue
320
+ serialised per table) → local DuckDB → R2Pusher → R2Fetcher (incremental /
321
+ prefetch) → SyncScheduler (`expo-background-task`-friendly cycle).
322
+
323
+ ### Quick wiring
324
+
325
+ ```tsx
326
+ import { createAnalytics } from '@mongrov/analytics'
327
+ import {
328
+ createSyncManager,
329
+ SyncProvider,
330
+ useSensorSink,
331
+ useSyncProgress,
332
+ useSyncState,
333
+ } from '@mongrov/analytics/sync'
334
+
335
+ const analytics = createAnalytics(analyticsConfig)
336
+
337
+ const sync = createSyncManager({
338
+ analytics,
339
+ storage: kv,
340
+ ctx: { brand: 'ziva', tenantScope: 'family', tenantId: 'fam_123', userId: 'u1' },
341
+ tables: [
342
+ 'hrv', 'heart_rate', 'spo2', 'temperature',
343
+ 'activity', 'activity_bucket',
344
+ 'sleep_session', 'sleep_stage', 'sleep_raw',
345
+ 'device_event', 'device_config',
346
+ ],
347
+ columnOrder: {
348
+ hrv: ['user_id', 'device_id', 'ts', 'rmssd_ms', 'sdnn_ms'],
349
+ // …one entry per table matching the Iceberg column order.
350
+ },
351
+ prefetchPolicy: { kind: 'recent-active-only', activeDays: 30, windowDays: 90 },
352
+ flush: { maxRows: 500, maxAgeMs: 60_000, concurrency: 3 },
353
+ overflow: { maxBufferBytes: 5 * 1024 * 1024, policy: 'drop-oldest' },
354
+ scheduler: { requiresCharging: false, requiresWifi: true, taskName: 'mongrov.sync' },
355
+ eventBus: appEventBus, // optional
356
+ refreshToken: analytics.refreshToken, // optional
357
+ })
358
+
359
+ await sync.start()
360
+
361
+ export default function App() {
362
+ return (
363
+ <AnalyticsProvider engine={analytics}>
364
+ <SyncProvider manager={sync}>
365
+ <Root />
366
+ </SyncProvider>
367
+ </AnalyticsProvider>
368
+ )
369
+ }
370
+ ```
371
+
372
+ ### SensorSink API
373
+
374
+ `useSensorSink()` (or `sync.sink`) returns:
375
+
376
+ - `push(batch: SensorBatch): Promise<void>` — enqueue a pre-mapped batch.
377
+ - `pushFirmware(fw: FirmwareExport, ctx: MapperContext): Promise<void>` —
378
+ runs the firmware mapper (`mapFirmwareExport`) and fans out per table.
379
+ - `flush(): Promise<FlushResult[]>` — drain every configured table now.
380
+ - `pendingRowCount(table?: string): Promise<number>` — in-memory + overflow.
381
+ - `clear(): Promise<void>` — reset buffer + overflow (used on sign-out).
382
+
383
+ ### Config reference (sync)
384
+
385
+ | Field | Type | Notes |
386
+ |---|---|---|
387
+ | `analytics` | `AnalyticsEngine` | Core engine created by `createAnalytics(...)`. |
388
+ | `storage` | `KVStore` | Backing store for overflow chunks + watermarks. |
389
+ | `ctx` | `AttachContext` | Passed to scheduler cycle (pushAll / fetchIncremental). |
390
+ | `tables` | `readonly string[]` | Ordered list; enforces flush / push / fetch scope. |
391
+ | `columnOrder` | `Record<string, readonly string[]>` | Column order per table; must match Iceberg schema. |
392
+ | `prefetchPolicy` | `PrefetchPolicy` | `all-family-on-attach` / `recent-active-only` / `lazy`. |
393
+ | `flush.maxRows` | `number` | Row-count trigger (default 500). |
394
+ | `flush.maxAgeMs` | `number` | Age trigger (default 60 000 ms). |
395
+ | `flush.concurrency` | `number` | p-queue concurrency (default 3). |
396
+ | `overflow.maxBufferBytes` | `number` | Ring budget before spill. |
397
+ | `overflow.policy` | `'drop-oldest' \| 'drop-newest' \| 'block'` | Overflow strategy. |
398
+ | `scheduler.requiresCharging` | `boolean` | Skip cycle unless charging. |
399
+ | `scheduler.requiresWifi` | `boolean` | Skip cycle unless Wi-Fi. |
400
+ | `scheduler.taskName` | `string` | Background-task registration name. |
401
+ | `eventBus?` | `EventBus` | Optional bus for `{table}:insert` / `{table}:sync_complete` fan-out. |
402
+ | `refreshToken?` | `() => Promise<void>` | Called by pusher on `401 token_expired` before retry. |
403
+ | `backgroundTask?` | `BackgroundTaskPort` | Inject a native background port (defaults to an in-process no-op). |
404
+ | `constraints?` | `ConstraintPort` | Inject Wi-Fi + charging probes (defaults to always-allowed). |
405
+ | `logger?` | `SchedulerLogger` | Debug hook for the scheduler cycle. |
406
+
407
+ ### Prefetch policies
408
+
409
+ - `{ kind: 'all-family-on-attach', windowDays }` — pull the last `windowDays`
410
+ of every configured table for the whole family after attach.
411
+ - `{ kind: 'recent-active-only', activeDays, windowDays }` — only pull for
412
+ users active within `activeDays`, capped at `windowDays`.
413
+ - `{ kind: 'lazy' }` — no prefetch; `fetchIncremental` picks up from the
414
+ fetch watermark on the first scheduler cycle.
415
+
416
+ Call `sync.prefetch(ctx)` right after `analytics.attach(ctx)` completes.
417
+
418
+ ### Background scheduling
419
+
420
+ `SyncScheduler` registers a single task (`config.scheduler.taskName`) with
421
+ the injected `BackgroundTaskPort`. Every registered cycle:
422
+
423
+ 1. Constraint check (Wi-Fi + optional charging). Missing constraints emit
424
+ `constraint_not_met` and skip.
425
+ 2. `coordinator.flushAll()` — parallel `flusher.flush(table, 'scheduled')`.
426
+ 3. `coordinator.pushAll(tables, ctx)` — `R2Pusher.pushAll` (concurrency-safe).
427
+ 4. `coordinator.fetchIncremental(ctx)` — `R2Fetcher.fetchIncremental`.
428
+
429
+ `sync.triggerNow()` bypasses constraints (e.g., manual "sync now" button).
430
+ `sync.subscribe(fn)` fans out scheduler transitions (`idle | running | error`).
431
+
432
+ ### Mapper contract
433
+
434
+ `@mongrov/analytics/sync` exposes per-metric mappers plus a firmware-export
435
+ fan-out:
436
+
437
+ - `mapHrv`, `mapHeartRate`, `mapSpo2`, `mapTemperature`, `mapActivity`,
438
+ `mapBattery`, `mapRingConfig`, `reconstructSleepSessions` — one per raw
439
+ metric family.
440
+ - `mapFirmwareExport(fw, ctx)` — runs every applicable mapper and returns a
441
+ `FirmwareMappedBatch` keyed by table (matches `sync.sink.pushFirmware`).
442
+
443
+ Row types (`HrvRow`, `HeartRateRow`, …, `SleepSessionRow`) are re-exported
444
+ for typed sinks / adapters. `computeNightOf` + `parseTimestamp` cover the
445
+ common time helpers.
446
+
447
+ ### Firmware fixtures
448
+
449
+ Fixture exports used across mapper + firmware tests live at
450
+ `src/sync/__tests__/__fixtures__/` (e.g. `firmware-full-day.json`).
451
+ Consumers writing their own regression tests can import them via a relative
452
+ path inside this workspace; they are excluded from the published `dist/`.
453
+
454
+ ## AI tools (`@mongrov/analytics/tools`)
455
+
456
+ Six read-only AI SDK v4 tools (`getHRV`, `getSleepSummary`,
457
+ `getActivityTotal`, `compareTrend`, `detectAnomaly`, `getInsights`)
458
+ sit on top of the warehouse, wired through a
459
+ `rate → auth → execute → budget → audit` chain. See
460
+ [`src/tools/README.md`](./src/tools/README.md).
461
+
462
+ ### MCP dev server (`@mongrov/analytics/tools/mcp`)
463
+
464
+ Same six tools, exposed over Model Context Protocol via stdio
465
+ (Claude Desktop) or HTTP with bearer auth (MCP Inspector, curl).
466
+ Dev-guarded (`shouldStartMcpServer()`) + `sideEffects: false` so
467
+ prod RN bundles drop the SDK. See
468
+ [`src/tools/mcp/README.md`](./src/tools/mcp/README.md).
469
+
470
+ ## License
471
+
472
+ MIT
@@ -0,0 +1,24 @@
1
+ /**
2
+ * React context for the analytics engine.
3
+ *
4
+ * `AnalyticsProvider` wires an already-constructed `AnalyticsEngine` into the
5
+ * subtree; hooks (`useAnalytics`, `useTimeseries`, `useInsight`) resolve the
6
+ * engine via `useAnalyticsEngine()`.
7
+ *
8
+ * Kept in its own module so `hooks.ts` remains pure logic (easier to unit
9
+ * test) and the .tsx file boundary carries only JSX.
10
+ */
11
+ import * as React from 'react';
12
+ import type { AnalyticsEngine } from './types';
13
+ export interface AnalyticsProviderProps {
14
+ engine: AnalyticsEngine;
15
+ children: React.ReactNode;
16
+ }
17
+ export declare function AnalyticsProvider({ engine, children }: AnalyticsProviderProps): import("react/jsx-runtime").JSX.Element;
18
+ /**
19
+ * Internal — hooks call this to resolve the engine. Throws a clear error when
20
+ * used outside `AnalyticsProvider` so misconfigured apps fail loudly at mount
21
+ * rather than surfacing an opaque null-ref later.
22
+ */
23
+ export declare function useAnalyticsEngine(): AnalyticsEngine;
24
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAI9C,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,sBAAsB,2CAM7E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CASpD"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * React context for the analytics engine.
4
+ *
5
+ * `AnalyticsProvider` wires an already-constructed `AnalyticsEngine` into the
6
+ * subtree; hooks (`useAnalytics`, `useTimeseries`, `useInsight`) resolve the
7
+ * engine via `useAnalyticsEngine()`.
8
+ *
9
+ * Kept in its own module so `hooks.ts` remains pure logic (easier to unit
10
+ * test) and the .tsx file boundary carries only JSX.
11
+ */
12
+ import * as React from 'react';
13
+ const AnalyticsContext = React.createContext(null);
14
+ export function AnalyticsProvider({ engine, children }) {
15
+ return (_jsx(AnalyticsContext.Provider, { value: engine, children: children }));
16
+ }
17
+ /**
18
+ * Internal — hooks call this to resolve the engine. Throws a clear error when
19
+ * used outside `AnalyticsProvider` so misconfigured apps fail loudly at mount
20
+ * rather than surfacing an opaque null-ref later.
21
+ */
22
+ export function useAnalyticsEngine() {
23
+ const engine = React.useContext(AnalyticsContext);
24
+ if (!engine) {
25
+ throw new Error('@mongrov/analytics: hook used outside <AnalyticsProvider>. '
26
+ + 'Wrap your app with <AnalyticsProvider engine={createAnalytics(...)}>.');
27
+ }
28
+ return engine;
29
+ }
30
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAAyB,IAAI,CAAC,CAAA;AAO1E,MAAM,UAAU,iBAAiB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAA0B;IAC5E,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YACrC,QAAQ,GACiB,CAC7B,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAA;IACjD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,6DAA6D;cAC3D,uEAAuE,CAC1E,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Thin wrapper around a native DuckDB instance.
3
+ *
4
+ * Owns lifecycle (`open`/`close`), maps native errors into the analytics
5
+ * error taxonomy, and normalises `execute`/`stream`/`createAppender` so the
6
+ * rest of the package doesn't touch `react-native-duckdb` directly.
7
+ *
8
+ * We depend on `react-native-duckdb` only structurally (via the
9
+ * `DuckDBInstance` interface below) so the package builds and tests without
10
+ * the native module installed. The public factory (T-10) supplies a real
11
+ * instance via a `DuckDBFactory`; test suites supply fakes.
12
+ */
13
+ /**
14
+ * Minimal structural type for the underlying DuckDB connection.
15
+ *
16
+ * Anything the real `react-native-duckdb` connection offers beyond this is
17
+ * ignored — we only lean on what spec §Engine + downstream phases need.
18
+ */
19
+ export interface DuckDBInstance {
20
+ execute: (sql: string, params?: Record<string, unknown>) => Promise<unknown[]>;
21
+ stream: (sql: string, params?: Record<string, unknown>) => AsyncIterable<unknown[]>;
22
+ createAppender: (table: string) => DuckDBAppender;
23
+ close: () => Promise<void>;
24
+ }
25
+ export interface DuckDBAppender {
26
+ appendRow: (values: unknown[]) => void;
27
+ flush: () => void;
28
+ close: () => void;
29
+ }
30
+ /** Factory that yields a DuckDB instance (native or fake). */
31
+ export type DuckDBFactory = () => Promise<DuckDBInstance>;
32
+ /**
33
+ * Owns the DuckDB connection lifecycle.
34
+ *
35
+ * - `open()` is idempotent: repeated calls after a successful open are no-ops.
36
+ * - `close()` is idempotent and terminal — subsequent `execute`/`stream`/
37
+ * `createAppender` throw `not_ready`.
38
+ * - `execute()` returns a full row set; `stream()` yields pages of rows
39
+ * (recommended page size `HybridDuckDB.PAGE_SIZE`, applied by the native
40
+ * driver; the wrapper just forwards).
41
+ * - Any native throw is wrapped in `AnalyticsError`.
42
+ */
43
+ export declare class HybridDuckDB {
44
+ #private;
45
+ /** Recommended page size for streamed queries (spec §Engine). */
46
+ static readonly PAGE_SIZE = 500;
47
+ constructor(factory: DuckDBFactory);
48
+ /** Whether `open()` has completed successfully and `close()` has not been called. */
49
+ get isOpen(): boolean;
50
+ open(): Promise<void>;
51
+ execute<T = unknown>(sql: string, params?: Record<string, unknown>): Promise<T[]>;
52
+ stream<T = unknown>(sql: string, params?: Record<string, unknown>): AsyncIterable<T[]>;
53
+ createAppender(table: string): DuckDBAppender;
54
+ close(): Promise<void>;
55
+ }
56
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IAC9E,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,CAAC,OAAO,EAAE,CAAC,CAAA;IACnF,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,cAAc,CAAA;IACjD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;IACtC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAA;AAEzD;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;;IACvB,iEAAiE;IACjE,MAAM,CAAC,QAAQ,CAAC,SAAS,OAAM;gBAMnB,OAAO,EAAE,aAAa;IAIlC,qFAAqF;IACrF,IAAI,MAAM,IAAI,OAAO,CAEpB;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAerB,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAWhF,MAAM,CAAC,CAAC,GAAG,OAAO,EACvB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,aAAa,CAAC,CAAC,EAAE,CAAC;IAYrB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;IAcvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAqB7B"}