tinybase 8.0.0-beta.3 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/@types/middleware/index.d.ts +44 -124
  2. package/@types/middleware/with-schemas/index.d.ts +44 -147
  3. package/@types/ui-react-dom/index.d.ts +6 -4
  4. package/@types/ui-react-dom/with-schemas/index.d.ts +6 -4
  5. package/index.js +100 -69
  6. package/mergeable-store/index.js +99 -48
  7. package/mergeable-store/with-schemas/index.js +99 -48
  8. package/middleware/index.js +1 -39
  9. package/middleware/with-schemas/index.js +1 -39
  10. package/min/index.js +1 -1
  11. package/min/index.js.gz +0 -0
  12. package/min/mergeable-store/index.js +1 -1
  13. package/min/mergeable-store/index.js.gz +0 -0
  14. package/min/mergeable-store/with-schemas/index.js +1 -1
  15. package/min/mergeable-store/with-schemas/index.js.gz +0 -0
  16. package/min/middleware/index.js +1 -1
  17. package/min/middleware/index.js.gz +0 -0
  18. package/min/middleware/with-schemas/index.js +1 -1
  19. package/min/middleware/with-schemas/index.js.gz +0 -0
  20. package/min/omni/index.js +1 -1
  21. package/min/omni/index.js.gz +0 -0
  22. package/min/omni/with-schemas/index.js +1 -1
  23. package/min/omni/with-schemas/index.js.gz +0 -0
  24. package/min/store/index.js +1 -1
  25. package/min/store/index.js.gz +0 -0
  26. package/min/store/with-schemas/index.js +1 -1
  27. package/min/store/with-schemas/index.js.gz +0 -0
  28. package/min/ui-react-dom/index.js +1 -1
  29. package/min/ui-react-dom/index.js.gz +0 -0
  30. package/min/ui-react-dom/with-schemas/index.js +1 -1
  31. package/min/ui-react-dom/with-schemas/index.js.gz +0 -0
  32. package/min/ui-react-inspector/index.js +1 -1
  33. package/min/ui-react-inspector/index.js.gz +0 -0
  34. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  35. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  36. package/min/with-schemas/index.js +1 -1
  37. package/min/with-schemas/index.js.gz +0 -0
  38. package/omni/index.js +156 -102
  39. package/omni/with-schemas/index.js +156 -102
  40. package/package.json +6 -6
  41. package/readme.md +3 -3
  42. package/releases.md +39 -4
  43. package/store/index.js +99 -48
  44. package/store/with-schemas/index.js +99 -48
  45. package/ui-react-dom/index.js +56 -34
  46. package/ui-react-dom/with-schemas/index.js +56 -34
  47. package/ui-react-inspector/index.js +155 -82
  48. package/ui-react-inspector/with-schemas/index.js +155 -82
  49. package/with-schemas/index.js +100 -69
package/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- <link rel="preload" as="image" href="https://beta.tinybase.org/react.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/indexeddb.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/browser.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/cloudflare.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/postgresql.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/pglite.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/sqlite.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/bun.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/expo.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/electric.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/turso.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/powersync.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/partykit.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/yjs.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/crsqlite.png"><link rel="preload" as="image" href="https://beta.tinybase.org/automerge.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/zod.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/typebox.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/valibot.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/arktype.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/yup.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/effect.svg?asImg"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="https://img.shields.io/badge/Bluesky-Follow-blue?style=for-the-badge&amp;logo=bluesky&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%230285FF"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="https://img.shields.io/npm/v/tinybase?style=for-the-badge&amp;logo=npm&amp;logoColor=%23fff&amp;labelColor=%23bd0005&amp;color=%23333"><link rel="preload" as="image" href="https://beta.tinybase.org/ui-react-dom.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/inspector.webp"><link rel="preload" as="image" href="https://github.com/fastrepl.png?size=48"><link rel="preload" as="image" href="https://github.com/ComputelessComputer.png?size=48"><link rel="preload" as="image" href="https://github.com/cancelself.png?size=48"><link rel="preload" as="image" href="https://github.com/expo.png?size=48"><link rel="preload" as="image" href="https://github.com/braden-w.png?size=48"><link rel="preload" as="image" href="https://github.com/dylmye.png?size=48"><link rel="preload" as="image" href="https://github.com/gonza224.png?size=48"><link rel="preload" as="image" href="https://github.com/cpojer.png?size=48"><link rel="preload" as="image" href="https://github.com/beekeeb.png?size=48"><link rel="preload" as="image" href="https://github.com/WonderPanda.png?size=48"><link rel="preload" as="image" href="https://github.com/arpitBhalla.png?size=48"><link rel="preload" as="image" href="https://github.com/behrends.png?size=48"><link rel="preload" as="image" href="https://github.com/betomoedano.png?size=48"><link rel="preload" as="image" href="https://github.com/brentvatne.png?size=48"><link rel="preload" as="image" href="https://github.com/byCedric.png?size=48"><link rel="preload" as="image" href="https://github.com/circadian-risk.png?size=48"><link rel="preload" as="image" href="https://github.com/cubecull.png?size=48"><link rel="preload" as="image" href="https://github.com/erwinkn.png?size=48"><link rel="preload" as="image" href="https://github.com/ezra-en.png?size=48"><link rel="preload" as="image" href="https://github.com/feychenie.png?size=48"><link rel="preload" as="image" href="https://github.com/flaming-codes.png?size=48"><link rel="preload" as="image" href="https://github.com/fostertheweb.png?size=48"><link rel="preload" as="image" href="https://github.com/Giulio987.png?size=48"><link rel="preload" as="image" href="https://github.com/hi-ogawa.png?size=48"><link rel="preload" as="image" href="https://github.com/itsdevcoffee.png?size=48"><link rel="preload" as="image" href="https://github.com/jbolda.png?size=48"><link rel="preload" as="image" href="https://github.com/Kayoo-asso.png?size=48"><link rel="preload" as="image" href="https://github.com/kotofurumiya.png?size=48"><link rel="preload" as="image" href="https://github.com/Kudo.png?size=48"><link rel="preload" as="image" href="https://github.com/learn-anything.png?size=48"><link rel="preload" as="image" href="https://github.com/lluc.png?size=48"><link rel="preload" as="image" href="https://github.com/marksteve.png?size=48"><link rel="preload" as="image" href="https://github.com/miking-the-viking.png?size=48"><link rel="preload" as="image" href="https://github.com/mjamesderocher.png?size=48"><link rel="preload" as="image" href="https://github.com/mouktardev.png?size=48"><link rel="preload" as="image" href="https://github.com/nickmessing.png?size=48"><link rel="preload" as="image" href="https://github.com/nikitavoloboev.png?size=48"><link rel="preload" as="image" href="https://github.com/nkzw-tech.png?size=48"><link rel="preload" as="image" href="https://github.com/palerdot.png?size=48"><link rel="preload" as="image" href="https://github.com/PorcoRosso85.png?size=48"><link rel="preload" as="image" href="https://github.com/primodiumxyz.png?size=48"><link rel="preload" as="image" href="https://github.com/shaneosullivan.png?size=48"><link rel="preload" as="image" href="https://github.com/sudo-self.png?size=48"><link rel="preload" as="image" href="https://github.com/SuperSonicHub1.png?size=48"><link rel="preload" as="image" href="https://github.com/threepointone.png?size=48"><link rel="preload" as="image" href="https://github.com/uptonking.png?size=48"><link rel="preload" as="image" href="https://github.com/ViktorZhurbin.png?size=48"><link rel="preload" as="image" href="https://github.com/wilkerlucio.png?size=48"><link rel="preload" as="image" href="https://synclets.org/favicon.svg?asImg"><link rel="preload" as="image" href="https://tinywidgets.org/favicon.svg?asImg"><link rel="preload" as="image" href="https://tinytick.org/favicon.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/youtube.webp"><section id="hero"><h2 id="a-reactive-data-store-sync-engine">A <em>reactive</em> data store &amp; <span><em>sync</em> engine</span></h2></section><p><a href="https://beta.tinybase.org/guides/releases/#v8-0"><em>NEW!</em> v8.0 release</a></p><p><span id="one-with">&quot;The one with middleware!&quot;</span></p><p><a class="start" href="https://beta.tinybase.org/guides/the-basics/getting-started/">Get started</a></p><p><a href="https://beta.tinybase.org/demos/">Try the demos</a></p><p><a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/">Read the docs</a></p><hr><section><h2 id="let-s-build-local-first-apps">Let&#x27;s build <em>local-first</em> apps</h2><p>Create a todo list, a chat app, a drawing tool, or a tic-tac-toe game - with sync &amp; persistence! - in less than 60 seconds.</p></section>
1
+ <link rel="preload" as="image" href="https://beta.tinybase.org/react.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/indexeddb.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/browser.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/cloudflare.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/postgresql.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/pglite.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/sqlite.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/bun.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/expo.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/electric.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/turso.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/powersync.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/partykit.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/yjs.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/crsqlite.png"><link rel="preload" as="image" href="https://beta.tinybase.org/automerge.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/zod.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/typebox.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/valibot.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/arktype.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/yup.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/effect.svg?asImg"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="https://img.shields.io/badge/Bluesky-Follow-blue?style=for-the-badge&amp;logo=bluesky&amp;logoColor=%23fff&amp;color=%23333&amp;labelColor=%230285FF"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="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"><link rel="preload" as="image" href="https://img.shields.io/npm/v/tinybase?style=for-the-badge&amp;logo=npm&amp;logoColor=%23fff&amp;labelColor=%23bd0005&amp;color=%23333"><link rel="preload" as="image" href="https://beta.tinybase.org/ui-react-dom.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/inspector.webp"><link rel="preload" as="image" href="https://github.com/fastrepl.png?size=48"><link rel="preload" as="image" href="https://github.com/ComputelessComputer.png?size=48"><link rel="preload" as="image" href="https://github.com/cancelself.png?size=48"><link rel="preload" as="image" href="https://github.com/expo.png?size=48"><link rel="preload" as="image" href="https://github.com/braden-w.png?size=48"><link rel="preload" as="image" href="https://github.com/dylmye.png?size=48"><link rel="preload" as="image" href="https://github.com/gonza224.png?size=48"><link rel="preload" as="image" href="https://github.com/cpojer.png?size=48"><link rel="preload" as="image" href="https://github.com/beekeeb.png?size=48"><link rel="preload" as="image" href="https://github.com/WonderPanda.png?size=48"><link rel="preload" as="image" href="https://github.com/arpitBhalla.png?size=48"><link rel="preload" as="image" href="https://github.com/behrends.png?size=48"><link rel="preload" as="image" href="https://github.com/betomoedano.png?size=48"><link rel="preload" as="image" href="https://github.com/brentvatne.png?size=48"><link rel="preload" as="image" href="https://github.com/byCedric.png?size=48"><link rel="preload" as="image" href="https://github.com/circadian-risk.png?size=48"><link rel="preload" as="image" href="https://github.com/cubecull.png?size=48"><link rel="preload" as="image" href="https://github.com/erwinkn.png?size=48"><link rel="preload" as="image" href="https://github.com/ezra-en.png?size=48"><link rel="preload" as="image" href="https://github.com/feychenie.png?size=48"><link rel="preload" as="image" href="https://github.com/flaming-codes.png?size=48"><link rel="preload" as="image" href="https://github.com/fostertheweb.png?size=48"><link rel="preload" as="image" href="https://github.com/Giulio987.png?size=48"><link rel="preload" as="image" href="https://github.com/hi-ogawa.png?size=48"><link rel="preload" as="image" href="https://github.com/itsdevcoffee.png?size=48"><link rel="preload" as="image" href="https://github.com/jbolda.png?size=48"><link rel="preload" as="image" href="https://github.com/Kayoo-asso.png?size=48"><link rel="preload" as="image" href="https://github.com/kotofurumiya.png?size=48"><link rel="preload" as="image" href="https://github.com/Kudo.png?size=48"><link rel="preload" as="image" href="https://github.com/learn-anything.png?size=48"><link rel="preload" as="image" href="https://github.com/lluc.png?size=48"><link rel="preload" as="image" href="https://github.com/marksteve.png?size=48"><link rel="preload" as="image" href="https://github.com/miking-the-viking.png?size=48"><link rel="preload" as="image" href="https://github.com/mjamesderocher.png?size=48"><link rel="preload" as="image" href="https://github.com/mouktardev.png?size=48"><link rel="preload" as="image" href="https://github.com/nickmessing.png?size=48"><link rel="preload" as="image" href="https://github.com/nikitavoloboev.png?size=48"><link rel="preload" as="image" href="https://github.com/nkzw-tech.png?size=48"><link rel="preload" as="image" href="https://github.com/palerdot.png?size=48"><link rel="preload" as="image" href="https://github.com/PorcoRosso85.png?size=48"><link rel="preload" as="image" href="https://github.com/primodiumxyz.png?size=48"><link rel="preload" as="image" href="https://github.com/shaneosullivan.png?size=48"><link rel="preload" as="image" href="https://github.com/sudo-self.png?size=48"><link rel="preload" as="image" href="https://github.com/SuperSonicHub1.png?size=48"><link rel="preload" as="image" href="https://github.com/threepointone.png?size=48"><link rel="preload" as="image" href="https://github.com/uptonking.png?size=48"><link rel="preload" as="image" href="https://github.com/ViktorZhurbin.png?size=48"><link rel="preload" as="image" href="https://github.com/wilkerlucio.png?size=48"><link rel="preload" as="image" href="https://synclets.org/favicon.svg?asImg"><link rel="preload" as="image" href="https://tinywidgets.org/favicon.svg?asImg"><link rel="preload" as="image" href="https://tinytick.org/favicon.svg?asImg"><link rel="preload" as="image" href="https://beta.tinybase.org/youtube.webp"><section id="hero"><h2 id="a-reactive-data-store-sync-engine">A <em>reactive</em> data store &amp; <span><em>sync</em> engine</span></h2></section><p><a href="https://beta.tinybase.org/guides/releases/#v8-0"><em>NEW!</em> v8.0 release</a></p><p><span id="one-with">&quot;The one with objects, arrays &amp; middleware!&quot;</span></p><p><a class="start" href="https://beta.tinybase.org/guides/the-basics/getting-started/">Get started</a></p><p><a href="https://beta.tinybase.org/demos/">Try the demos</a></p><p><a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/">Read the docs</a></p><hr><section><h2 id="let-s-build-local-first-apps">Let&#x27;s build <em>local-first</em> apps</h2><p>Create a todo list, a chat app, a drawing tool, or a tic-tac-toe game - with sync &amp; persistence! - in less than 60 seconds.</p></section>
2
2
 
3
3
  ```bash
4
4
  > npm create tinybase@latest
@@ -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://beta.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://beta.tinybase.org/api/persister-browser">browser storage</a>, <a href="https://beta.tinybase.org/api/persister-indexed-db">IndexedDB</a>, <a href="https://beta.tinybase.org/guides/persistence/database-persistence/">SQLite or PostgreSQL databases</a>, and <a href="https://beta.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">6.0kB - 13.1kB</a> and with no dependencies - yet <a href="#well-tested-and-documented">100% tested</a>, <a href="https://beta.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://beta.tinybase.org/guides/building-uis/getting-started-with-ui-react"><img src="https://beta.tinybase.org/react.svg?asImg" width="48"> React</a></div><div><a href="https://beta.tinybase.org/api/persister-indexed-db/functions/creation/createindexeddbpersister"><img src="https://beta.tinybase.org/indexeddb.svg?asImg" width="48"> IndexedDB</a></div><div><a href="https://beta.tinybase.org/api/persister-browser"><img src="https://beta.tinybase.org/browser.svg?asImg" width="48"> OPFS</a></div><div><a href="https://beta.tinybase.org/guides/integrations/cloudflare-durable-objects"><img src="https://beta.tinybase.org/cloudflare.svg?asImg" width="48"> Cloudflare</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/postgresql.svg?asImg" width="48"> PostgreSQL</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/pglite.svg?asImg" width="48"> PGlite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/sqlite.svg?asImg" width="48"> SQLite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/bun.svg?asImg" width="48"> Bun SQLite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/expo.svg?asImg" width="48"> Expo SQLite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/electric.svg?asImg" width="48"> ElectricSQL</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/turso.svg?asImg" width="48"> Turso</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/powersync.svg?asImg" width="48"> PowerSync</a></div><div><a href="https://beta.tinybase.org/api/persister-partykit-client"><img src="https://beta.tinybase.org/partykit.svg?asImg" width="48"> PartyKit</a></div><div><a href="https://beta.tinybase.org/api/persister-yjs/functions/creation/createyjspersister"><img src="https://beta.tinybase.org/yjs.svg?asImg" width="48"> YJS</a></div><div><a href="https://beta.tinybase.org/api/persister-cr-sqlite-wasm"><img src="https://beta.tinybase.org/crsqlite.png" width="48"> CR-SQLite</a></div><div><a href="https://beta.tinybase.org/api/persister-automerge"><img src="https://beta.tinybase.org/automerge.svg?asImg" width="48"> Automerge</a></div><div><a href="https://beta.tinybase.org/api/schematizer-zod/functions/creation/createzodschematizer"><img src="https://beta.tinybase.org/zod.svg?asImg" width="48"> Zod</a></div><div><a href="https://beta.tinybase.org/api/schematizer-typebox/functions/creation/createtypeboxschematizer"><img src="https://beta.tinybase.org/typebox.svg?asImg" width="48"> TypeBox</a></div><div><a href="https://beta.tinybase.org/api/schematizer-valibot/functions/creation/createvalibotschematizer"><img src="https://beta.tinybase.org/valibot.svg?asImg" width="48"> Valibot</a></div><div><a href="https://beta.tinybase.org/api/schematizer-arktype/functions/creation/createarktypeschematizer"><img src="https://beta.tinybase.org/arktype.svg?asImg" width="48"> ArkType</a></div><div><a href="https://beta.tinybase.org/api/schematizer-yup/functions/creation/createyupschematizer"><img src="https://beta.tinybase.org/yup.svg?asImg" width="48"> Yup</a></div><div><a href="https://beta.tinybase.org/api/schematizer-effect/functions/creation/createeffectschematizer"><img src="https://beta.tinybase.org/effect.svg?asImg" width="48"> Effect</a></div><p>(Baffled by all these logos? Check out our <a href="https://beta.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/8.0.0-beta.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://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> requires just a simple call to the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/createstore/"><code>createStore</code></a> function. Once you have one, you can easily set <a href="https://beta.tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> in it by unique <a href="https://beta.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://beta.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://beta.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://beta.tinybase.org/api/persister-browser">browser storage</a>, <a href="https://beta.tinybase.org/api/persister-indexed-db">IndexedDB</a>, <a href="https://beta.tinybase.org/guides/persistence/database-persistence/">SQLite or PostgreSQL databases</a>, and <a href="https://beta.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">6.2kB - 13.2kB</a> and with no dependencies - yet <a href="#well-tested-and-documented">100% tested</a>, <a href="https://beta.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://beta.tinybase.org/guides/building-uis/getting-started-with-ui-react"><img src="https://beta.tinybase.org/react.svg?asImg" width="48"> React</a></div><div><a href="https://beta.tinybase.org/api/persister-indexed-db/functions/creation/createindexeddbpersister"><img src="https://beta.tinybase.org/indexeddb.svg?asImg" width="48"> IndexedDB</a></div><div><a href="https://beta.tinybase.org/api/persister-browser"><img src="https://beta.tinybase.org/browser.svg?asImg" width="48"> OPFS</a></div><div><a href="https://beta.tinybase.org/guides/integrations/cloudflare-durable-objects"><img src="https://beta.tinybase.org/cloudflare.svg?asImg" width="48"> Cloudflare</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/postgresql.svg?asImg" width="48"> PostgreSQL</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/pglite.svg?asImg" width="48"> PGlite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/sqlite.svg?asImg" width="48"> SQLite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/bun.svg?asImg" width="48"> Bun SQLite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/expo.svg?asImg" width="48"> Expo SQLite</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/electric.svg?asImg" width="48"> ElectricSQL</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/turso.svg?asImg" width="48"> Turso</a></div><div><a href="https://beta.tinybase.org/guides/schemas-and-persistence/database-persistence"><img src="https://beta.tinybase.org/powersync.svg?asImg" width="48"> PowerSync</a></div><div><a href="https://beta.tinybase.org/api/persister-partykit-client"><img src="https://beta.tinybase.org/partykit.svg?asImg" width="48"> PartyKit</a></div><div><a href="https://beta.tinybase.org/api/persister-yjs/functions/creation/createyjspersister"><img src="https://beta.tinybase.org/yjs.svg?asImg" width="48"> YJS</a></div><div><a href="https://beta.tinybase.org/api/persister-cr-sqlite-wasm"><img src="https://beta.tinybase.org/crsqlite.png" width="48"> CR-SQLite</a></div><div><a href="https://beta.tinybase.org/api/persister-automerge"><img src="https://beta.tinybase.org/automerge.svg?asImg" width="48"> Automerge</a></div><div><a href="https://beta.tinybase.org/api/schematizer-zod/functions/creation/createzodschematizer"><img src="https://beta.tinybase.org/zod.svg?asImg" width="48"> Zod</a></div><div><a href="https://beta.tinybase.org/api/schematizer-typebox/functions/creation/createtypeboxschematizer"><img src="https://beta.tinybase.org/typebox.svg?asImg" width="48"> TypeBox</a></div><div><a href="https://beta.tinybase.org/api/schematizer-valibot/functions/creation/createvalibotschematizer"><img src="https://beta.tinybase.org/valibot.svg?asImg" width="48"> Valibot</a></div><div><a href="https://beta.tinybase.org/api/schematizer-arktype/functions/creation/createarktypeschematizer"><img src="https://beta.tinybase.org/arktype.svg?asImg" width="48"> ArkType</a></div><div><a href="https://beta.tinybase.org/api/schematizer-yup/functions/creation/createyupschematizer"><img src="https://beta.tinybase.org/yup.svg?asImg" width="48"> Yup</a></div><div><a href="https://beta.tinybase.org/api/schematizer-effect/functions/creation/createeffectschematizer"><img src="https://beta.tinybase.org/effect.svg?asImg" width="48"> Effect</a></div><p>(Baffled by all these logos? Check out our <a href="https://beta.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/8.0.0-beta.4" 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://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> requires just a simple call to the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/createstore/"><code>createStore</code></a> function. Once you have one, you can easily set <a href="https://beta.tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> in it by unique <a href="https://beta.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://beta.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://beta.tinybase.org/api/store/"><code>store</code></a> module alone, you&#x27;ll only add a gzipped <em>6.0kB</em> to your app. Incrementally add the other modules as you need more functionality, or get it all for <em>13.1kB</em>.</p><p>The optional <a href="https://beta.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://beta.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://beta.tinybase.org/api/store/">tinybase/store</a> (minimal)</th><td>6.0kB</td><td>62.5kB</td></tr><tr><th class="right">tinybase (complete)</th><td>13.1kB</td><td>139.7kB</td></tr><tr><th class="right"><a href="https://beta.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://beta.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://beta.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,596</td><td>2,596</td><td>100.0%</td></tr><tr><th class="right">Statements</th><td>2,820</td><td>2,820</td><td>100.0%</td></tr><tr><th class="right">Functions</th><td>1,146</td><td>1,146</td><td>100.0%</td></tr><tr><th class="right">Branches</th><td>974</td><td>974</td><td>100.0%</td></tr><tr><th class="right">Tests</th><td colspan="3">7,665</td></tr><tr><th class="right">Assertions</th><td colspan="3">32,897</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/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/expo" target="_blank"><img src="https://github.com/expo.png?size=48" title="expo" width="48" height="48"></a><a href="https://github.com/braden-w" target="_blank"><img src="https://github.com/braden-w.png?size=48" title="braden-w" 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/gonza224" target="_blank"><img src="https://github.com/gonza224.png?size=48" title="gonza224" 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://beta.tinybase.org/guides/the-basics/getting-started/">Get started</a></p><p><a href="https://beta.tinybase.org/demos/">Try the demos</a></p><p><a href="https://beta.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://beta.tinybase.org/guides/how-tinybase-is-built/credits/#giants">projects</a> and <a href="https://beta.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://beta.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://beta.tinybase.org/api/store/"><code>store</code></a> module alone, you&#x27;ll only add a gzipped <em>6.2kB</em> to your app. Incrementally add the other modules as you need more functionality, or get it all for <em>13.2kB</em>.</p><p>The optional <a href="https://beta.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>4.0kB</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://beta.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://beta.tinybase.org/api/store/">tinybase/store</a> (minimal)</th><td>6.2kB</td><td>65.4kB</td></tr><tr><th class="right">tinybase (complete)</th><td>13.2kB</td><td>142.7kB</td></tr><tr><th class="right"><a href="https://beta.tinybase.org/api/ui-react/">ui-react</a></th><td>5.5kB</td><td>60.0kB</td></tr><tr><th class="right"><a href="https://beta.tinybase.org/api/ui-react-dom/">ui-react-dom</a></th><td>4.0kB</td><td>36.5kB</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://beta.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,614</td><td>2,613</td><td>100.0%</td></tr><tr><th class="right">Statements</th><td>2,840</td><td>2,839</td><td>100.0%</td></tr><tr><th class="right">Functions</th><td>1,157</td><td>1,156</td><td>99.9%</td></tr><tr><th class="right">Branches</th><td>1,008</td><td>1,008</td><td>100.0%</td></tr><tr><th class="right">Tests</th><td colspan="3">7,607</td></tr><tr><th class="right">Assertions</th><td colspan="3">32,746</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/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/expo" target="_blank"><img src="https://github.com/expo.png?size=48" title="expo" width="48" height="48"></a><a href="https://github.com/braden-w" target="_blank"><img src="https://github.com/braden-w.png?size=48" title="braden-w" 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/gonza224" target="_blank"><img src="https://github.com/gonza224.png?size=48" title="gonza224" 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://beta.tinybase.org/guides/the-basics/getting-started/">Get started</a></p><p><a href="https://beta.tinybase.org/demos/">Try the demos</a></p><p><a href="https://beta.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://beta.tinybase.org/guides/how-tinybase-is-built/credits/#giants">projects</a> and <a href="https://beta.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://beta.tinybase.org/youtube.webp"></a></section>
package/releases.md CHANGED
@@ -1,8 +1,43 @@
1
- <link rel="preload" as="image" href="https://beta.tinybase.org/inspector.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/partykit.gif"><link rel="preload" as="image" href="https://beta.tinybase.org/ui-react-dom.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/store-inspector.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/car-analysis.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/movie-database.webp"><p>This is a reverse chronological list of the major TinyBase releases, with highlighted features.</p><hr><h1 id="v8-0">v8.0</h1><h2 id="introducing-middleware">Introducing <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a></h2><p>This release introduces a powerful new system for intercepting and transforming data as it flows into your TinyBase <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>. <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> callbacks can be registered to fire before data is written to the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>, allowing you to modify, validate, or even reject changes before they take effect.</p>
1
+ <link rel="preload" as="image" href="https://beta.tinybase.org/inspector.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/partykit.gif"><link rel="preload" as="image" href="https://beta.tinybase.org/ui-react-dom.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/store-inspector.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/car-analysis.webp"><link rel="preload" as="image" href="https://beta.tinybase.org/movie-database.webp"><p>This is a reverse chronological list of the major TinyBase releases, with highlighted features.</p><hr><h1 id="v8-0">v8.0</h1><h2 id="object-and-array-types">Object And Array Types</h2><p>This release also extends the range of types that a <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> or <a href="https://beta.tinybase.org/api/store/type-aliases/store/value/"><code>Value</code></a> can hold. Previously, TinyBase supported <code>string</code>, <code>number</code>, <code>boolean</code>, and (since v7.0) <code>null</code>. Now you can also store plain JavaScript <strong>objects</strong> and <strong>arrays</strong> directly in a <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>.</p>
2
+
3
+ ```js
4
+ import {createStore} from 'tinybase';
5
+
6
+ const store = createStore().setRow('pets', 'fido', {
7
+ species: 'dog',
8
+ traits: {friendly: true, energetic: true},
9
+ vaccinations: ['rabies', 'distemper', 'parvovirus'],
10
+ });
11
+
12
+ console.log(store.getCell('pets', 'fido', 'traits'));
13
+ // -> {friendly: true, energetic: true}
14
+
15
+ console.log(store.getCell('pets', 'fido', 'vaccinations'));
16
+ // -> ['rabies', 'distemper', 'parvovirus']
17
+ ```
18
+
19
+ <p>Internally, objects and arrays are stored as JSON-encoded strings with a special prefix character, so they round-trip transparently through persistence layers. But in and out of your application code, the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> API always expects or returns them as native JavaScript values.</p><p><a href="https://beta.tinybase.org/guides/schemas/">Schemas</a> also gain <code>object</code> and <code>array</code> as valid types:</p>
20
+
21
+ ```js
22
+ store.setTablesSchema({
23
+ pets: {
24
+ species: {type: 'string'},
25
+ traits: {type: 'object', default: {}},
26
+ vaccinations: {type: 'array', default: []},
27
+ },
28
+ });
29
+
30
+ store.setRow('pets', 'fido', {species: 'dog'});
31
+ console.log(store.getRow('pets', 'fido'));
32
+ // -> {species: 'dog', traits: {}, vaccinations: []}
33
+
34
+ store.delSchema();
35
+ ```
36
+
37
+ <p>Note that TinyBase does not deeply validate the contents of objects or arrays when a schema is applied - it simply checks that the value is of the right top-level type. For deeper validation, consider combining with a <a href="https://beta.tinybase.org/guides/the-basics/using-middleware/">Middleware</a> callback. On which note...</p><h2 id="introducing-middleware">Introducing <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a></h2><p>This release introduces a powerful new system for intercepting and transforming data as it flows into your TinyBase <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>. <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> callbacks can be registered to fire before data is written to the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>, allowing you to modify, validate, or even reject changes before they take effect.</p>
2
38
 
3
39
  ```jsx
4
- import {createMiddleware, createStore} from 'tinybase';
5
- const store = createStore();
40
+ import {createMiddleware} from 'tinybase';
6
41
 
7
42
  const middleware = createMiddleware(store);
8
43
  middleware.addWillSetCellCallback((tableId, rowId, cellId, cell) => {
@@ -15,7 +50,7 @@ middleware.addWillSetCellCallback((tableId, rowId, cellId, cell) => {
15
50
  });
16
51
  ```
17
52
 
18
- <p>Read more in our new comprehensive <a href="https://beta.tinybase.org/guides/using-middleware/">Using Middleware</a> guide, which includes examples of using <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> for data validation, transformation, and more.</p><p><a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> complements listeners but is distinct in that it runs before changes are applied to the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>, whereas listeners run after. <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> can modify the data that listeners see, and can prevent changes from being applied at all by returning <code>undefined</code>. In conjunction with schemas, <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> provides a powerful way to enforce data integrity and implement complex data transformations - and it should work in synchronization environments too.</p><p>As well as intercepting changes with the <code>WillSet*</code> callbacks, <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> can also provide a post-transaction <a href="https://beta.tinybase.org/api/middleware/type-aliases/callback/didsetrowcallback/"><code>DidSetRowCallback</code></a> callback. This fires once per changed <a href="https://beta.tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> after all writes in a transaction have settled, including writes made by mutating listeners, hence providing you ultimate definitive control over the rows written to a <a href="https://beta.tinybase.org/api/store/type-aliases/store/table/"><code>Table</code></a>.</p><p>Many, many thanks to <a href="https://github.com/bitmage">Brandon Mason</a> for designing and implementing this concept. Despite the major version number, we trust there are no breaking changes in this release. But please let us know if you find any!</p><hr><h1 id="v7-3">v7.3</h1><h2 id="introducing-state-hooks">Introducing State Hooks</h2><p>This release introduces a new family of convenience hooks that follow React&#x27;s <code>useState</code> pattern, making it even easier to read and write TinyBase data in your React components.</p><p>Each state hook returns a tuple containing both the current value and a setter function, eliminating the need to use separate getter and setter hooks.</p><p>State hooks combine the functionality of getter hooks (like the <a href="https://beta.tinybase.org/api/the-essentials/using-react/userow/"><code>useRow</code></a> hook) and setter callback hooks (like the <a href="https://beta.tinybase.org/api/ui-react/functions/store-hooks/usesetrowcallback/"><code>useSetRowCallback</code></a> hook) into a single, convenient API that feels just like React&#x27;s <code>useState</code>:</p>
53
+ <p>Read more in our new comprehensive <a href="https://beta.tinybase.org/guides/using-middleware/">Using Middleware</a> guide, which includes examples of using <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> for data validation, transformation, and more.</p><p><a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> complements listeners but is distinct in that it runs before changes are applied to the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>, whereas listeners run after. <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> can modify the data that listeners see, and can prevent changes from being applied at all by returning <code>undefined</code>. In conjunction with schemas, <a href="https://beta.tinybase.org/api/middleware/interfaces/middleware/middleware/"><code>Middleware</code></a> provides a powerful way to enforce data integrity and implement complex data transformations - and it should work in synchronization environments too.</p><p>Many, many thanks to <a href="https://github.com/bitmage">Brandon Mason</a> for designing and implementing this concept. Despite the major version number, we trust there are no breaking changes in this release. But please let us know if you find any!</p><hr><h1 id="v7-3">v7.3</h1><h2 id="introducing-state-hooks">Introducing State Hooks</h2><p>This release introduces a new family of convenience hooks that follow React&#x27;s <code>useState</code> pattern, making it even easier to read and write TinyBase data in your React components.</p><p>Each state hook returns a tuple containing both the current value and a setter function, eliminating the need to use separate getter and setter hooks.</p><p>State hooks combine the functionality of getter hooks (like the <a href="https://beta.tinybase.org/api/the-essentials/using-react/userow/"><code>useRow</code></a> hook) and setter callback hooks (like the <a href="https://beta.tinybase.org/api/ui-react/functions/store-hooks/usesetrowcallback/"><code>useSetRowCallback</code></a> hook) into a single, convenient API that feels just like React&#x27;s <code>useState</code>:</p>
19
54
 
20
55
  ```jsx
21
56
  import {useCellState} from 'tinybase/ui-react';
package/store/index.js CHANGED
@@ -85,7 +85,6 @@ const isObject = (obj) =>
85
85
  const objIds = object.keys;
86
86
  const objFreeze = object.freeze;
87
87
  const objNew = (entries = []) => object.fromEntries(entries);
88
- const objMerge = (...objs) => object.assign({}, ...objs);
89
88
  const objHas = (obj, id) => id in obj;
90
89
  const objDel = (obj, id) => {
91
90
  delete obj[id];
@@ -631,10 +630,24 @@ const createStore = () => {
631
630
  );
632
631
  const setOrDelTables = (tables) =>
633
632
  objIsEmpty(tables) ? delTables() : setTables(tables);
634
- const setOrDelCell = (tableId, rowId, cellId, cell, skipMiddleware) =>
633
+ const setOrDelCell = (
634
+ tableId,
635
+ rowId,
636
+ cellId,
637
+ cell,
638
+ skipMiddleware,
639
+ skipRowMiddleware,
640
+ ) =>
635
641
  isUndefined(cell)
636
642
  ? delCell(tableId, rowId, cellId, true, skipMiddleware)
637
- : setCell(tableId, rowId, cellId, cell, skipMiddleware);
643
+ : setCell(
644
+ tableId,
645
+ rowId,
646
+ cellId,
647
+ cell,
648
+ skipMiddleware,
649
+ skipRowMiddleware,
650
+ );
638
651
  const setOrDelValues = (values) =>
639
652
  objIsEmpty(values) ? delValues() : setValues(values);
640
653
  const setOrDelValue = (valueId, value, skipMiddleware) =>
@@ -734,6 +747,30 @@ const createStore = () => {
734
747
  ),
735
748
  objIsEqual,
736
749
  );
750
+ const applyRowDirectly = (tableId, tableMap, rowId, row, skipMiddleware) => {
751
+ mapMatch(
752
+ mapEnsure(tableMap, rowId, () => {
753
+ rowIdsChanged(tableId, rowId, 1);
754
+ return mapNew();
755
+ }),
756
+ row,
757
+ (rowMap, cellId, cell) =>
758
+ ifNotUndefined(
759
+ getValidatedCell(tableId, rowId, cellId, cell),
760
+ (validCell) =>
761
+ setValidCell(
762
+ tableId,
763
+ rowId,
764
+ rowMap,
765
+ cellId,
766
+ validCell,
767
+ skipMiddleware,
768
+ ),
769
+ ),
770
+ (rowMap, cellId) =>
771
+ delValidCell(tableId, tableMap, rowId, rowMap, cellId, true),
772
+ );
773
+ };
737
774
  const setValidCell = (tableId, rowId, rowMap, cellId, cell, skipMiddleware) =>
738
775
  ifTransformed(
739
776
  cell,
@@ -1410,7 +1447,14 @@ const createStore = () => {
1410
1447
  tableId,
1411
1448
  rowId,
1412
1449
  );
1413
- const setCell = (tableId, rowId, cellId, cell, skipMiddleware) =>
1450
+ const setCell = (
1451
+ tableId,
1452
+ rowId,
1453
+ cellId,
1454
+ cell,
1455
+ skipMiddleware,
1456
+ skipRowMiddleware,
1457
+ ) =>
1414
1458
  fluentTransaction(
1415
1459
  (tableId2, rowId2, cellId2) =>
1416
1460
  ifNotUndefined(
@@ -1420,15 +1464,47 @@ const createStore = () => {
1420
1464
  cellId2,
1421
1465
  isFunction(cell) ? cell(getCell(tableId2, rowId2, cellId2)) : cell,
1422
1466
  ),
1423
- (validCell) =>
1424
- setCellIntoNewRow(
1425
- tableId2,
1426
- getOrCreateTable(tableId2),
1427
- rowId2,
1428
- cellId2,
1429
- validCell,
1430
- skipMiddleware,
1431
- ),
1467
+ (validCell) => {
1468
+ const tableMap = getOrCreateTable(tableId2);
1469
+ ifNotUndefined(
1470
+ skipMiddleware || skipRowMiddleware || !middleware[14]?.()
1471
+ ? void 0
1472
+ : middleware[3],
1473
+ (willSetRow) => {
1474
+ const existingRowMap = mapGet(tableMap, rowId2);
1475
+ const prospectiveRow = {
1476
+ ...(existingRowMap ? mapToObj(existingRowMap) : {}),
1477
+ [cellId2]: validCell,
1478
+ };
1479
+ ifNotUndefined(
1480
+ whileMutating(() =>
1481
+ willSetRow(
1482
+ tableId2,
1483
+ rowId2,
1484
+ structuredClone(prospectiveRow),
1485
+ ),
1486
+ ),
1487
+ (row) =>
1488
+ applyRowDirectly(
1489
+ tableId2,
1490
+ tableMap,
1491
+ rowId2,
1492
+ row,
1493
+ skipMiddleware,
1494
+ ),
1495
+ );
1496
+ },
1497
+ () =>
1498
+ setCellIntoNewRow(
1499
+ tableId2,
1500
+ tableMap,
1501
+ rowId2,
1502
+ cellId2,
1503
+ validCell,
1504
+ skipMiddleware,
1505
+ ),
1506
+ );
1507
+ },
1432
1508
  ),
1433
1509
  tableId,
1434
1510
  rowId,
@@ -1477,7 +1553,14 @@ const createStore = () => {
1477
1553
  isUndefined(row)
1478
1554
  ? delRow(tableId, rowId)
1479
1555
  : objMap(row, (cell, cellId) =>
1480
- setOrDelCell(tableId, rowId, cellId, cell),
1556
+ setOrDelCell(
1557
+ tableId,
1558
+ rowId,
1559
+ cellId,
1560
+ cell,
1561
+ void 0,
1562
+ true,
1563
+ ),
1481
1564
  ),
1482
1565
  ),
1483
1566
  );
@@ -1640,37 +1723,6 @@ const createStore = () => {
1640
1723
  mapToObj3(changedCellIds),
1641
1724
  mapToObj(changedValueIds),
1642
1725
  ];
1643
- const doDidSetRows = () => {
1644
- if (middleware[14]) {
1645
- const changedCells2 = clonedChangedCells(changedCells);
1646
- collForEach(changedCells2, (rows, tableId) =>
1647
- collForEach(rows, (cells, rowId) => {
1648
- if (
1649
- !arrayEvery(
1650
- collValues(cells),
1651
- ([oldCell, newCell]) => oldCell === newCell,
1652
- )
1653
- ) {
1654
- const newRow = getRow(tableId, rowId);
1655
- const oldRow = objMerge(newRow);
1656
- collForEach(cells, ([oldCell], cellId) =>
1657
- isUndefined(oldCell)
1658
- ? objDel(oldRow, cellId)
1659
- : (oldRow[cellId] = oldCell),
1660
- );
1661
- const didSetRow = middleware[14](tableId, rowId, oldRow, newRow);
1662
- if (!objIsEqual(didSetRow, newRow)) {
1663
- const setOrDelRow = objMap(newRow, () => void 0);
1664
- objMap(didSetRow, (cell, cellId) => (setOrDelRow[cellId] = cell));
1665
- objMap(setOrDelRow, (cell, cellId) =>
1666
- setOrDelCell(tableId, rowId, cellId, cell, true),
1667
- );
1668
- }
1669
- }
1670
- }),
1671
- );
1672
- }
1673
- };
1674
1726
  const finishTransaction = (doRollback) => {
1675
1727
  if (transactions > 0) {
1676
1728
  transactions--;
@@ -1680,7 +1732,6 @@ const createStore = () => {
1680
1732
  callInvalidCellListeners(1);
1681
1733
  if (!collIsEmpty(changedCells)) {
1682
1734
  callTabularListenersForChanges(1);
1683
- doDidSetRows();
1684
1735
  }
1685
1736
  callInvalidValueListeners(1);
1686
1737
  if (!collIsEmpty(changedValues)) {
@@ -1851,7 +1902,7 @@ const createStore = () => {
1851
1902
  willDelValues,
1852
1903
  willDelValue,
1853
1904
  willApplyChanges,
1854
- didSetRow,
1905
+ hasWillSetRowCallbacks,
1855
1906
  ) =>
1856
1907
  (middleware = [
1857
1908
  willSetContent,
@@ -1868,7 +1919,7 @@ const createStore = () => {
1868
1919
  willDelValues,
1869
1920
  willDelValue,
1870
1921
  willApplyChanges,
1871
- didSetRow,
1922
+ hasWillSetRowCallbacks,
1872
1923
  ]);
1873
1924
  const setInternalListeners = (
1874
1925
  preStartTransaction,