tinybase 7.3.3 → 7.3.4

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 (244) hide show
  1. package/checkpoints/index.js +12 -11
  2. package/checkpoints/with-schemas/index.js +12 -11
  3. package/common/index.js +3 -2
  4. package/common/with-schemas/index.js +3 -2
  5. package/index.js +92 -64
  6. package/indexes/index.js +13 -12
  7. package/indexes/with-schemas/index.js +13 -12
  8. package/mergeable-store/index.js +92 -64
  9. package/mergeable-store/with-schemas/index.js +92 -64
  10. package/metrics/index.js +13 -12
  11. package/metrics/with-schemas/index.js +13 -12
  12. package/min/checkpoints/index.js +1 -1
  13. package/min/checkpoints/index.js.gz +0 -0
  14. package/min/checkpoints/with-schemas/index.js +1 -1
  15. package/min/checkpoints/with-schemas/index.js.gz +0 -0
  16. package/min/common/index.js +1 -1
  17. package/min/common/index.js.gz +0 -0
  18. package/min/common/with-schemas/index.js +1 -1
  19. package/min/common/with-schemas/index.js.gz +0 -0
  20. package/min/index.js +1 -1
  21. package/min/index.js.gz +0 -0
  22. package/min/indexes/index.js +1 -1
  23. package/min/indexes/index.js.gz +0 -0
  24. package/min/indexes/with-schemas/index.js +1 -1
  25. package/min/indexes/with-schemas/index.js.gz +0 -0
  26. package/min/mergeable-store/index.js +1 -1
  27. package/min/mergeable-store/index.js.gz +0 -0
  28. package/min/mergeable-store/with-schemas/index.js +1 -1
  29. package/min/mergeable-store/with-schemas/index.js.gz +0 -0
  30. package/min/metrics/index.js +1 -1
  31. package/min/metrics/index.js.gz +0 -0
  32. package/min/metrics/with-schemas/index.js +1 -1
  33. package/min/metrics/with-schemas/index.js.gz +0 -0
  34. package/min/omni/index.js +1 -1
  35. package/min/omni/index.js.gz +0 -0
  36. package/min/omni/with-schemas/index.js +1 -1
  37. package/min/omni/with-schemas/index.js.gz +0 -0
  38. package/min/persisters/index.js +1 -1
  39. package/min/persisters/index.js.gz +0 -0
  40. package/min/persisters/persister-automerge/index.js +1 -1
  41. package/min/persisters/persister-automerge/index.js.gz +0 -0
  42. package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
  43. package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
  44. package/min/persisters/persister-browser/index.js +1 -1
  45. package/min/persisters/persister-browser/index.js.gz +0 -0
  46. package/min/persisters/persister-browser/with-schemas/index.js +1 -1
  47. package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
  48. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  49. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  50. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  51. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  52. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  53. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  54. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  55. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  56. package/min/persisters/persister-durable-object-storage/index.js +1 -1
  57. package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
  58. package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
  59. package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
  60. package/min/persisters/persister-electric-sql/index.js +1 -1
  61. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  62. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  63. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  64. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  65. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  66. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  67. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  68. package/min/persisters/persister-file/index.js +1 -1
  69. package/min/persisters/persister-file/index.js.gz +0 -0
  70. package/min/persisters/persister-file/with-schemas/index.js +1 -1
  71. package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
  72. package/min/persisters/persister-indexed-db/index.js +1 -1
  73. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  74. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  75. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  76. package/min/persisters/persister-libsql/index.js +1 -1
  77. package/min/persisters/persister-libsql/index.js.gz +0 -0
  78. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  79. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  80. package/min/persisters/persister-partykit-client/index.js +1 -1
  81. package/min/persisters/persister-partykit-client/index.js.gz +0 -0
  82. package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
  83. package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
  84. package/min/persisters/persister-pglite/index.js +1 -1
  85. package/min/persisters/persister-pglite/index.js.gz +0 -0
  86. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  87. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  88. package/min/persisters/persister-postgres/index.js +1 -1
  89. package/min/persisters/persister-postgres/index.js.gz +0 -0
  90. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  91. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  92. package/min/persisters/persister-powersync/index.js +1 -1
  93. package/min/persisters/persister-powersync/index.js.gz +0 -0
  94. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  95. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  96. package/min/persisters/persister-react-native-mmkv/index.js +1 -1
  97. package/min/persisters/persister-react-native-mmkv/index.js.gz +0 -0
  98. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js +1 -1
  99. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js.gz +0 -0
  100. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  101. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  102. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  103. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  104. package/min/persisters/persister-remote/index.js +1 -1
  105. package/min/persisters/persister-remote/index.js.gz +0 -0
  106. package/min/persisters/persister-remote/with-schemas/index.js +1 -1
  107. package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
  108. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  109. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  110. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  111. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  112. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  113. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  114. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  115. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  116. package/min/persisters/persister-sqlite3/index.js +1 -1
  117. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  118. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  119. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  120. package/min/persisters/persister-yjs/index.js +1 -1
  121. package/min/persisters/persister-yjs/index.js.gz +0 -0
  122. package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
  123. package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
  124. package/min/persisters/with-schemas/index.js +1 -1
  125. package/min/persisters/with-schemas/index.js.gz +0 -0
  126. package/min/queries/index.js +1 -1
  127. package/min/queries/index.js.gz +0 -0
  128. package/min/queries/with-schemas/index.js +1 -1
  129. package/min/queries/with-schemas/index.js.gz +0 -0
  130. package/min/relationships/index.js +1 -1
  131. package/min/relationships/index.js.gz +0 -0
  132. package/min/relationships/with-schemas/index.js +1 -1
  133. package/min/relationships/with-schemas/index.js.gz +0 -0
  134. package/min/store/index.js +1 -1
  135. package/min/store/index.js.gz +0 -0
  136. package/min/store/with-schemas/index.js +1 -1
  137. package/min/store/with-schemas/index.js.gz +0 -0
  138. package/min/synchronizers/index.js +1 -1
  139. package/min/synchronizers/index.js.gz +0 -0
  140. package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
  141. package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
  142. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
  143. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
  144. package/min/synchronizers/synchronizer-local/index.js +1 -1
  145. package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
  146. package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
  147. package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
  148. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  149. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  150. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  151. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  152. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  153. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  154. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  155. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  156. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  157. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  158. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  159. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  160. package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
  161. package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
  162. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
  163. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
  164. package/min/synchronizers/with-schemas/index.js +1 -1
  165. package/min/synchronizers/with-schemas/index.js.gz +0 -0
  166. package/min/ui-react-inspector/index.js +1 -1
  167. package/min/ui-react-inspector/index.js.gz +0 -0
  168. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  169. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  170. package/min/with-schemas/index.js +1 -1
  171. package/min/with-schemas/index.js.gz +0 -0
  172. package/omni/index.js +99 -64
  173. package/omni/with-schemas/index.js +99 -64
  174. package/package.json +3 -3
  175. package/persisters/index.js +19 -11
  176. package/persisters/persister-automerge/index.js +17 -9
  177. package/persisters/persister-automerge/with-schemas/index.js +17 -9
  178. package/persisters/persister-browser/index.js +17 -9
  179. package/persisters/persister-browser/with-schemas/index.js +17 -9
  180. package/persisters/persister-cr-sqlite-wasm/index.js +19 -11
  181. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +19 -11
  182. package/persisters/persister-durable-object-sql-storage/index.js +19 -11
  183. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +19 -11
  184. package/persisters/persister-durable-object-storage/index.js +19 -11
  185. package/persisters/persister-durable-object-storage/with-schemas/index.js +19 -11
  186. package/persisters/persister-electric-sql/index.js +19 -11
  187. package/persisters/persister-electric-sql/with-schemas/index.js +19 -11
  188. package/persisters/persister-expo-sqlite/index.js +19 -11
  189. package/persisters/persister-expo-sqlite/with-schemas/index.js +19 -11
  190. package/persisters/persister-file/index.js +17 -9
  191. package/persisters/persister-file/with-schemas/index.js +17 -9
  192. package/persisters/persister-indexed-db/index.js +17 -9
  193. package/persisters/persister-indexed-db/with-schemas/index.js +17 -9
  194. package/persisters/persister-libsql/index.js +19 -11
  195. package/persisters/persister-libsql/with-schemas/index.js +19 -11
  196. package/persisters/persister-partykit-client/index.js +17 -9
  197. package/persisters/persister-partykit-client/with-schemas/index.js +17 -9
  198. package/persisters/persister-partykit-server/index.js +2 -2
  199. package/persisters/persister-partykit-server/with-schemas/index.js +2 -2
  200. package/persisters/persister-pglite/index.js +19 -11
  201. package/persisters/persister-pglite/with-schemas/index.js +19 -11
  202. package/persisters/persister-postgres/index.js +19 -11
  203. package/persisters/persister-postgres/with-schemas/index.js +19 -11
  204. package/persisters/persister-powersync/index.js +19 -11
  205. package/persisters/persister-powersync/with-schemas/index.js +19 -11
  206. package/persisters/persister-react-native-mmkv/index.js +17 -9
  207. package/persisters/persister-react-native-mmkv/with-schemas/index.js +17 -9
  208. package/persisters/persister-react-native-sqlite/index.js +19 -11
  209. package/persisters/persister-react-native-sqlite/with-schemas/index.js +19 -11
  210. package/persisters/persister-remote/index.js +17 -9
  211. package/persisters/persister-remote/with-schemas/index.js +17 -9
  212. package/persisters/persister-sqlite-bun/index.js +19 -11
  213. package/persisters/persister-sqlite-bun/with-schemas/index.js +19 -11
  214. package/persisters/persister-sqlite-wasm/index.js +19 -11
  215. package/persisters/persister-sqlite-wasm/with-schemas/index.js +19 -11
  216. package/persisters/persister-sqlite3/index.js +19 -11
  217. package/persisters/persister-sqlite3/with-schemas/index.js +19 -11
  218. package/persisters/persister-yjs/index.js +19 -11
  219. package/persisters/persister-yjs/with-schemas/index.js +19 -11
  220. package/persisters/with-schemas/index.js +19 -11
  221. package/queries/index.js +15 -14
  222. package/queries/with-schemas/index.js +15 -14
  223. package/readme.md +2 -2
  224. package/relationships/index.js +13 -12
  225. package/relationships/with-schemas/index.js +13 -12
  226. package/store/index.js +74 -60
  227. package/store/with-schemas/index.js +74 -60
  228. package/synchronizers/index.js +17 -9
  229. package/synchronizers/synchronizer-broadcast-channel/index.js +17 -9
  230. package/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +17 -9
  231. package/synchronizers/synchronizer-local/index.js +19 -11
  232. package/synchronizers/synchronizer-local/with-schemas/index.js +19 -11
  233. package/synchronizers/synchronizer-ws-client/index.js +17 -9
  234. package/synchronizers/synchronizer-ws-client/with-schemas/index.js +17 -9
  235. package/synchronizers/synchronizer-ws-server/index.js +20 -12
  236. package/synchronizers/synchronizer-ws-server/with-schemas/index.js +20 -12
  237. package/synchronizers/synchronizer-ws-server-durable-object/index.js +17 -9
  238. package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +17 -9
  239. package/synchronizers/synchronizer-ws-server-simple/index.js +11 -10
  240. package/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +11 -10
  241. package/synchronizers/with-schemas/index.js +17 -9
  242. package/ui-react-inspector/index.js +81 -60
  243. package/ui-react-inspector/with-schemas/index.js +81 -60
  244. package/with-schemas/index.js +92 -64
package/readme.md CHANGED
@@ -6,7 +6,7 @@
6
6
  📦 Creating your project...
7
7
  ```
8
8
 
9
- <hr><section><h2 id="it-s-reactive">It&#x27;s <em>Reactive</em></h2><p>TinyBase lets you <a href="#register-granular-listeners">listen to changes</a> made to any part of your data. This means your app will be fast, since you only spend rendering cycles on things that change. The optional <a href="#call-hooks-to-bind-to-data">bindings to React</a> and <a href="#pre-built-reactive-components">pre-built components</a> let you easily build fully reactive UIs on top of TinyBase. You even get a built-in <a href="#set-checkpoints-for-an-undo-stack">undo stack</a>, and <a href="#an-inspector-for-your-data">developer tools</a>!</p></section><section><h2 id="it-s-database-like">It&#x27;s <em>Database-Like</em></h2><p>Consumer app? Enterprise app? Or even a game? Model <a href="#start-with-a-simple-key-value-store">key-value data</a> and <a href="#level-up-to-use-tabular-data">tabular data</a> with optional typed <a href="#apply-schemas-to-tables-values">schematization</a>, whatever its data structures. There are built-in <a href="#create-indexes-for-fast-lookups">indexing</a>, <a href="#define-metrics-and-aggregations">metric aggregation</a>, and tabular <a href="#model-table-relationships">relationships</a> APIs - and a powerful <a href="#build-complex-queries-with-tinyql">query engine</a> to select, join, filter, and group data (reactively!) without SQL.</p></section><section><h2 id="it-synchronizes">It <em>Synchronizes</em></h2><p>TinyBase has <a href="#synchronize-between-devices">native CRDT</a> support, meaning that you can deterministically <a href="https://tinybase.org/guides/synchronization/">synchronize</a> and merge data across multiple sources, clients, and servers. And although TinyBase is an in-memory data store, you can easily <a href="#persist-to-storage-databases-more">persist</a> your data to file, <a href="https://tinybase.org/api/persister-browser">browser storage</a>, <a href="https://tinybase.org/api/persister-indexed-db">IndexedDB</a>, <a href="https://tinybase.org/guides/persistence/database-persistence/">SQLite or PostgreSQL databases</a>, and <a href="https://tinybase.org/guides/persistence/third-party-crdt-persistence/">more</a>.</p></section><section><h2 id="it-s-built-for-a-local-first-world">It&#x27;s Built For A <em>Local-First</em> World</h2><p>TinyBase works anywhere that JavaScript does, but it&#x27;s especially great for local-first apps: where data is stored locally on the user&#x27;s device and that can be run offline. It&#x27;s tiny by name, tiny by nature: just <a href="#did-we-say-tiny">5.4kB - 12.1kB</a> and with no dependencies - yet <a href="#well-tested-and-documented">100% tested</a>, <a href="https://tinybase.org/guides/the-basics/getting-started/">fully documented</a>, and of course, <a href="https://github.com/tinyplex/tinybase">open source</a>!</p></section><hr><section id="friends"><h2 id="tinybase-works-great-on-its-own-but-also-plays-well-with-friends">TinyBase works great on its own, but also plays well with friends.</h2><div><a href="https://tinybase.org/guides/building-uis/getting-started-with-ui-react"><img src="https://tinybase.org/react.svg?asImg" width="48"> React</a></div><div><a href="https://tinybase.org/api/persister-indexed-db/functions/creation/createindexeddbpersister"><img src="https://tinybase.org/indexeddb.svg?asImg" width="48"> IndexedDB</a></div><div><a href="https://tinybase.org/api/persister-browser"><img src="https://tinybase.org/browser.svg?asImg" width="48"> OPFS</a></div><div><a href="https://tinybase.org/guides/integrations/cloudflare-durable-objects"><img src="https://tinybase.org/cloudflare.svg?asImg" width="48"> Cloudflare</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/postgresql.svg?asImg" width="48"> PostgreSQL</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/pglite.svg?asImg" width="48"> PGlite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/sqlite.svg?asImg" width="48"> SQLite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/bun.svg?asImg" width="48"> Bun SQLite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/expo.svg?asImg" width="48"> Expo SQLite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/electric.svg?asImg" width="48"> ElectricSQL</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/turso.svg?asImg" width="48"> Turso</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/powersync.svg?asImg" width="48"> PowerSync</a></div><div><a href="https://tinybase.org/api/persister-partykit-client"><img src="https://tinybase.org/partykit.svg?asImg" width="48"> PartyKit</a></div><div><a href="https://tinybase.org/api/persister-yjs/functions/creation/createyjspersister"><img src="https://tinybase.org/yjs.svg?asImg" width="48"> YJS</a></div><div><a href="https://tinybase.org/api/persister-cr-sqlite-wasm"><img src="https://tinybase.org/crsqlite.png" width="48"> CR-SQLite</a></div><div><a href="https://tinybase.org/api/persister-automerge"><img src="https://tinybase.org/automerge.svg?asImg" width="48"> Automerge</a></div><div><a href="https://tinybase.org/api/schematizer-zod/functions/creation/createzodschematizer"><img src="https://tinybase.org/zod.svg?asImg" width="48"> Zod</a></div><div><a href="https://tinybase.org/api/schematizer-typebox/functions/creation/createtypeboxschematizer"><img src="https://tinybase.org/typebox.svg?asImg" width="48"> TypeBox</a></div><div><a href="https://tinybase.org/api/schematizer-valibot/functions/creation/createvalibotschematizer"><img src="https://tinybase.org/valibot.svg?asImg" width="48"> Valibot</a></div><div><a href="https://tinybase.org/api/schematizer-arktype/functions/creation/createarktypeschematizer"><img src="https://tinybase.org/arktype.svg?asImg" width="48"> ArkType</a></div><div><a href="https://tinybase.org/api/schematizer-yup/functions/creation/createyupschematizer"><img src="https://tinybase.org/yup.svg?asImg" width="48"> Yup</a></div><div><a href="https://tinybase.org/api/schematizer-effect/functions/creation/createeffectschematizer"><img src="https://tinybase.org/effect.svg?asImg" width="48"> Effect</a></div><p>(Baffled by all these logos? Check out our <a href="https://tinybase.org/guides/the-basics/architectural-options">architectural options</a> guide to make sense of it all!)</p></section><hr><section id="follow"><a href="https://github.com/tinyplex/tinybase" target="_blank"><img src="https://img.shields.io/github/stars/tinyplex/tinybase?style=for-the-badge&amp;logo=GitHub&amp;logoColor=%23fff&amp;label=GitHub&amp;labelColor=%23d81b60&amp;color=%23333"> </a><a href="https://bsky.app/profile/tinybase.bsky.social"><img src="https://img.shields.io/badge/Bluesky-Follow-blue?style=for-the-badge&amp;logo=bluesky&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%230285FF"> </a><a href="https://x.com/tinybasejs" target="_blank"><img src="https://img.shields.io/badge/%2F%20Twitter-Follow-blue?style=for-the-badge&amp;logo=x&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%23000"> </a><a href="https://discord.com/invite/mGz3mevwP8" target="_blank"><img src="https://img.shields.io/discord/1027918215323590676?style=for-the-badge&amp;logo=discord&amp;logoColor=%23fff&amp;label=Discord&amp;labelColor=%233131e8&amp;color=%23333"></a><br><a href="https://github.com/tinyplex/tinybase/discussions" target="_blank"><img src="https://img.shields.io/github/discussions/tinyplex/tinybase?style=for-the-badge&amp;logo=GitHub&amp;logoColor=%23fff&amp;label=Ideas&amp;labelColor=%23d81b60&amp;color=%23333"> </a><a href="https://github.com/tinyplex/tinybase/issues" target="_blank"><img src="https://img.shields.io/github/issues/tinyplex/tinybase?style=for-the-badge&amp;logo=GitHub&amp;logoColor=%23fff&amp;label=Issues&amp;labelColor=%23d81b60&amp;color=%23333"> </a><a href="#well-tested-and-documented"><img src="https://img.shields.io/badge/Tests-100%25-green?style=for-the-badge&amp;logo=Vitest&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%2387c305"> </a><a href="https://www.npmjs.com/package/tinybase/v/7.3.2" target="_blank"><img src="https://img.shields.io/npm/v/tinybase?style=for-the-badge&amp;logo=npm&amp;logoColor=%23fff&amp;labelColor=%23bd0005&amp;color=%23333"></a></section><hr><section><h2 id="start-with-a-simple-key-value-store">Start with a simple key-value store.</h2><p>Creating a <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> requires just a simple call to the <a href="https://tinybase.org/api/the-essentials/creating-stores/createstore/"><code>createStore</code></a> function. Once you have one, you can easily set <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> in it by unique <a href="https://tinybase.org/api/common/type-aliases/identity/id/"><code>Id</code></a>. And of course you can easily get them back out again.</p><p>Read more about using keyed value data in <a href="https://tinybase.org/guides/the-basics/">The Basics</a> guide.</p></section>
9
+ <hr><section><h2 id="it-s-reactive">It&#x27;s <em>Reactive</em></h2><p>TinyBase lets you <a href="#register-granular-listeners">listen to changes</a> made to any part of your data. This means your app will be fast, since you only spend rendering cycles on things that change. The optional <a href="#call-hooks-to-bind-to-data">bindings to React</a> and <a href="#pre-built-reactive-components">pre-built components</a> let you easily build fully reactive UIs on top of TinyBase. You even get a built-in <a href="#set-checkpoints-for-an-undo-stack">undo stack</a>, and <a href="#an-inspector-for-your-data">developer tools</a>!</p></section><section><h2 id="it-s-database-like">It&#x27;s <em>Database-Like</em></h2><p>Consumer app? Enterprise app? Or even a game? Model <a href="#start-with-a-simple-key-value-store">key-value data</a> and <a href="#level-up-to-use-tabular-data">tabular data</a> with optional typed <a href="#apply-schemas-to-tables-values">schematization</a>, whatever its data structures. There are built-in <a href="#create-indexes-for-fast-lookups">indexing</a>, <a href="#define-metrics-and-aggregations">metric aggregation</a>, and tabular <a href="#model-table-relationships">relationships</a> APIs - and a powerful <a href="#build-complex-queries-with-tinyql">query engine</a> to select, join, filter, and group data (reactively!) without SQL.</p></section><section><h2 id="it-synchronizes">It <em>Synchronizes</em></h2><p>TinyBase has <a href="#synchronize-between-devices">native CRDT</a> support, meaning that you can deterministically <a href="https://tinybase.org/guides/synchronization/">synchronize</a> and merge data across multiple sources, clients, and servers. And although TinyBase is an in-memory data store, you can easily <a href="#persist-to-storage-databases-more">persist</a> your data to file, <a href="https://tinybase.org/api/persister-browser">browser storage</a>, <a href="https://tinybase.org/api/persister-indexed-db">IndexedDB</a>, <a href="https://tinybase.org/guides/persistence/database-persistence/">SQLite or PostgreSQL databases</a>, and <a href="https://tinybase.org/guides/persistence/third-party-crdt-persistence/">more</a>.</p></section><section><h2 id="it-s-built-for-a-local-first-world">It&#x27;s Built For A <em>Local-First</em> World</h2><p>TinyBase works anywhere that JavaScript does, but it&#x27;s especially great for local-first apps: where data is stored locally on the user&#x27;s device and that can be run offline. It&#x27;s tiny by name, tiny by nature: just <a href="#did-we-say-tiny">5.4kB - 12.1kB</a> and with no dependencies - yet <a href="#well-tested-and-documented">100% tested</a>, <a href="https://tinybase.org/guides/the-basics/getting-started/">fully documented</a>, and of course, <a href="https://github.com/tinyplex/tinybase">open source</a>!</p></section><hr><section id="friends"><h2 id="tinybase-works-great-on-its-own-but-also-plays-well-with-friends">TinyBase works great on its own, but also plays well with friends.</h2><div><a href="https://tinybase.org/guides/building-uis/getting-started-with-ui-react"><img src="https://tinybase.org/react.svg?asImg" width="48"> React</a></div><div><a href="https://tinybase.org/api/persister-indexed-db/functions/creation/createindexeddbpersister"><img src="https://tinybase.org/indexeddb.svg?asImg" width="48"> IndexedDB</a></div><div><a href="https://tinybase.org/api/persister-browser"><img src="https://tinybase.org/browser.svg?asImg" width="48"> OPFS</a></div><div><a href="https://tinybase.org/guides/integrations/cloudflare-durable-objects"><img src="https://tinybase.org/cloudflare.svg?asImg" width="48"> Cloudflare</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/postgresql.svg?asImg" width="48"> PostgreSQL</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/pglite.svg?asImg" width="48"> PGlite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/sqlite.svg?asImg" width="48"> SQLite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/bun.svg?asImg" width="48"> Bun SQLite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/expo.svg?asImg" width="48"> Expo SQLite</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/electric.svg?asImg" width="48"> ElectricSQL</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/turso.svg?asImg" width="48"> Turso</a></div><div><a href="https://tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://tinybase.org/powersync.svg?asImg" width="48"> PowerSync</a></div><div><a href="https://tinybase.org/api/persister-partykit-client"><img src="https://tinybase.org/partykit.svg?asImg" width="48"> PartyKit</a></div><div><a href="https://tinybase.org/api/persister-yjs/functions/creation/createyjspersister"><img src="https://tinybase.org/yjs.svg?asImg" width="48"> YJS</a></div><div><a href="https://tinybase.org/api/persister-cr-sqlite-wasm"><img src="https://tinybase.org/crsqlite.png" width="48"> CR-SQLite</a></div><div><a href="https://tinybase.org/api/persister-automerge"><img src="https://tinybase.org/automerge.svg?asImg" width="48"> Automerge</a></div><div><a href="https://tinybase.org/api/schematizer-zod/functions/creation/createzodschematizer"><img src="https://tinybase.org/zod.svg?asImg" width="48"> Zod</a></div><div><a href="https://tinybase.org/api/schematizer-typebox/functions/creation/createtypeboxschematizer"><img src="https://tinybase.org/typebox.svg?asImg" width="48"> TypeBox</a></div><div><a href="https://tinybase.org/api/schematizer-valibot/functions/creation/createvalibotschematizer"><img src="https://tinybase.org/valibot.svg?asImg" width="48"> Valibot</a></div><div><a href="https://tinybase.org/api/schematizer-arktype/functions/creation/createarktypeschematizer"><img src="https://tinybase.org/arktype.svg?asImg" width="48"> ArkType</a></div><div><a href="https://tinybase.org/api/schematizer-yup/functions/creation/createyupschematizer"><img src="https://tinybase.org/yup.svg?asImg" width="48"> Yup</a></div><div><a href="https://tinybase.org/api/schematizer-effect/functions/creation/createeffectschematizer"><img src="https://tinybase.org/effect.svg?asImg" width="48"> Effect</a></div><p>(Baffled by all these logos? Check out our <a href="https://tinybase.org/guides/the-basics/architectural-options">architectural options</a> guide to make sense of it all!)</p></section><hr><section id="follow"><a href="https://github.com/tinyplex/tinybase" target="_blank"><img src="https://img.shields.io/github/stars/tinyplex/tinybase?style=for-the-badge&amp;logo=GitHub&amp;logoColor=%23fff&amp;label=GitHub&amp;labelColor=%23d81b60&amp;color=%23333"> </a><a href="https://bsky.app/profile/tinybase.bsky.social"><img src="https://img.shields.io/badge/Bluesky-Follow-blue?style=for-the-badge&amp;logo=bluesky&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%230285FF"> </a><a href="https://x.com/tinybasejs" target="_blank"><img src="https://img.shields.io/badge/%2F%20Twitter-Follow-blue?style=for-the-badge&amp;logo=x&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%23000"> </a><a href="https://discord.com/invite/mGz3mevwP8" target="_blank"><img src="https://img.shields.io/discord/1027918215323590676?style=for-the-badge&amp;logo=discord&amp;logoColor=%23fff&amp;label=Discord&amp;labelColor=%233131e8&amp;color=%23333"></a><br><a href="https://github.com/tinyplex/tinybase/discussions" target="_blank"><img src="https://img.shields.io/github/discussions/tinyplex/tinybase?style=for-the-badge&amp;logo=GitHub&amp;logoColor=%23fff&amp;label=Ideas&amp;labelColor=%23d81b60&amp;color=%23333"> </a><a href="https://github.com/tinyplex/tinybase/issues" target="_blank"><img src="https://img.shields.io/github/issues/tinyplex/tinybase?style=for-the-badge&amp;logo=GitHub&amp;logoColor=%23fff&amp;label=Issues&amp;labelColor=%23d81b60&amp;color=%23333"> </a><a href="#well-tested-and-documented"><img src="https://img.shields.io/badge/Tests-100%25-green?style=for-the-badge&amp;logo=Vitest&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%2387c305"> </a><a href="https://www.npmjs.com/package/tinybase/v/7.3.3" target="_blank"><img src="https://img.shields.io/npm/v/tinybase?style=for-the-badge&amp;logo=npm&amp;logoColor=%23fff&amp;labelColor=%23bd0005&amp;color=%23333"></a></section><hr><section><h2 id="start-with-a-simple-key-value-store">Start with a simple key-value store.</h2><p>Creating a <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> requires just a simple call to the <a href="https://tinybase.org/api/the-essentials/creating-stores/createstore/"><code>createStore</code></a> function. Once you have one, you can easily set <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> in it by unique <a href="https://tinybase.org/api/common/type-aliases/identity/id/"><code>Id</code></a>. And of course you can easily get them back out again.</p><p>Read more about using keyed value data in <a href="https://tinybase.org/guides/the-basics/">The Basics</a> guide.</p></section>
10
10
 
11
11
  ```js
12
12
  import {createStore} from 'tinybase';
@@ -283,4 +283,4 @@ console.log(store.getCell('pets', 'felix', 'sold'));
283
283
  // -> false
284
284
  ```
285
285
 
286
- <section><h2 id="did-we-say-tiny">Did we say tiny?</h2><p>If you use the basic <a href="https://tinybase.org/api/store/"><code>store</code></a> module alone, you&#x27;ll only add a gzipped <em>5.4kB</em> to your app. Incrementally add the other modules as you need more functionality, or get it all for <em>12.1kB</em>.</p><p>The optional <a href="https://tinybase.org/api/ui-react/"><code>ui-react</code></a> module is just <em>5.5kB</em>, the ui-react-dom components are another <em>3.8kB</em>, and everything is super fast. Life is easy when you have zero dependencies!</p><p>Read more about how TinyBase is structured and packaged in the <a href="https://tinybase.org/guides/how-tinybase-is-built/architecture/">Architecture</a> guide.</p></section><div class="table"><table class="fixed"><tbody><tr><th> </th><th>Minified .js.gz</th><th>Source .js</th></tr><tr><th class="right"><a href="https://tinybase.org/api/store/">tinybase/store</a> (minimal)</th><td>5.4kB</td><td>54.6kB</td></tr><tr><th class="right">tinybase (complete)</th><td>12.1kB</td><td>127.0kB</td></tr><tr><th class="right"><a href="https://tinybase.org/api/ui-react/">ui-react</a></th><td>5.5kB</td><td>59.8kB</td></tr><tr><th class="right"><a href="https://tinybase.org/api/ui-react-dom/">ui-react-dom</a></th><td>3.8kB</td><td>34.0kB</td></tr></tbody></table></div><section><h2 id="well-tested-and-documented">Well tested and documented.</h2><p>TinyBase has <em>100.0%</em> test coverage, including the code throughout the documentation - even on this page! The guides, demos, and API examples are designed to make it as easy as possible for you to get your TinyBase-powered app up and running.</p><p>Read more about how TinyBase is tested in the Unit <a href="https://tinybase.org/guides/how-tinybase-is-built/testing/">Testing</a> guide.</p></section><div class="table"><table class="fixed"><tbody><tr><th width="30%"> </th><th>Total</th><th>Tested</th><th>Coverage</th></tr><tr><th class="right">Lines</th><td>2,408</td><td>2,408</td><td>100.0%</td></tr><tr><th class="right">Statements</th><td>2,617</td><td>2,617</td><td>100.0%</td></tr><tr><th class="right">Functions</th><td>1,046</td><td>1,046</td><td>100.0%</td></tr><tr><th class="right">Branches</th><td>906</td><td>906</td><td>100.0%</td></tr><tr><th class="right">Tests</th><td colspan="3">7,286</td></tr><tr><th class="right">Assertions</th><td colspan="3">32,322</td></tr></tbody></table></div><hr><section id="sponsors"><h2 id="proud-to-be-supported-by">Proud to be supported by:</h2><a href="https://github.com/fastrepl" target="_blank"><img src="https://github.com/fastrepl.png?size=48" title="fastrepl" width="48" height="48"></a><a href="https://github.com/expo" target="_blank"><img src="https://github.com/expo.png?size=48" title="expo" width="48" height="48"></a><a href="https://github.com/dylmye" target="_blank"><img src="https://github.com/dylmye.png?size=48" title="dylmye" width="48" height="48"></a><a href="https://github.com/ComputelessComputer" target="_blank"><img src="https://github.com/ComputelessComputer.png?size=48" title="ComputelessComputer" width="48" height="48"></a><a href="https://github.com/cancelself" target="_blank"><img src="https://github.com/cancelself.png?size=48" title="cancelself" width="48" height="48"></a><a href="https://github.com/cpojer" target="_blank"><img src="https://github.com/cpojer.png?size=48" title="cpojer" width="48" height="48"></a><a href="https://github.com/beekeeb" target="_blank"><img src="https://github.com/beekeeb.png?size=48" title="beekeeb" width="48" height="48"></a><a href="https://github.com/WonderPanda" target="_blank"><img src="https://github.com/WonderPanda.png?size=48" title="WonderPanda" width="48" height="48"></a><a href="https://github.com/arpitBhalla" target="_blank"><img src="https://github.com/arpitBhalla.png?size=48" title="arpitBhalla" width="48" height="48"></a></section><section id="users"><h2 id="excited-to-be-used-by">Excited to be used by:</h2><a href="https://github.com/behrends" target="_blank"><img src="https://github.com/behrends.png?size=48" title="behrends" width="48" height="48"></a><a href="https://github.com/betomoedano" target="_blank"><img src="https://github.com/betomoedano.png?size=48" title="betomoedano" width="48" height="48"></a><a href="https://github.com/brentvatne" target="_blank"><img src="https://github.com/brentvatne.png?size=48" title="brentvatne" width="48" height="48"></a><a href="https://github.com/byCedric" target="_blank"><img src="https://github.com/byCedric.png?size=48" title="byCedric" width="48" height="48"></a><a href="https://github.com/circadian-risk" target="_blank"><img src="https://github.com/circadian-risk.png?size=48" title="circadian-risk" width="48" height="48"></a><a href="https://github.com/cpojer" target="_blank"><img src="https://github.com/cpojer.png?size=48" title="cpojer" width="48" height="48"></a><a href="https://github.com/cubecull" target="_blank"><img src="https://github.com/cubecull.png?size=48" title="cubecull" width="48" height="48"></a><a href="https://github.com/erwinkn" target="_blank"><img src="https://github.com/erwinkn.png?size=48" title="erwinkn" width="48" height="48"></a><a href="https://github.com/expo" target="_blank"><img src="https://github.com/expo.png?size=48" title="expo" width="48" height="48"></a><a href="https://github.com/ezra-en" target="_blank"><img src="https://github.com/ezra-en.png?size=48" title="ezra-en" width="48" height="48"></a><a href="https://github.com/feychenie" target="_blank"><img src="https://github.com/feychenie.png?size=48" title="feychenie" width="48" height="48"></a><a href="https://github.com/flaming-codes" target="_blank"><img src="https://github.com/flaming-codes.png?size=48" title="flaming-codes" width="48" height="48"></a><a href="https://github.com/fostertheweb" target="_blank"><img src="https://github.com/fostertheweb.png?size=48" title="fostertheweb" width="48" height="48"></a><a href="https://github.com/Giulio987" target="_blank"><img src="https://github.com/Giulio987.png?size=48" title="Giulio987" width="48" height="48"></a><a href="https://github.com/hi-ogawa" target="_blank"><img src="https://github.com/hi-ogawa.png?size=48" title="hi-ogawa" width="48" height="48"></a><a href="https://github.com/itsdevcoffee" target="_blank"><img src="https://github.com/itsdevcoffee.png?size=48" title="itsdevcoffee" width="48" height="48"></a><a href="https://github.com/jbolda" target="_blank"><img src="https://github.com/jbolda.png?size=48" title="jbolda" width="48" height="48"></a><a href="https://github.com/Kayoo-asso" target="_blank"><img src="https://github.com/Kayoo-asso.png?size=48" title="Kayoo-asso" width="48" height="48"></a><a href="https://github.com/kotofurumiya" target="_blank"><img src="https://github.com/kotofurumiya.png?size=48" title="kotofurumiya" width="48" height="48"></a><a href="https://github.com/Kudo" target="_blank"><img src="https://github.com/Kudo.png?size=48" title="Kudo" width="48" height="48"></a><a href="https://github.com/learn-anything" target="_blank"><img src="https://github.com/learn-anything.png?size=48" title="learn-anything" width="48" height="48"></a><a href="https://github.com/lluc" target="_blank"><img src="https://github.com/lluc.png?size=48" title="lluc" width="48" height="48"></a><a href="https://github.com/marksteve" target="_blank"><img src="https://github.com/marksteve.png?size=48" title="marksteve" width="48" height="48"></a><a href="https://github.com/miking-the-viking" target="_blank"><img src="https://github.com/miking-the-viking.png?size=48" title="miking-the-viking" width="48" height="48"></a><a href="https://github.com/mjamesderocher" target="_blank"><img src="https://github.com/mjamesderocher.png?size=48" title="mjamesderocher" width="48" height="48"></a><a href="https://github.com/mouktardev" target="_blank"><img src="https://github.com/mouktardev.png?size=48" title="mouktardev" width="48" height="48"></a><a href="https://github.com/nickmessing" target="_blank"><img src="https://github.com/nickmessing.png?size=48" title="nickmessing" width="48" height="48"></a><a href="https://github.com/nikitavoloboev" target="_blank"><img src="https://github.com/nikitavoloboev.png?size=48" title="nikitavoloboev" width="48" height="48"></a><a href="https://github.com/nkzw-tech" target="_blank"><img src="https://github.com/nkzw-tech.png?size=48" title="nkzw-tech" width="48" height="48"></a><a href="https://github.com/palerdot" target="_blank"><img src="https://github.com/palerdot.png?size=48" title="palerdot" width="48" height="48"></a><a href="https://github.com/PorcoRosso85" target="_blank"><img src="https://github.com/PorcoRosso85.png?size=48" title="PorcoRosso85" width="48" height="48"></a><a href="https://github.com/primodiumxyz" target="_blank"><img src="https://github.com/primodiumxyz.png?size=48" title="primodiumxyz" width="48" height="48"></a><a href="https://github.com/shaneosullivan" target="_blank"><img src="https://github.com/shaneosullivan.png?size=48" title="shaneosullivan" width="48" height="48"></a><a href="https://github.com/sudo-self" target="_blank"><img src="https://github.com/sudo-self.png?size=48" title="sudo-self" width="48" height="48"></a><a href="https://github.com/SuperSonicHub1" target="_blank"><img src="https://github.com/SuperSonicHub1.png?size=48" title="SuperSonicHub1" width="48" height="48"></a><a href="https://github.com/threepointone" target="_blank"><img src="https://github.com/threepointone.png?size=48" title="threepointone" width="48" height="48"></a><a href="https://github.com/uptonking" target="_blank"><img src="https://github.com/uptonking.png?size=48" title="uptonking" width="48" height="48"></a><a href="https://github.com/ViktorZhurbin" target="_blank"><img src="https://github.com/ViktorZhurbin.png?size=48" title="ViktorZhurbin" width="48" height="48"></a><a href="https://github.com/wilkerlucio" target="_blank"><img src="https://github.com/wilkerlucio.png?size=48" title="wilkerlucio" width="48" height="48"></a><a href="https://github.com/WonderPanda" target="_blank"><img src="https://github.com/WonderPanda.png?size=48" title="WonderPanda" width="48" height="48"></a></section><hr><p><a class="start" href="https://tinybase.org/guides/the-basics/getting-started/">Get started</a></p><p><a href="https://tinybase.org/demos/">Try the demos</a></p><p><a href="https://tinybase.org/api/the-essentials/creating-stores/store/">Read the docs</a></p><hr><section id="family"><h2 id="meet-the-family">Meet the family</h2><p>TinyBase is part of a group of small libraries designed to help make rich client and local-first apps easier to build. Check out the others!</p><p><a href="https://synclets.org" target="_blank"><img src="https://synclets.org/favicon.svg?asImg" width="48"><br><b>Synclets</b></a><br>An open, storage-agnostic, sync engine development kit.</p><p><a href="https://tinywidgets.org" target="_blank"><img src="https://tinywidgets.org/favicon.svg?asImg" width="48"><br><b>TinyWidgets</b></a><br>A collection of tiny, reusable, UI components.</p><p><a href="https://tinytick.org" target="_blank"><img src="https://tinytick.org/favicon.svg?asImg" width="48"><br><b>TinyTick</b></a><br>A tiny but very useful task orchestrator.</p></section><hr><section id="about"><h2 id="about">About</h2><p>Modern apps deserve better. Why trade reactive user experiences to be able to use relational data? Or sacrifice features for bundle size? And why does the cloud do all the work <a href="https://localfirstweb.dev/" target="_blank">anyway</a>?</p><p>Building TinyBase was originally an interesting exercise for <a rel="me" href="https://tripleodeon.com">me</a> in API design, minification, and documentation. But now it has taken on a life of its own, and has grown beyond my wildest expectations.</p><p>It could not have been built without these great <a href="https://tinybase.org/guides/how-tinybase-is-built/credits/#giants">projects</a> and <a href="https://tinybase.org/guides/how-tinybase-is-built/credits/#and-friends">friends</a>, and I hope you enjoy using it as much as I do building it!</p></section><section id="story"><h2 id="the-story">The story</h2><a href="https://youtu.be/hXL7OkW-Prk?t=1232" target="_blank"><img src="https://tinybase.org/youtube.webp"></a></section>
286
+ <section><h2 id="did-we-say-tiny">Did we say tiny?</h2><p>If you use the basic <a href="https://tinybase.org/api/store/"><code>store</code></a> module alone, you&#x27;ll only add a gzipped <em>5.4kB</em> to your app. Incrementally add the other modules as you need more functionality, or get it all for <em>12.1kB</em>.</p><p>The optional <a href="https://tinybase.org/api/ui-react/"><code>ui-react</code></a> module is just <em>5.5kB</em>, the ui-react-dom components are another <em>3.8kB</em>, and everything is super fast. Life is easy when you have zero dependencies!</p><p>Read more about how TinyBase is structured and packaged in the <a href="https://tinybase.org/guides/how-tinybase-is-built/architecture/">Architecture</a> guide.</p></section><div class="table"><table class="fixed"><tbody><tr><th> </th><th>Minified .js.gz</th><th>Source .js</th></tr><tr><th class="right"><a href="https://tinybase.org/api/store/">tinybase/store</a> (minimal)</th><td>5.4kB</td><td>54.6kB</td></tr><tr><th class="right">tinybase (complete)</th><td>12.1kB</td><td>127.0kB</td></tr><tr><th class="right"><a href="https://tinybase.org/api/ui-react/">ui-react</a></th><td>5.5kB</td><td>59.8kB</td></tr><tr><th class="right"><a href="https://tinybase.org/api/ui-react-dom/">ui-react-dom</a></th><td>3.8kB</td><td>34.0kB</td></tr></tbody></table></div><section><h2 id="well-tested-and-documented">Well tested and documented.</h2><p>TinyBase has <em>100.0%</em> test coverage, including the code throughout the documentation - even on this page! The guides, demos, and API examples are designed to make it as easy as possible for you to get your TinyBase-powered app up and running.</p><p>Read more about how TinyBase is tested in the Unit <a href="https://tinybase.org/guides/how-tinybase-is-built/testing/">Testing</a> guide.</p></section><div class="table"><table class="fixed"><tbody><tr><th width="30%"> </th><th>Total</th><th>Tested</th><th>Coverage</th></tr><tr><th class="right">Lines</th><td>2,408</td><td>2,408</td><td>100.0%</td></tr><tr><th class="right">Statements</th><td>2,617</td><td>2,617</td><td>100.0%</td></tr><tr><th class="right">Functions</th><td>1,046</td><td>1,046</td><td>100.0%</td></tr><tr><th class="right">Branches</th><td>906</td><td>906</td><td>100.0%</td></tr><tr><th class="right">Tests</th><td colspan="3">7,291</td></tr><tr><th class="right">Assertions</th><td colspan="3">32,336</td></tr></tbody></table></div><hr><section id="sponsors"><h2 id="proud-to-be-supported-by">Proud to be supported by:</h2><a href="https://github.com/fastrepl" target="_blank"><img src="https://github.com/fastrepl.png?size=48" title="fastrepl" width="48" height="48"></a><a href="https://github.com/expo" target="_blank"><img src="https://github.com/expo.png?size=48" title="expo" width="48" height="48"></a><a href="https://github.com/dylmye" target="_blank"><img src="https://github.com/dylmye.png?size=48" title="dylmye" width="48" height="48"></a><a href="https://github.com/ComputelessComputer" target="_blank"><img src="https://github.com/ComputelessComputer.png?size=48" title="ComputelessComputer" width="48" height="48"></a><a href="https://github.com/cancelself" target="_blank"><img src="https://github.com/cancelself.png?size=48" title="cancelself" width="48" height="48"></a><a href="https://github.com/cpojer" target="_blank"><img src="https://github.com/cpojer.png?size=48" title="cpojer" width="48" height="48"></a><a href="https://github.com/beekeeb" target="_blank"><img src="https://github.com/beekeeb.png?size=48" title="beekeeb" width="48" height="48"></a><a href="https://github.com/WonderPanda" target="_blank"><img src="https://github.com/WonderPanda.png?size=48" title="WonderPanda" width="48" height="48"></a><a href="https://github.com/arpitBhalla" target="_blank"><img src="https://github.com/arpitBhalla.png?size=48" title="arpitBhalla" width="48" height="48"></a></section><section id="users"><h2 id="excited-to-be-used-by">Excited to be used by:</h2><a href="https://github.com/behrends" target="_blank"><img src="https://github.com/behrends.png?size=48" title="behrends" width="48" height="48"></a><a href="https://github.com/betomoedano" target="_blank"><img src="https://github.com/betomoedano.png?size=48" title="betomoedano" width="48" height="48"></a><a href="https://github.com/brentvatne" target="_blank"><img src="https://github.com/brentvatne.png?size=48" title="brentvatne" width="48" height="48"></a><a href="https://github.com/byCedric" target="_blank"><img src="https://github.com/byCedric.png?size=48" title="byCedric" width="48" height="48"></a><a href="https://github.com/circadian-risk" target="_blank"><img src="https://github.com/circadian-risk.png?size=48" title="circadian-risk" width="48" height="48"></a><a href="https://github.com/cpojer" target="_blank"><img src="https://github.com/cpojer.png?size=48" title="cpojer" width="48" height="48"></a><a href="https://github.com/cubecull" target="_blank"><img src="https://github.com/cubecull.png?size=48" title="cubecull" width="48" height="48"></a><a href="https://github.com/erwinkn" target="_blank"><img src="https://github.com/erwinkn.png?size=48" title="erwinkn" width="48" height="48"></a><a href="https://github.com/expo" target="_blank"><img src="https://github.com/expo.png?size=48" title="expo" width="48" height="48"></a><a href="https://github.com/ezra-en" target="_blank"><img src="https://github.com/ezra-en.png?size=48" title="ezra-en" width="48" height="48"></a><a href="https://github.com/feychenie" target="_blank"><img src="https://github.com/feychenie.png?size=48" title="feychenie" width="48" height="48"></a><a href="https://github.com/flaming-codes" target="_blank"><img src="https://github.com/flaming-codes.png?size=48" title="flaming-codes" width="48" height="48"></a><a href="https://github.com/fostertheweb" target="_blank"><img src="https://github.com/fostertheweb.png?size=48" title="fostertheweb" width="48" height="48"></a><a href="https://github.com/Giulio987" target="_blank"><img src="https://github.com/Giulio987.png?size=48" title="Giulio987" width="48" height="48"></a><a href="https://github.com/hi-ogawa" target="_blank"><img src="https://github.com/hi-ogawa.png?size=48" title="hi-ogawa" width="48" height="48"></a><a href="https://github.com/itsdevcoffee" target="_blank"><img src="https://github.com/itsdevcoffee.png?size=48" title="itsdevcoffee" width="48" height="48"></a><a href="https://github.com/jbolda" target="_blank"><img src="https://github.com/jbolda.png?size=48" title="jbolda" width="48" height="48"></a><a href="https://github.com/Kayoo-asso" target="_blank"><img src="https://github.com/Kayoo-asso.png?size=48" title="Kayoo-asso" width="48" height="48"></a><a href="https://github.com/kotofurumiya" target="_blank"><img src="https://github.com/kotofurumiya.png?size=48" title="kotofurumiya" width="48" height="48"></a><a href="https://github.com/Kudo" target="_blank"><img src="https://github.com/Kudo.png?size=48" title="Kudo" width="48" height="48"></a><a href="https://github.com/learn-anything" target="_blank"><img src="https://github.com/learn-anything.png?size=48" title="learn-anything" width="48" height="48"></a><a href="https://github.com/lluc" target="_blank"><img src="https://github.com/lluc.png?size=48" title="lluc" width="48" height="48"></a><a href="https://github.com/marksteve" target="_blank"><img src="https://github.com/marksteve.png?size=48" title="marksteve" width="48" height="48"></a><a href="https://github.com/miking-the-viking" target="_blank"><img src="https://github.com/miking-the-viking.png?size=48" title="miking-the-viking" width="48" height="48"></a><a href="https://github.com/mjamesderocher" target="_blank"><img src="https://github.com/mjamesderocher.png?size=48" title="mjamesderocher" width="48" height="48"></a><a href="https://github.com/mouktardev" target="_blank"><img src="https://github.com/mouktardev.png?size=48" title="mouktardev" width="48" height="48"></a><a href="https://github.com/nickmessing" target="_blank"><img src="https://github.com/nickmessing.png?size=48" title="nickmessing" width="48" height="48"></a><a href="https://github.com/nikitavoloboev" target="_blank"><img src="https://github.com/nikitavoloboev.png?size=48" title="nikitavoloboev" width="48" height="48"></a><a href="https://github.com/nkzw-tech" target="_blank"><img src="https://github.com/nkzw-tech.png?size=48" title="nkzw-tech" width="48" height="48"></a><a href="https://github.com/palerdot" target="_blank"><img src="https://github.com/palerdot.png?size=48" title="palerdot" width="48" height="48"></a><a href="https://github.com/PorcoRosso85" target="_blank"><img src="https://github.com/PorcoRosso85.png?size=48" title="PorcoRosso85" width="48" height="48"></a><a href="https://github.com/primodiumxyz" target="_blank"><img src="https://github.com/primodiumxyz.png?size=48" title="primodiumxyz" width="48" height="48"></a><a href="https://github.com/shaneosullivan" target="_blank"><img src="https://github.com/shaneosullivan.png?size=48" title="shaneosullivan" width="48" height="48"></a><a href="https://github.com/sudo-self" target="_blank"><img src="https://github.com/sudo-self.png?size=48" title="sudo-self" width="48" height="48"></a><a href="https://github.com/SuperSonicHub1" target="_blank"><img src="https://github.com/SuperSonicHub1.png?size=48" title="SuperSonicHub1" width="48" height="48"></a><a href="https://github.com/threepointone" target="_blank"><img src="https://github.com/threepointone.png?size=48" title="threepointone" width="48" height="48"></a><a href="https://github.com/uptonking" target="_blank"><img src="https://github.com/uptonking.png?size=48" title="uptonking" width="48" height="48"></a><a href="https://github.com/ViktorZhurbin" target="_blank"><img src="https://github.com/ViktorZhurbin.png?size=48" title="ViktorZhurbin" width="48" height="48"></a><a href="https://github.com/wilkerlucio" target="_blank"><img src="https://github.com/wilkerlucio.png?size=48" title="wilkerlucio" width="48" height="48"></a><a href="https://github.com/WonderPanda" target="_blank"><img src="https://github.com/WonderPanda.png?size=48" title="WonderPanda" width="48" height="48"></a></section><hr><p><a class="start" href="https://tinybase.org/guides/the-basics/getting-started/">Get started</a></p><p><a href="https://tinybase.org/demos/">Try the demos</a></p><p><a href="https://tinybase.org/api/the-essentials/creating-stores/store/">Read the docs</a></p><hr><section id="family"><h2 id="meet-the-family">Meet the family</h2><p>TinyBase is part of a group of small libraries designed to help make rich client and local-first apps easier to build. Check out the others!</p><p><a href="https://synclets.org" target="_blank"><img src="https://synclets.org/favicon.svg?asImg" width="48"><br><b>Synclets</b></a><br>An open, storage-agnostic, sync engine development kit.</p><p><a href="https://tinywidgets.org" target="_blank"><img src="https://tinywidgets.org/favicon.svg?asImg" width="48"><br><b>TinyWidgets</b></a><br>A collection of tiny, reusable, UI components.</p><p><a href="https://tinytick.org" target="_blank"><img src="https://tinytick.org/favicon.svg?asImg" width="48"><br><b>TinyTick</b></a><br>A tiny but very useful task orchestrator.</p></section><hr><section id="about"><h2 id="about">About</h2><p>Modern apps deserve better. Why trade reactive user experiences to be able to use relational data? Or sacrifice features for bundle size? And why does the cloud do all the work <a href="https://localfirstweb.dev/" target="_blank">anyway</a>?</p><p>Building TinyBase was originally an interesting exercise for <a rel="me" href="https://tripleodeon.com">me</a> in API design, minification, and documentation. But now it has taken on a life of its own, and has grown beyond my wildest expectations.</p><p>It could not have been built without these great <a href="https://tinybase.org/guides/how-tinybase-is-built/credits/#giants">projects</a> and <a href="https://tinybase.org/guides/how-tinybase-is-built/credits/#and-friends">friends</a>, and I hope you enjoy using it as much as I do building it!</p></section><section id="story"><h2 id="the-story">The story</h2><a href="https://youtu.be/hXL7OkW-Prk?t=1232" target="_blank"><img src="https://tinybase.org/youtube.webp"></a></section>
@@ -37,20 +37,21 @@ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
37
37
  const object = Object;
38
38
  const objFreeze = object.freeze;
39
39
 
40
- const mapNew = (entries) => new Map(entries);
41
- const mapKeys = (map) => [...(map?.keys() ?? [])];
42
- const mapGet = (map, key) => map?.get(key);
43
- const mapForEach = (map, cb) =>
44
- collForEach(map, (value, key) => cb(key, value));
45
- const mapSet = (map, key, value) =>
46
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
47
- const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
48
- if (!collHas(map, key)) {
49
- mapSet(map, key, getDefaultValue());
40
+ const map = Map;
41
+ const mapNew = (entries) => new map(entries);
42
+ const mapKeys = (map2) => [...(map2?.keys() ?? [])];
43
+ const mapGet = (map2, key) => map2?.get(key);
44
+ const mapForEach = (map2, cb) =>
45
+ collForEach(map2, (value, key) => cb(key, value));
46
+ const mapSet = (map2, key, value) =>
47
+ isUndefined(value) ? (collDel(map2, key), map2) : map2?.set(key, value);
48
+ const mapEnsure = (map2, key, getDefaultValue, hadExistingValue) => {
49
+ if (!collHas(map2, key)) {
50
+ mapSet(map2, key, getDefaultValue());
50
51
  } else {
51
- hadExistingValue?.(mapGet(map, key));
52
+ hadExistingValue?.(mapGet(map2, key));
52
53
  }
53
- return mapGet(map, key);
54
+ return mapGet(map2, key);
54
55
  };
55
56
  const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
56
57
  ifNotUndefined(
@@ -37,20 +37,21 @@ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
37
37
  const object = Object;
38
38
  const objFreeze = object.freeze;
39
39
 
40
- const mapNew = (entries) => new Map(entries);
41
- const mapKeys = (map) => [...(map?.keys() ?? [])];
42
- const mapGet = (map, key) => map?.get(key);
43
- const mapForEach = (map, cb) =>
44
- collForEach(map, (value, key) => cb(key, value));
45
- const mapSet = (map, key, value) =>
46
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
47
- const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
48
- if (!collHas(map, key)) {
49
- mapSet(map, key, getDefaultValue());
40
+ const map = Map;
41
+ const mapNew = (entries) => new map(entries);
42
+ const mapKeys = (map2) => [...(map2?.keys() ?? [])];
43
+ const mapGet = (map2, key) => map2?.get(key);
44
+ const mapForEach = (map2, cb) =>
45
+ collForEach(map2, (value, key) => cb(key, value));
46
+ const mapSet = (map2, key, value) =>
47
+ isUndefined(value) ? (collDel(map2, key), map2) : map2?.set(key, value);
48
+ const mapEnsure = (map2, key, getDefaultValue, hadExistingValue) => {
49
+ if (!collHas(map2, key)) {
50
+ mapSet(map2, key, getDefaultValue());
50
51
  } else {
51
- hadExistingValue?.(mapGet(map, key));
52
+ hadExistingValue?.(mapGet(map2, key));
52
53
  }
53
- return mapGet(map, key);
54
+ return mapGet(map2, key);
54
55
  };
55
56
  const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
56
57
  ifNotUndefined(
package/store/index.js CHANGED
@@ -140,31 +140,32 @@ const objValidate = (obj, validateChild, onInvalidObj, emptyIsValid = 0) => {
140
140
  return emptyIsValid ? true : !objIsEmpty(obj);
141
141
  };
142
142
 
143
- const mapNew = (entries) => new Map(entries);
144
- const mapKeys = (map) => [...(map?.keys() ?? [])];
145
- const mapGet = (map, key) => map?.get(key);
146
- const mapForEach = (map, cb) =>
147
- collForEach(map, (value, key) => cb(key, value));
143
+ const map = Map;
144
+ const mapNew = (entries) => new map(entries);
145
+ const mapKeys = (map2) => [...(map2?.keys() ?? [])];
146
+ const mapGet = (map2, key) => map2?.get(key);
147
+ const mapForEach = (map2, cb) =>
148
+ collForEach(map2, (value, key) => cb(key, value));
148
149
  const mapMap = (coll, cb) =>
149
150
  arrayMap([...(coll?.entries() ?? [])], ([key, value]) => cb(value, key));
150
- const mapSet = (map, key, value) =>
151
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
152
- const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
153
- if (!collHas(map, key)) {
154
- mapSet(map, key, getDefaultValue());
151
+ const mapSet = (map2, key, value) =>
152
+ isUndefined(value) ? (collDel(map2, key), map2) : map2?.set(key, value);
153
+ const mapEnsure = (map2, key, getDefaultValue, hadExistingValue) => {
154
+ if (!collHas(map2, key)) {
155
+ mapSet(map2, key, getDefaultValue());
155
156
  } else {
156
- hadExistingValue?.(mapGet(map, key));
157
+ hadExistingValue?.(mapGet(map2, key));
157
158
  }
158
- return mapGet(map, key);
159
+ return mapGet(map2, key);
159
160
  };
160
- const mapMatch = (map, obj, set, del = mapSet) => {
161
- objMap(obj, (value, id) => set(map, id, value));
162
- mapForEach(map, (id) => (objHas(obj, id) ? 0 : del(map, id)));
163
- return map;
161
+ const mapMatch = (map2, obj, set, del = mapSet) => {
162
+ objMap(obj, (value, id) => set(map2, id, value));
163
+ mapForEach(map2, (id) => (objHas(obj, id) ? 0 : del(map2, id)));
164
+ return map2;
164
165
  };
165
- const mapToObj = (map, valueMapper, excludeMapValue, excludeObjValue) => {
166
+ const mapToObj = (map2, valueMapper, excludeMapValue, excludeObjValue) => {
166
167
  const obj = {};
167
- collForEach(map, (mapValue, id) => {
168
+ collForEach(map2, (mapValue, id) => {
168
169
  if (!excludeMapValue?.(mapValue, id)) {
169
170
  const objValue = valueMapper ? valueMapper(mapValue, id) : mapValue;
170
171
  if (!excludeObjValue?.(objValue)) {
@@ -174,27 +175,27 @@ const mapToObj = (map, valueMapper, excludeMapValue, excludeObjValue) => {
174
175
  });
175
176
  return obj;
176
177
  };
177
- const mapToObj2 = (map, valueMapper, excludeMapValue) =>
178
+ const mapToObj2 = (map2, valueMapper, excludeMapValue) =>
178
179
  mapToObj(
179
- map,
180
+ map2,
180
181
  (childMap) => mapToObj(childMap, valueMapper, excludeMapValue),
181
182
  collIsEmpty,
182
183
  objIsEmpty,
183
184
  );
184
- const mapToObj3 = (map, valueMapper, excludeMapValue) =>
185
+ const mapToObj3 = (map2, valueMapper, excludeMapValue) =>
185
186
  mapToObj(
186
- map,
187
+ map2,
187
188
  (childMap) => mapToObj2(childMap, valueMapper, excludeMapValue),
188
189
  collIsEmpty,
189
190
  objIsEmpty,
190
191
  );
191
- const mapClone = (map, mapValue) => {
192
- const map2 = mapNew();
193
- collForEach(map, (value, key) => map2.set(key, mapValue?.(value) ?? value));
194
- return map2;
192
+ const mapClone = (map2, mapValue) => {
193
+ const map22 = mapNew();
194
+ collForEach(map2, (value, key) => map22.set(key, mapValue?.(value) ?? value));
195
+ return map22;
195
196
  };
196
- const mapClone2 = (map) => mapClone(map, mapClone);
197
- const mapClone3 = (map) => mapClone(map, mapClone2);
197
+ const mapClone2 = (map2) => mapClone(map2, mapClone);
198
+ const mapClone3 = (map2) => mapClone(map2, mapClone2);
198
199
  const visitTree = (node, path, ensureLeaf, pruneLeaf, p = 0) =>
199
200
  ifNotUndefined(
200
201
  (ensureLeaf ? mapEnsure : mapGet)(
@@ -340,6 +341,7 @@ const createStore = () => {
340
341
  let hadValues = false;
341
342
  let transactions = 0;
342
343
  let internalListeners = [];
344
+ let mutating = 0;
343
345
  const changedTableIds = mapNew();
344
346
  const changedTableCellIds = mapNew();
345
347
  const changedRowCount = mapNew();
@@ -744,13 +746,13 @@ const createStore = () => {
744
746
  cellId,
745
747
  () => [oldCell, 0],
746
748
  )[1] = newCell;
747
- internalListeners[3]?.(tableId, rowId, cellId, newCell);
749
+ internalListeners[3]?.(tableId, rowId, cellId, newCell, mutating);
748
750
  };
749
751
  const valueIdsChanged = (valueId, addedOrRemoved) =>
750
752
  idsChanged(changedValueIds, valueId, addedOrRemoved);
751
753
  const valueChanged = (valueId, oldValue, newValue) => {
752
754
  mapEnsure(changedValues, valueId, () => [oldValue, 0])[1] = newValue;
753
- internalListeners[4]?.(valueId, newValue);
755
+ internalListeners[4]?.(valueId, newValue, mutating);
754
756
  };
755
757
  const cellInvalid = (tableId, rowId, cellId, invalidCell, defaultedCell) => {
756
758
  arrayPush(
@@ -825,14 +827,11 @@ const createStore = () => {
825
827
  }
826
828
  };
827
829
  const callTabularListenersForChanges = (mutator) => {
828
- const hasTablesNow = hasTables();
829
- if (hasTablesNow != hadTables) {
830
- callListeners(hasTablesListeners[mutator], void 0, hasTablesNow);
831
- }
832
- const emptySortedRowIdListeners = collIsEmpty(
830
+ const hasHasTablesListeners = !collIsEmpty(hasTablesListeners[mutator]);
831
+ const hasSortedRowIdListeners = !collIsEmpty(
833
832
  sortedRowIdsListeners[mutator],
834
833
  );
835
- const emptyIdAndHasListeners =
834
+ const hasIdOrHasListeners = !(
836
835
  collIsEmpty(cellIdsListeners[mutator]) &&
837
836
  collIsEmpty(hasCellListeners[mutator]) &&
838
837
  collIsEmpty(rowIdsListeners[mutator]) &&
@@ -840,15 +839,17 @@ const createStore = () => {
840
839
  collIsEmpty(tableCellIdsListeners[mutator]) &&
841
840
  collIsEmpty(hasTableCellListeners[mutator]) &&
842
841
  collIsEmpty(rowCountListeners[mutator]) &&
843
- emptySortedRowIdListeners &&
842
+ !hasSortedRowIdListeners &&
844
843
  collIsEmpty(tableIdsListeners[mutator]) &&
845
- collIsEmpty(hasTableListeners[mutator]);
846
- const emptyOtherListeners =
844
+ collIsEmpty(hasTableListeners[mutator])
845
+ );
846
+ const hasOtherListeners = !(
847
847
  collIsEmpty(cellListeners[mutator]) &&
848
848
  collIsEmpty(rowListeners[mutator]) &&
849
849
  collIsEmpty(tableListeners[mutator]) &&
850
- collIsEmpty(tablesListeners[mutator]);
851
- if (!emptyIdAndHasListeners || !emptyOtherListeners) {
850
+ collIsEmpty(tablesListeners[mutator])
851
+ );
852
+ if (hasHasTablesListeners || hasIdOrHasListeners || hasOtherListeners) {
852
853
  const changes = mutator
853
854
  ? [
854
855
  mapClone(changedTableIds),
@@ -856,7 +857,9 @@ const createStore = () => {
856
857
  mapClone(changedRowCount),
857
858
  mapClone2(changedRowIds),
858
859
  mapClone3(changedCellIds),
859
- mapClone3(changedCells),
860
+ mapClone(changedCells, (map) =>
861
+ mapClone(map, (map2) => mapClone(map2, pairClone)),
862
+ ),
860
863
  ]
861
864
  : [
862
865
  changedTableIds,
@@ -866,7 +869,13 @@ const createStore = () => {
866
869
  changedCellIds,
867
870
  changedCells,
868
871
  ];
869
- if (!emptyIdAndHasListeners) {
872
+ if (hasHasTablesListeners) {
873
+ const hasTablesNow = hasTables();
874
+ if (hasTablesNow != hadTables) {
875
+ callListeners(hasTablesListeners[mutator], void 0, hasTablesNow);
876
+ }
877
+ }
878
+ if (hasIdOrHasListeners) {
870
879
  callIdsAndHasListenersIfChanged(
871
880
  changes[0],
872
881
  tableIdsListeners[mutator],
@@ -898,13 +907,13 @@ const createStore = () => {
898
907
  hasRowListeners[mutator],
899
908
  [tableId],
900
909
  ) &&
901
- !emptySortedRowIdListeners
910
+ hasSortedRowIdListeners
902
911
  ) {
903
912
  callListeners(sortedRowIdsListeners[mutator], [tableId, null]);
904
913
  setAdd(calledSortableTableIds, tableId);
905
914
  }
906
915
  });
907
- if (!emptySortedRowIdListeners) {
916
+ if (hasSortedRowIdListeners) {
908
917
  collForEach(changes[5], (rows, tableId) => {
909
918
  if (!collHas(calledSortableTableIds, tableId)) {
910
919
  const sortableCellIds = setNew();
@@ -935,7 +944,7 @@ const createStore = () => {
935
944
  ),
936
945
  );
937
946
  }
938
- if (!emptyOtherListeners) {
947
+ if (hasOtherListeners) {
939
948
  let tablesChanged;
940
949
  collForEach(changes[5], (rows, tableId) => {
941
950
  let tableChanged;
@@ -972,28 +981,31 @@ const createStore = () => {
972
981
  }
973
982
  };
974
983
  const callValuesListenersForChanges = (mutator) => {
975
- const hasValuesNow = hasValues();
976
- if (hasValuesNow != hadValues) {
977
- callListeners(hasValuesListeners[mutator], void 0, hasValuesNow);
978
- }
979
- const emptyIdAndHasListeners =
980
- collIsEmpty(valueIdsListeners[mutator]) &&
981
- collIsEmpty(hasValueListeners[mutator]);
982
- const emptyOtherListeners =
983
- collIsEmpty(valueListeners[mutator]) &&
984
- collIsEmpty(valuesListeners[mutator]);
985
- if (!emptyIdAndHasListeners || !emptyOtherListeners) {
984
+ const hasHasValuesListeners = !collIsEmpty(hasValuesListeners[mutator]);
985
+ const hasIdOrHasListeners =
986
+ !collIsEmpty(valueIdsListeners[mutator]) ||
987
+ !collIsEmpty(hasValueListeners[mutator]);
988
+ const hasOtherListeners =
989
+ !collIsEmpty(valueListeners[mutator]) ||
990
+ !collIsEmpty(valuesListeners[mutator]);
991
+ if (hasHasValuesListeners || hasIdOrHasListeners || hasOtherListeners) {
986
992
  const changes = mutator
987
- ? [mapClone(changedValueIds), mapClone(changedValues)]
993
+ ? [mapClone(changedValueIds), mapClone(changedValues, pairClone)]
988
994
  : [changedValueIds, changedValues];
989
- if (!emptyIdAndHasListeners) {
995
+ if (hasHasValuesListeners) {
996
+ const hasValuesNow = hasValues();
997
+ if (hasValuesNow != hadValues) {
998
+ callListeners(hasValuesListeners[mutator], void 0, hasValuesNow);
999
+ }
1000
+ }
1001
+ if (hasIdOrHasListeners) {
990
1002
  callIdsAndHasListenersIfChanged(
991
1003
  changes[0],
992
1004
  valueIdsListeners[mutator],
993
1005
  hasValueListeners[mutator],
994
1006
  );
995
1007
  }
996
- if (!emptyOtherListeners) {
1008
+ if (hasOtherListeners) {
997
1009
  let valuesChanged;
998
1010
  collForEach(changes[1], ([oldValue, newValue], valueId) => {
999
1011
  if (newValue !== oldValue) {
@@ -1390,6 +1402,7 @@ const createStore = () => {
1390
1402
  transactions--;
1391
1403
  if (transactions == 0) {
1392
1404
  transactions = 1;
1405
+ mutating = 1;
1393
1406
  callInvalidCellListeners(1);
1394
1407
  if (!collIsEmpty(changedCells)) {
1395
1408
  callTabularListenersForChanges(1);
@@ -1398,6 +1411,7 @@ const createStore = () => {
1398
1411
  if (!collIsEmpty(changedValues)) {
1399
1412
  callValuesListenersForChanges(1);
1400
1413
  }
1414
+ mutating = 0;
1401
1415
  if (doRollback?.(store)) {
1402
1416
  collForEach(changedCells, (table, tableId) =>
1403
1417
  collForEach(table, (row, rowId) =>