@spooky-sync/core 0.0.1-canary.240 → 0.0.1-canary.242

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 (292) hide show
  1. package/AGENTS.md +34 -15
  2. package/README.md +56 -16
  3. package/dist/index.d.ts +1136 -1846
  4. package/dist/index.js +10168 -11267
  5. package/dist/sqlite-open.js +10 -1
  6. package/dist/types.d.ts +22 -79
  7. package/package.json +8 -4
  8. package/skills/sp00ky-core/SKILL.md +18 -0
  9. package/src/boot/auth-flip.saga.test.ts +82 -0
  10. package/src/boot/auth-flip.saga.ts +62 -0
  11. package/src/boot/boot.saga.test.ts +151 -0
  12. package/src/boot/boot.saga.ts +128 -0
  13. package/src/boot/bucket-switch.saga.test.ts +158 -0
  14. package/src/boot/bucket-switch.saga.ts +115 -0
  15. package/src/boot/preload.saga.test.ts +51 -0
  16. package/src/boot/preload.saga.ts +31 -0
  17. package/src/client/bucket-handle.test.ts +117 -0
  18. package/src/client/bucket-handle.ts +227 -0
  19. package/src/client/router.test.ts +56 -0
  20. package/src/client/router.ts +94 -0
  21. package/src/client/runtime.test.ts +192 -0
  22. package/src/client/runtime.ts +296 -0
  23. package/src/client/services.ts +349 -0
  24. package/src/client/sp00ky-client.test.ts +241 -0
  25. package/src/client/sp00ky-client.ts +482 -0
  26. package/src/index.ts +1 -0
  27. package/src/kernel/constants.test.ts +19 -0
  28. package/src/kernel/constants.ts +54 -0
  29. package/src/kernel/effects.test.ts +40 -0
  30. package/src/kernel/effects.ts +159 -0
  31. package/src/kernel/events.ts +55 -0
  32. package/src/kernel/interpreter.test.ts +116 -0
  33. package/src/kernel/interpreter.ts +143 -0
  34. package/src/kernel/saga.test.ts +89 -0
  35. package/src/kernel/saga.ts +73 -0
  36. package/src/modules/app-release/index.ts +13 -5
  37. package/src/modules/devtools/index.ts +10 -3
  38. package/src/modules/devtools/state-shape.test.ts +4 -1
  39. package/src/modules/feature-flag/index.ts +13 -5
  40. package/src/mutation/jobs.test.ts +25 -0
  41. package/src/mutation/jobs.ts +41 -0
  42. package/src/{modules/data → mutation}/mutation-id.test.ts +12 -0
  43. package/src/mutation/push.saga.test.ts +285 -0
  44. package/src/mutation/push.saga.ts +215 -0
  45. package/src/mutation/rows.test.ts +201 -0
  46. package/src/mutation/rows.ts +377 -0
  47. package/src/mutation/tray.saga.test.ts +63 -0
  48. package/src/mutation/tray.saga.ts +48 -0
  49. package/src/mutation/write.saga.test.ts +163 -0
  50. package/src/mutation/write.saga.ts +166 -0
  51. package/src/query/env.test.ts +23 -0
  52. package/src/query/env.ts +56 -0
  53. package/src/query/fetch.saga.test.ts +156 -0
  54. package/src/query/fetch.saga.ts +109 -0
  55. package/src/query/hash.test.ts +14 -0
  56. package/src/query/hash.ts +18 -0
  57. package/src/{modules/data → query}/id-set-plan.test.ts +12 -2
  58. package/src/query/lifecycle.saga.test.ts +122 -0
  59. package/src/query/lifecycle.saga.ts +101 -0
  60. package/src/query/materialize.saga.test.ts +82 -0
  61. package/src/query/materialize.saga.ts +75 -0
  62. package/src/query/materialize.test.ts +50 -0
  63. package/src/query/materialize.ts +44 -0
  64. package/src/query/membership.saga.test.ts +246 -0
  65. package/src/query/membership.saga.ts +208 -0
  66. package/src/query/membership.test.ts +118 -0
  67. package/src/query/membership.ts +158 -0
  68. package/src/query/register.saga.test.ts +251 -0
  69. package/src/query/register.saga.ts +190 -0
  70. package/src/query/render-set.test.ts +36 -0
  71. package/src/query/render-set.ts +60 -0
  72. package/src/query/sql.test.ts +62 -0
  73. package/src/query/sql.ts +79 -0
  74. package/src/{modules/data → query}/window-query.test.ts +18 -0
  75. package/src/{modules/data → query}/window-query.ts +2 -1
  76. package/src/services/database/connection-supervisor.test.ts +29 -0
  77. package/src/services/database/connection-supervisor.ts +7 -0
  78. package/src/services/database/database.responses.test.ts +43 -0
  79. package/src/services/database/database.ts +25 -10
  80. package/src/services/database/local-migrator.ts +2 -2
  81. package/src/services/database/plan-render.test.ts +1 -1
  82. package/src/services/database/sqlite-cache-engine.test.ts +4 -3
  83. package/src/services/database/sqlite-cache-engine.ts +2 -11
  84. package/src/services/database/sqlite-devtools-queries.integration.test.ts +1 -1
  85. package/src/services/database/sqlite-plan-sql.ts +16 -1
  86. package/src/services/database/sqlite-projection.test.ts +25 -3
  87. package/src/services/tabs/protocol.ts +7 -3
  88. package/src/sp00ky.ts +5 -1854
  89. package/src/state/client-state.ts +184 -0
  90. package/src/state/lifecycle.test.ts +118 -0
  91. package/src/state/lifecycle.ts +86 -0
  92. package/src/state/reducers.test.ts +280 -0
  93. package/src/state/reducers.ts +476 -0
  94. package/src/state/selectors.test.ts +149 -0
  95. package/src/state/selectors.ts +184 -0
  96. package/src/sync/connection.saga.test.ts +86 -0
  97. package/src/sync/connection.saga.ts +85 -0
  98. package/src/sync/live.saga.test.ts +69 -0
  99. package/src/sync/live.saga.ts +49 -0
  100. package/src/sync/policy.health.test.ts +56 -0
  101. package/src/{modules/sync/utils.test.ts → sync/policy.test.ts} +31 -3
  102. package/src/{modules/sync/utils.ts → sync/policy.ts} +67 -5
  103. package/src/sync/poll.saga.test.ts +51 -0
  104. package/src/sync/poll.saga.ts +36 -0
  105. package/src/sync/tabs.saga.test.ts +74 -0
  106. package/src/sync/tabs.saga.ts +92 -0
  107. package/src/testing/adapters.ts +133 -0
  108. package/src/testing/build.ts +61 -0
  109. package/src/testing/fake-services.ts +34 -0
  110. package/src/testing/run-pure.test.ts +92 -0
  111. package/src/testing/run-pure.ts +128 -0
  112. package/src/testing/services.ts +17 -0
  113. package/src/types.ts +8 -7
  114. package/src/utils/utils.test.ts +2 -1
  115. package/vitest.config.ts +48 -0
  116. package/coverage/lcov-report/base.css +0 -224
  117. package/coverage/lcov-report/block-navigation.js +0 -87
  118. package/coverage/lcov-report/favicon.png +0 -0
  119. package/coverage/lcov-report/index.html +0 -476
  120. package/coverage/lcov-report/prettify.css +0 -1
  121. package/coverage/lcov-report/prettify.js +0 -2
  122. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  123. package/coverage/lcov-report/sorter.js +0 -210
  124. package/coverage/lcov-report/src/boot/auth-flip.saga.ts.html +0 -271
  125. package/coverage/lcov-report/src/boot/boot.saga.ts.html +0 -469
  126. package/coverage/lcov-report/src/boot/bucket-switch.saga.ts.html +0 -430
  127. package/coverage/lcov-report/src/boot/index.html +0 -161
  128. package/coverage/lcov-report/src/boot/preload.saga.ts.html +0 -178
  129. package/coverage/lcov-report/src/client/bucket-handle.ts.html +0 -766
  130. package/coverage/lcov-report/src/client/index.html +0 -161
  131. package/coverage/lcov-report/src/client/router.ts.html +0 -367
  132. package/coverage/lcov-report/src/client/runtime.ts.html +0 -940
  133. package/coverage/lcov-report/src/client/sp00ky-client.ts.html +0 -1531
  134. package/coverage/lcov-report/src/events/index.html +0 -116
  135. package/coverage/lcov-report/src/events/index.ts.html +0 -877
  136. package/coverage/lcov-report/src/index.html +0 -146
  137. package/coverage/lcov-report/src/index.ts.html +0 -202
  138. package/coverage/lcov-report/src/kernel/constants.ts.html +0 -247
  139. package/coverage/lcov-report/src/kernel/effects.ts.html +0 -562
  140. package/coverage/lcov-report/src/kernel/events.ts.html +0 -250
  141. package/coverage/lcov-report/src/kernel/index.html +0 -176
  142. package/coverage/lcov-report/src/kernel/interpreter.ts.html +0 -514
  143. package/coverage/lcov-report/src/kernel/saga.ts.html +0 -304
  144. package/coverage/lcov-report/src/modules/app-release/index.html +0 -116
  145. package/coverage/lcov-report/src/modules/app-release/index.ts.html +0 -712
  146. package/coverage/lcov-report/src/modules/auth/events/index.html +0 -116
  147. package/coverage/lcov-report/src/modules/auth/events/index.ts.html +0 -142
  148. package/coverage/lcov-report/src/modules/auth/index.html +0 -116
  149. package/coverage/lcov-report/src/modules/auth/index.ts.html +0 -1225
  150. package/coverage/lcov-report/src/modules/crdt/crdt-field.ts.html +0 -967
  151. package/coverage/lcov-report/src/modules/crdt/index.html +0 -146
  152. package/coverage/lcov-report/src/modules/crdt/index.ts.html +0 -1474
  153. package/coverage/lcov-report/src/modules/crdt/loro-loader.ts.html +0 -160
  154. package/coverage/lcov-report/src/modules/devtools/flags.ts.html +0 -1132
  155. package/coverage/lcov-report/src/modules/devtools/index.html +0 -161
  156. package/coverage/lcov-report/src/modules/devtools/index.ts.html +0 -2413
  157. package/coverage/lcov-report/src/modules/devtools/storage-info.ts.html +0 -589
  158. package/coverage/lcov-report/src/modules/devtools/versions.ts.html +0 -415
  159. package/coverage/lcov-report/src/modules/feature-flag/index.html +0 -116
  160. package/coverage/lcov-report/src/modules/feature-flag/index.ts.html +0 -1033
  161. package/coverage/lcov-report/src/modules/index.html +0 -116
  162. package/coverage/lcov-report/src/modules/ref-tables.ts.html +0 -349
  163. package/coverage/lcov-report/src/mutation/index.html +0 -191
  164. package/coverage/lcov-report/src/mutation/jobs.ts.html +0 -208
  165. package/coverage/lcov-report/src/mutation/mutation-id.ts.html +0 -190
  166. package/coverage/lcov-report/src/mutation/push.saga.ts.html +0 -730
  167. package/coverage/lcov-report/src/mutation/rows.ts.html +0 -1216
  168. package/coverage/lcov-report/src/mutation/tray.saga.ts.html +0 -229
  169. package/coverage/lcov-report/src/mutation/write.saga.ts.html +0 -583
  170. package/coverage/lcov-report/src/otel/index.html +0 -116
  171. package/coverage/lcov-report/src/otel/index.ts.html +0 -466
  172. package/coverage/lcov-report/src/query/env.ts.html +0 -253
  173. package/coverage/lcov-report/src/query/fetch.saga.ts.html +0 -412
  174. package/coverage/lcov-report/src/query/hash.ts.html +0 -139
  175. package/coverage/lcov-report/src/query/index.html +0 -281
  176. package/coverage/lcov-report/src/query/lifecycle.saga.ts.html +0 -388
  177. package/coverage/lcov-report/src/query/materialize.saga.ts.html +0 -310
  178. package/coverage/lcov-report/src/query/materialize.ts.html +0 -217
  179. package/coverage/lcov-report/src/query/membership.saga.ts.html +0 -709
  180. package/coverage/lcov-report/src/query/membership.ts.html +0 -559
  181. package/coverage/lcov-report/src/query/register.saga.ts.html +0 -655
  182. package/coverage/lcov-report/src/query/render-set.ts.html +0 -265
  183. package/coverage/lcov-report/src/query/sql.ts.html +0 -322
  184. package/coverage/lcov-report/src/query/window-query.ts.html +0 -670
  185. package/coverage/lcov-report/src/services/blobs/blob-cache.ts.html +0 -1894
  186. package/coverage/lcov-report/src/services/blobs/blob-manifest.ts.html +0 -766
  187. package/coverage/lcov-report/src/services/blobs/blob-store.ts.html +0 -1162
  188. package/coverage/lcov-report/src/services/blobs/index.html +0 -161
  189. package/coverage/lcov-report/src/services/blobs/index.ts.html +0 -295
  190. package/coverage/lcov-report/src/services/database/cache-engine.ts.html +0 -664
  191. package/coverage/lcov-report/src/services/database/connection-supervisor.ts.html +0 -1330
  192. package/coverage/lcov-report/src/services/database/database.ts.html +0 -658
  193. package/coverage/lcov-report/src/services/database/engine-factory.ts.html +0 -184
  194. package/coverage/lcov-report/src/services/database/errors.ts.html +0 -187
  195. package/coverage/lcov-report/src/services/database/events/index.html +0 -116
  196. package/coverage/lcov-report/src/services/database/events/index.ts.html +0 -184
  197. package/coverage/lcov-report/src/services/database/index.html +0 -386
  198. package/coverage/lcov-report/src/services/database/index.ts.html +0 -121
  199. package/coverage/lcov-report/src/services/database/local-migrator.ts.html +0 -703
  200. package/coverage/lcov-report/src/services/database/local.ts.html +0 -1633
  201. package/coverage/lcov-report/src/services/database/plan-render.ts.html +0 -409
  202. package/coverage/lcov-report/src/services/database/relation-resolver.ts.html +0 -682
  203. package/coverage/lcov-report/src/services/database/remote.ts.html +0 -1036
  204. package/coverage/lcov-report/src/services/database/sqlite-cache-engine.ts.html +0 -4132
  205. package/coverage/lcov-report/src/services/database/sqlite-lock-verify.ts.html +0 -220
  206. package/coverage/lcov-report/src/services/database/sqlite-open.ts.html +0 -577
  207. package/coverage/lcov-report/src/services/database/sqlite-plan-sql.ts.html +0 -499
  208. package/coverage/lcov-report/src/services/database/sqlite-select.ts.html +0 -478
  209. package/coverage/lcov-report/src/services/database/sqlite-transport.ts.html +0 -757
  210. package/coverage/lcov-report/src/services/database/surql-translate.ts.html +0 -1333
  211. package/coverage/lcov-report/src/services/database/surreal-cache-engine.ts.html +0 -568
  212. package/coverage/lcov-report/src/services/logger/index.html +0 -116
  213. package/coverage/lcov-report/src/services/logger/index.ts.html +0 -154
  214. package/coverage/lcov-report/src/services/persistence/index.html +0 -146
  215. package/coverage/lcov-report/src/services/persistence/localstorage.ts.html +0 -163
  216. package/coverage/lcov-report/src/services/persistence/resilient.ts.html +0 -208
  217. package/coverage/lcov-report/src/services/persistence/surrealdb.ts.html +0 -271
  218. package/coverage/lcov-report/src/services/stream-processor/index.html +0 -146
  219. package/coverage/lcov-report/src/services/stream-processor/index.ts.html +0 -3313
  220. package/coverage/lcov-report/src/services/stream-processor/permissions.ts.html +0 -244
  221. package/coverage/lcov-report/src/services/stream-processor/wasm-types.ts.html +0 -346
  222. package/coverage/lcov-report/src/services/tabs/broker-client.ts.html +0 -934
  223. package/coverage/lcov-report/src/services/tabs/coordinator.ts.html +0 -1984
  224. package/coverage/lcov-report/src/services/tabs/index.html +0 -191
  225. package/coverage/lcov-report/src/services/tabs/leader-locks.ts.html +0 -310
  226. package/coverage/lcov-report/src/services/tabs/protocol.ts.html +0 -871
  227. package/coverage/lcov-report/src/services/tabs/support.ts.html +0 -193
  228. package/coverage/lcov-report/src/services/tabs/tabs-broker-worker.ts.html +0 -2005
  229. package/coverage/lcov-report/src/sp00ky.ts.html +0 -100
  230. package/coverage/lcov-report/src/state/client-state.ts.html +0 -637
  231. package/coverage/lcov-report/src/state/index.html +0 -161
  232. package/coverage/lcov-report/src/state/lifecycle.ts.html +0 -343
  233. package/coverage/lcov-report/src/state/reducers.ts.html +0 -1471
  234. package/coverage/lcov-report/src/state/selectors.ts.html +0 -637
  235. package/coverage/lcov-report/src/sync/connection.saga.ts.html +0 -343
  236. package/coverage/lcov-report/src/sync/index.html +0 -176
  237. package/coverage/lcov-report/src/sync/live.saga.ts.html +0 -232
  238. package/coverage/lcov-report/src/sync/policy.ts.html +0 -1606
  239. package/coverage/lcov-report/src/sync/poll.saga.ts.html +0 -193
  240. package/coverage/lcov-report/src/sync/tabs.saga.ts.html +0 -361
  241. package/coverage/lcov-report/src/types.ts.html +0 -2419
  242. package/coverage/lcov-report/src/utils/blurhash.ts.html +0 -355
  243. package/coverage/lcov-report/src/utils/error-classification.ts.html +0 -244
  244. package/coverage/lcov-report/src/utils/index.html +0 -191
  245. package/coverage/lcov-report/src/utils/index.ts.html +0 -820
  246. package/coverage/lcov-report/src/utils/parser.ts.html +0 -313
  247. package/coverage/lcov-report/src/utils/semver.ts.html +0 -175
  248. package/coverage/lcov-report/src/utils/surql.ts.html +0 -667
  249. package/coverage/lcov.info +0 -19079
  250. package/src/modules/cache/cache.relay.test.ts +0 -95
  251. package/src/modules/cache/index.ts +0 -361
  252. package/src/modules/cache/types.ts +0 -19
  253. package/src/modules/data/data.activity.test.ts +0 -109
  254. package/src/modules/data/data.authority.test.ts +0 -122
  255. package/src/modules/data/data.heartbeat-wake.test.ts +0 -115
  256. package/src/modules/data/data.hydration.test.ts +0 -142
  257. package/src/modules/data/data.membership.test.ts +0 -623
  258. package/src/modules/data/data.notify-table.test.ts +0 -41
  259. package/src/modules/data/data.pending-ids.test.ts +0 -199
  260. package/src/modules/data/data.rebind.test.ts +0 -170
  261. package/src/modules/data/data.rematerialize.test.ts +0 -114
  262. package/src/modules/data/data.run.test.ts +0 -113
  263. package/src/modules/data/data.settled-writes.test.ts +0 -235
  264. package/src/modules/data/data.status.test.ts +0 -249
  265. package/src/modules/data/data.test.ts +0 -58
  266. package/src/modules/data/index.ts +0 -2745
  267. package/src/modules/sync/engine.ts +0 -250
  268. package/src/modules/sync/events/index.ts +0 -84
  269. package/src/modules/sync/index.ts +0 -3
  270. package/src/modules/sync/queue/index.ts +0 -2
  271. package/src/modules/sync/queue/queue-down.test.ts +0 -228
  272. package/src/modules/sync/queue/queue-down.ts +0 -184
  273. package/src/modules/sync/queue/queue-up.forwarded.test.ts +0 -164
  274. package/src/modules/sync/queue/queue-up.ts +0 -407
  275. package/src/modules/sync/scheduler.pause.test.ts +0 -109
  276. package/src/modules/sync/scheduler.retry.test.ts +0 -237
  277. package/src/modules/sync/scheduler.ts +0 -286
  278. package/src/modules/sync/sync.cleanup.test.ts +0 -116
  279. package/src/modules/sync/sync.health.test.ts +0 -167
  280. package/src/modules/sync/sync.heartbeat.test.ts +0 -80
  281. package/src/modules/sync/sync.list-ref-meta.test.ts +0 -193
  282. package/src/modules/sync/sync.live-removal.test.ts +0 -289
  283. package/src/modules/sync/sync.reconnect.test.ts +0 -145
  284. package/src/modules/sync/sync.rematerialize-after-fetch.test.ts +0 -93
  285. package/src/modules/sync/sync.settled-poll.test.ts +0 -58
  286. package/src/modules/sync/sync.subquery.test.ts +0 -82
  287. package/src/modules/sync/sync.tabs.test.ts +0 -249
  288. package/src/modules/sync/sync.ts +0 -2626
  289. package/src/sp00ky.auth-order.test.ts +0 -112
  290. package/src/sp00ky.init-query.test.ts +0 -183
  291. package/src/sp00ky.local-first.test.ts +0 -60
  292. /package/src/{modules/data → mutation}/mutation-id.ts +0 -0
package/AGENTS.md CHANGED
@@ -11,24 +11,35 @@ The sync engine. A `Sp00kyClient<S>` owns a local database (memory or IndexedDB)
11
11
  │ spky generate
12
12
  ▼
13
13
  schema.gen.ts (typed schema + SURQL_SCHEMA constant)
14
- │ passed to SyncedDbConfig
14
+ │ passed to Sp00kyConfig
15
15
  ▼
16
- Sp00kyClient<S> ── local store (memory | IndexedDB)
17
- │ ↑↓ DBSP reactive query layer
18
- │ ↑↓ mutation queue
19
- └── SSP ── remote SurrealDB
16
+ Sp00kyClient<S> ── facade: every method = one saga run, selector or subscription
17
+ │
18
+ ├── Runtime (client/runtime.ts): one immutable ClientState, lanes, timers,
19
+ │ subscriptions, dirty -> materialize scheduling. `query:status`,
20
+ │ `activity:changed` and `health:changed` are all notified from here,
21
+ │ off state transitions - never emitted by a saga, so a change made
22
+ │ outside a sync round (a socket drop, say) still reaches subscribers
23
+ ├── Sagas (query/ mutation/ sync/ boot/): pure generators yielding effects
24
+ └── Interpreter (kernel/interpreter.ts) -> adapters (services/):
25
+ local store · remote socket · SSP wasm circuit · tabs broker · blobs
20
26
  ```
21
27
 
22
- Local mutations are applied optimistically, ingested into a DBSP layer that drives reactive query updates, then drained to the remote via SSP. Live updates flow back the same path.
28
+ Local writes are applied optimistically (row + outbox entry in one local
29
+ transaction), rendered through the outbox overlay, and drained to the remote in
30
+ batches. Server membership arrives through one batched edge read (registration,
31
+ poll, LIVE dirt) and bodies are fetched once across all queries. Rows are always
32
+ re-materialized from state, never patched in place.
23
33
 
24
34
  ## Key exports (`src/index.ts`)
25
35
 
26
- - `Sp00kyClient<S>` — main class. Methods: `init()`, `create(id, payload)`, `update(table, id, payload, options?)`, `delete(table, idOrSelector)`, `query(table, opts?)`, `run(backend, route, payload)`, `bucket(name)`, `remoteQuery(sql, vars?)`, `useRemote(fn)`, `authenticate(token)`, `signOut()`. Plus `pendingMutationCount` / `subscribeToPendingMutations(cb)` (outbox depth) and `fetchingQueryCount` / `subscribeToFetchActivity(cb)` (queries mid-fetch, aggregate) for traffic indicators.
36
+ - `Sp00kyClient<S>` — main class. Methods: `init()`, `create(id, payload)`, `update(table, id, payload, options?)`, `delete(table, id)`, `query(table, opts?)`, `preload(query, { signal? })`, `run(backend, route, payload)`, `bucket(name)`, `remoteQuery(sql, vars?)`, `useRemote(fn)`, `authenticate(token)`, `auth.signOut()`. Traffic indicators: `pendingMutationCount` / `subscribeToPendingMutations(cb)` (outbox depth), `fetchingQueryCount` / `subscribeToFetchActivity(cb)`. Failed-writes tray: `failedMutationCount`, `subscribeToFailedMutations(cb)`, `listFailedMutations()`, `retryFailedMutation(id)`, `discardFailedMutation(id)`.
37
+ - `FailedMutation` — a row of the `_00_failed_mutations` tray (`mutationType`, `recordId`, `data`, `beforeRecord`, `error`, `revert`).
27
38
  - `BucketHandle` — file storage handle (`put`, `get`, `delete`, `exists`), plus the cache-aware `read`, `url`, `pin`/`unpin`, `evict`, `prefetch`. `get` is always remote; `read`/`url` go through the blob cache.
28
39
  - `services/blobs/` — durable cache for bucket bytes: OPFS holds the files, `_00_blob` holds a manifest that reconcile rebuilds from disk (so a wiped local store costs metadata, not the offline cache). Nothing expires on a timer; eviction is LRU under a byte budget only, skipping pinned and on-screen entries. Config: `blobCache` in `types.ts`.
29
40
  - `AuthService` — token management, sign-in/sign-out events.
30
41
  - `CrdtManager`, `CrdtField`, `cursorColorFromName`, `CURSOR_COLORS` — Loro-CRDT integration.
31
- - Types: `Sp00kyConfig`, `SyncedDbConfig` (re-exported by client-solid as the consumer-facing shape), `QueryTimeToLive`, `PersistenceClient`, `StoreType`, `UpdateOptions`, `RunOptions`.
42
+ - Types: `Sp00kyConfig` (client-solid wraps it as `SyncedDbConfig`), `QueryTimeToLive`, `PersistenceClient`, `StoreType`, `UpdateOptions`, `RunOptions`, `PreloadOptions`.
32
43
  - Subpath: `@spooky-sync/core/otel` — `createOtelTransmit(endpoint)` for piping pino logs to OpenTelemetry.
33
44
 
34
45
  ## Common gotchas
@@ -40,15 +51,23 @@ Local mutations are applied optimistically, ingested into a DBSP layer that driv
40
51
  - **`db.remoteQuery(sql, vars?)` is the one-shot remote read.** It runs one SurrealQL statement through the client's remote path (connect gate, per-statement timeout, concurrency limit) and returns the SurrealDB result array; results are not synced into the local cache. Use it for counts and other reads the query builder can't express.
41
52
  - **`db.useRemote(fn)` hands out the bare SDK client.** Same cache bypass, but it skips the connect gate (a call during a reconnect fails with "Specify a namespace to use"). Reserve it for SDK features `remoteQuery` cannot reach (transactions, `live`).
42
53
  - **`@parent` columns are auto-populated.** Defined in the `.surql` schema as `record<user>` with the `-- @parent` annotation; never write them yourself — they're set from the auth context server-side.
54
+ - **`db.preload` is a registered query.** Resolved before on this device (a `_00_view` row exists) it returns at once; never resolved it waits until the server's membership and every body are local. It holds no subscriber, so it is evicted a ttl after registration unless a view mounts the same query. `refresh`/`staleTime` are ignored; pass `signal` to abort a cold wait.
55
+ - **A rejected write is not lost.** Application errors roll the local row back (create/update/delete) and move the mutation to `_00_failed_mutations`; the tray API above lists, retries (as a fresh write) or discards it. Network errors keep the row queued.
43
56
 
44
- ## Module map (under `src/modules/`)
57
+ ## Module map (under `src/`)
45
58
 
46
- - `data/` — local store + DBSP reactive query layer.
47
- - `sync/` — SSP client, mutation queue, live-update ingestion.
48
- - `cache/` — query result cache with TTL.
49
- - `crdt/` — Loro CRDT manager for collaborative text fields.
50
- - `auth/` — token storage, sign-in flow.
51
- - `devtools/` — devtools bridge (talks to the browser extension and `@spooky-sync/devtools-mcp`).
59
+ - `kernel/` — `Effect` union, saga driver + lanes, constants, interpreter.
60
+ - `state/` — `ClientState`, lifecycle machine, reducers (they mark queries dirty), selectors (`overlay`, `needed`, `planFetch`, `settled`, `evictable`, `phaseTimings`).
61
+ - `query/` — keys, membership rules, render set, SQL, and the sagas: `register`, `membership`, `fetch`, `materialize`, `lifecycle`.
62
+ - `mutation/` — outbox rows (v2: `tableName`, `beforeRecord`, `createdAt`), `write`, `push` (drain + rollback), `tray`, jobs.
63
+ - `sync/` — `poll`, `live`, `connection` (health, self-heal), `tabs` relay, pure policy.
64
+ - `boot/` — `boot`, `auth-flip`, `bucket-switch`, `preload`.
65
+ - `client/` — `runtime`, `router` (event -> saga + lane), `services` (adapters), `sp00ky-client` (facade), `bucket-handle`.
66
+ - `testing/` — `runPure` (drive a saga with canned effect results), fake adapters/services, state builders.
67
+ - `modules/` — auth, crdt, feature-flag, app-release, devtools, ref-tables (unchanged adapters).
68
+ - `services/` — local store engines, remote socket + supervisor, SSP wasm wrapper, tabs broker, blobs, persistence, logger.
69
+
70
+ Rules: nothing under `kernel/state/query/mutation/sync/boot` imports `services/` or touches `Date`, `setTimeout`, `crypto`; those directories are gated at 100% coverage. Legacy services are reached through the typed `service` effect (`kernel/effects.ts`, `ServiceCalls`).
52
71
 
53
72
  ## Pointers
54
73
 
package/README.md CHANGED
@@ -1,21 +1,61 @@
1
- ## Register Query Flow
1
+ # @spooky-sync/core
2
2
 
3
- 1. Query registering (local) - Data Module
4
- 2. Query get local data - Data Module
5
- 3. (Query registing (remote) - Sync Module)
3
+ The sync engine behind `Sp00kyClient`. Everything that decides something is a
4
+ pure function or a generator saga that yields effect descriptions; the only
5
+ effectful code is the adapters under `services/` and the interpreter in
6
+ `kernel/interpreter.ts`. State is one immutable `ClientState` value.
6
7
 
7
- ## Query Local Updates
8
+ ## Layout
8
9
 
9
- 1. Local Mutation is executed - Data Module
10
- 2. Mutation is enqueued for remote - Sync Moduke
11
- 3. New Record is ingested to DBSP - Data Module
12
- 4. Related Queries are updated - Data Module
13
- 5. (Sync Module syncs the mutation to remote - Sync Module)
10
+ | Directory | Holds |
11
+ | --- | --- |
12
+ | `kernel/` | `Effect` union, the saga driver, lanes, constants, the interpreter |
13
+ | `state/` | `ClientState`, the query lifecycle machine, reducers, selectors |
14
+ | `query/` | keys, membership rules, render set, SQL builders, the query sagas (register, membership, fetch, materialize, lifecycle) |
15
+ | `mutation/` | outbox rows, write / push / rollback / tray sagas |
16
+ | `sync/` | poll, LIVE, connection health, shared-tabs relay sagas |
17
+ | `boot/` | boot, auth flip, bucket switch, preload sagas |
18
+ | `client/` | runtime (lanes, timers, subscriptions), router, adapters, the public facade |
19
+ | `services/` | adapters: local store engines, remote socket, SSP wasm, tabs broker, blobs |
14
20
 
15
- ## Query Live Updates
21
+ ## Query lifecycle
16
22
 
17
- 1. Live Update is received - Sync Module
18
- 2. Version is compared with local version - Sync Module
19
- 3. If remote version is higher, the record is updated - Sync Module
20
- 4. Record is ingested to DBSP Module - Data Module
21
- 5. Related Queries are updated - Data Module
23
+ A query is `cold` (never resolved on this device: rows come from a local
24
+ predicate scan, bindings show a loader), `cached` (a durable `_00_view` row
25
+ seeded its membership: rows paint from the store at once), `live` (a server
26
+ membership set was accepted this session) or `view-lost` (the server dropped
27
+ the view; rows are kept while it re-registers).
28
+
29
+ 1. `registerLocal` computes the keys, reads `_00_view`, builds the local SSP
30
+ view and publishes the entry. The runtime materializes it on the next tick.
31
+ 2. `registerRemote` registers on the server and reads edges + meta in one
32
+ request; `applyMembership` decides (non-empty always; empty only when the
33
+ server vouches for it) and writes `_00_view`.
34
+ 3. `fetchRows` pulls every body any query is missing, once, in parallel
35
+ chunks, and records versions. `settled` is a selector over that state.
36
+ 4. Later changes arrive as LIVE edge events (which only mark membership dirty)
37
+ or the poll; both go through the same batched re-read.
38
+
39
+ Rendered rows are always `(membership ∪ (writes ∩ localView)) − deletes`
40
+ where `writes`/`deletes` come from the outbox mirror in state, so unsynced
41
+ local mutations show and a rejected one disappears the moment it is rolled
42
+ back.
43
+
44
+ ## Mutations
45
+
46
+ `write` runs one local transaction (row + `_00_pending_mutations` entry with
47
+ `beforeRecord`), feeds the circuit and starts `drain`. The drain pushes up to
48
+ 50 statements per request and judges each on its own: accepted rows are
49
+ acked (kept in the overlay until membership names them), rejected rows are
50
+ rolled back and moved to `_00_failed_mutations` in the same transaction that
51
+ deletes the pending row, a transport cut leaves the tail queued behind a
52
+ backoff. The tray is exposed as `failedMutationCount`,
53
+ `subscribeToFailedMutations`, `listFailedMutations`, `retryFailedMutation`
54
+ and `discardFailedMutation`.
55
+
56
+ ## Testing
57
+
58
+ Sagas run against canned effect results with `src/testing/run-pure.ts`; the
59
+ interpreter and facade run against `src/testing/adapters.ts`. `pnpm test:run`
60
+ runs the suite, `pnpm test:coverage` enforces the thresholds in
61
+ `vitest.config.ts` (the saga core is held at 100%).