Skip to content

Releases: tinyplex/tinybase

v9.0

Choose a tag to compare

@jamesgpearce jamesgpearce released this 03 Jul 03:27

This release has no new features; just fixes and reliability improvements.

TinyBase v9.0 is all about addressing issues from the community - and making local-first apps behave better in production. The areas addressed include persistence, synchronization, schema defaults, infrastructure limits, and edge-case query semantics.

There is one new configuration option (for more selective Value persistence), but otherwise the wider theme is reliability. This release hardens WebSocket synchronization, Durable Object storage, PowerSync startup, custom Persister loading, and a few type and documentation edges so that apps recover and sync more cleanly under real-world conditions.

We hope you enjoy using TinyBase and if you find further issues, keep them coming!

Persistence Subsets

This release adds finer-grained configuration for tabular database Persisters, allowing Values persistence to be limited to selected Value Ids (#279).

For apps that keep durable state and UI-only state in the same Store, the DpcTabularValues load and save properties can now use an array of Value Ids instead of a simple boolean:

const valuesSubsetDatabasePersisterConfig = {
  mode: 'tabular',
  values: {
    load: ['selectedPet', 'open'],
    save: ['selectedPet'],
  },
};

console.log(valuesSubsetDatabasePersisterConfig.values.load);
// -> ['selectedPet', 'open']

When a subset is configured, unlisted Values in the Store are not saved, and unlisted columns in the Values database table are left untouched.

WebSocket Synchronization Fixes

WebSocket Synchronizers can now fragment large synchronization payloads and reassemble them on receipt. This helps deployments behind infrastructure with WebSocket message size limits, such as Cloudflare Workers and Durable Objects (#261).

The createWsSynchronizer and createWsServer functions now accept an optional fragment size argument. Incomplete fragment buffers expire using the existing request timeout, which can also now be set on createWsServer. Durable Object servers can override the getFragmentSize and getRequestTimeoutSeconds methods to set the same behavior for messages they send.

The WebSocket Synchronizer documentation now also clarifies that WsServer paths come from WebSocket URL paths, not MergeableStore Ids, so clients that need separate synchronization groups should connect to different URL paths (#206).

When a persisted WsServer path starts after having no connected clients, it now loads its persisted Store before starting synchronization. This means the first client to reconnect is sent only the data it is missing, instead of receiving the whole persisted Store as a fresh change (#205).

Schema Default Synchronization Fixes

Schema defaults inserted automatically into MergeableStores now use neutral timestamps, so defaulted Values and Cells no longer overwrite newer synced data from another peer. Explicit writes of default values still receive normal timestamps (#167).

PowerSync Persistence Fixes

The PowerSync Persister now updates existing tabular rows before inserting missing ones, instead of replacing whole rows during upserts. This avoids flooding PowerSync upload queues with replacement writes when schema validation causes loaded data to be written back unchanged on startup (#262).

Custom Persister Loading Fixes

Custom Persisters can now return undefined from getPersisted to indicate that there is no persisted content. Loading then uses initialContent if it was provided, or otherwise leaves the Store unchanged without invoking the ignored error handler (#161).

Durable Object Persistence Fixes

The Durable Object SQL Storage Persister's fragmented mode now stores table row data as one SQL row per TinyBase Row, instead of one SQL row per Cell. This reduces the number of SQLite writes for wide Rows while preserving the fragmented mode's protection from Cloudflare's 2MB row limit. Existing cell-level fragmented data is still loaded and is cleaned up when the Row is next saved (#268).

Query Transaction Fixes

Grouped queries, including those with having clauses, now correctly return their current result when a query definition is added during an active Store transaction (#259).

Query Documentation Clarifications

The TinyQL documentation now explicitly describes that a Row only appears in a query result when at least one selected Cell or calculated value is defined. If all selected values for a Row resolve to undefined, no ResultRow is created for that Row (#183).

Type Fixes

The schema-aware MergeableContent, MergeableChanges, persisted content, and Persister listener types now validate content being set or loaded in the same way as Store setters. This catches invalid Cell or Value Ids and values in custom Persisters and MergeableStore setters (#178).

Breaking Change

This release is a major version because the Durable Object SQL Storage Persister's fragmented mode uses a new storage layout. TinyBase v9.0 can read the old cell-level fragmented data written by earlier releases, but once it saves the new row-level fragmented data, older TinyBase versions are not designed to read that data back. Apps using fragmented Durable Object SQL storage should not roll those Durable Objects back to an earlier TinyBase version after v9.0 has written to them.

Thank You

Thanks to everyone whose reports and fixes shaped this release:

Dheeraj, Jakub Riedl, Patryk Wegrzyn, Damilola Romniyi, Andrew Glago, wattroll, Will Honey, and Daniel Berndt.

Couldn't do it without you!

v8.5.0

Choose a tag to compare

@jamesgpearce jamesgpearce released this 21 Jun 08:53

React Chart Components

This release adds the new ui-react-dom-charts module, providing reactive SVG chart components for React apps.

The LineChart component and BarChart component can render data directly from a Store Table, or from a Queries ResultTable, using the same Provider context patterns as the rest of the React UI modules. For more complex charts, the CartesianChart component can compose multiple LineSeries component children and BarSeries component children in one SVG.

image

A chart can be bound to a Table with just the Table Id and the Cell Ids to use for the x and y values:

import React from 'react';
import {createRoot as createReactRoot} from 'react-dom/client';
import {createStore} from 'tinybase';
import {
  CartesianChart,
  LineChart,
  LineSeries,
} from 'tinybase/ui-react-dom-charts';

const chartStore = createStore();
const app = document.createElement('div');
const root = createReactRoot(app);

chartStore.setTable('sales', {
  jan: {month: 'Jan', order: 1, profit: 4, revenue: 12},
  feb: {month: 'Feb', order: 2, profit: 7, revenue: 18},
  mar: {month: 'Mar', order: 3, profit: 5, revenue: 15},
});

const MyChart = () => (
  <LineChart
    tableId="sales"
    store={chartStore}
    xCellId="month"
    yCellId="revenue"
    sortCellId="order"
  />
);

root.render(<MyChart />);

console.log(app.firstChild?.nodeName.toLowerCase());
// -> 'svg'

To plot multiple series in the same chart, use the CartesianChart component as the shared frame and declare each child series explicitly:

const MyCompositeChart = () => (
  <CartesianChart tableId="sales" store={chartStore}>
    <LineSeries
      className="revenue"
      label="Revenue"
      xCellId="month"
      yCellId="revenue"
      sortCellId="order"
    />
    <LineSeries
      className="profit"
      label="Profit"
      xCellId="month"
      yCellId="profit"
      sortCellId="order"
    />
  </CartesianChart>
);

const compositeChartApp = document.createElement('div');
createReactRoot(compositeChartApp).render(<MyCompositeChart />);
console.log(compositeChartApp.querySelectorAll('.line-series').length);
// -> 2

The same CartesianChart frame can include zero or one XAxis component and YAxis component child. These configuration children let you override inferred axis titles, numeric bounds, explicit ticks, tick counts, tick formatters, and axis-specific class names without adding more top-level chart props.

image

The Axis Overrides demo shows this pattern with numeric timestamps formatted as dates on the x axis, and revenue ticks formatted as dollar amounts on the y axis.

The Time Axes demo focuses on date handling directly, showing ISO date strings that infer a time scale and Unix second timestamps that use scale="time" and timestampUnit="second".

Chart presentation is handled with CSS. The chart components emit stable SVG class names for grid lines, axes, data marks, and tooltips, so you can keep data binding in props and visual styling in stylesheets.

Read more in the Using Charts guide and the Chart Components (React) demos.

The create-tinybase CLI tool also now includes a Charting app option, so you can scaffold a complete editable table with reactive chart output by running npm create tinybase@latest.

There are no intended breaking changes in this release. Please try the new chart components and let us know which chart types or styling hooks would be most useful next.

v8.4.2

Choose a tag to compare

@jamesgpearce jamesgpearce released this 30 May 04:24

This release updates dependencies, including the resolution for a breaking Svelte 5.56 issue.

v8.4.1

Choose a tag to compare

@jamesgpearce jamesgpearce released this 19 May 04:49

This release addresses a small z-index issue with the Inspector components.

v8.4.0

Choose a tag to compare

@jamesgpearce jamesgpearce released this 16 May 20:34

Solid DOM Components And Inspector

This release completes TinyBase's Solid support with two new additions: the ui-solid-dom module and the ui-solid-inspector module.

The ui-solid-dom module provides browser-ready Solid components for rendering and editing TinyBase data as HTML tables. They mirror the React DOM components, but use Solid components and Accessors throughout.

image

Alongside the table components, the new ui-solid-inspector module brings the TinyBase development inspector to Solid apps too, making it easy to inspect and edit Stores, Indexes, Relationships, and Queries during development:

image

A small Solid app can use both modules together:

import {render} from 'solid-js/web';
import {createRoot} from 'solid-js';
import {createStore} from 'tinybase';
import {CellView, Provider, useCell} from 'tinybase/ui-solid';
import {TableInHtmlTable} from 'tinybase/ui-solid-dom';
import {Inspector} from 'tinybase/ui-solid-inspector';

const domStore = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
});
const inspectorStore = createStore().setTable('pets', {
  fido: {species: 'dog'},
});

const appSolid = document.createElement('div');
const disposeAppSolid = render(
  () => (
    <>
      <TableInHtmlTable tableId="pets" store={domStore} editable={true} />
      <Provider store={inspectorStore}>
        <Inspector open={true} />
      </Provider>
    </>
  ),
  appSolid,
);

console.log(appSolid.querySelectorAll('tbody tr').length);
// -> 2
console.log(typeof Inspector);
// -> 'function'

disposeAppSolid();

Read more in the Using Solid DOM Components guide and the Inspector (Solid) demo.

New Solid Demos

This release also adds a complete set of Solid UI component demos, plus a Countries demo and an Inspector demo, so you can see the new modules working across Stores, Indexes, Relationships, Queries, and editable views.

image

These demos intentionally mirror the React set where possible, making it easier to compare implementation patterns across frameworks.

There are no intended breaking changes in this release. If you spot any issues with the new Solid DOM components or the Solid inspector, please let us know!

v8.3.0

Choose a tag to compare

@jamesgpearce jamesgpearce released this 09 May 22:00

Solid Support

This release adds the new ui-solid module, bringing TinyBase's reactive Store bindings to Solid apps. It provides Solid primitives that return Accessor functions, listener primitives that clean up with Solid's lifecycle, Provider context helpers, and view components for rendering Store data directly in a Solid component tree.

The primitives follow Solid's fine-grained reactivity model. They read TinyBase data immediately, then update the Accessor when the underlying Store data changes:

import {createRoot} from 'solid-js';
import {createStore} from 'tinybase';
import {useCell} from 'tinybase/ui-solid';

const solidStore = createStore().setCell('pets', 'fido', 'color', 'brown');

createRoot((dispose) => {
  const color = useCell('pets', 'fido', 'color', solidStore);

  console.log(color());
  // -> 'brown'

  dispose();
});

The module also includes Solid view components and a Provider component, so you can assemble UI directly from TinyBase data while still taking advantage of Solid's selective updates:

import {render} from 'solid-js/web';
import {CellView} from 'tinybase/ui-solid';

const solidApp = document.createElement('div');
document.body.appendChild(solidApp);

const disposeSolid = render(
  () => (
    <CellView tableId="pets" rowId="fido" cellId="color" store={solidStore} />
  ),
  solidApp,
);

console.log(solidApp.textContent);
// -> 'brown'

disposeSolid();

Read more in the Building UIs With Solid guides and the ui-solid module documentation.

A huge thanks to Daniel Grant, @djgrant, for designing and implementing this new Solid support! Please let us know how it works for you and if you have any feedback or suggestions.

Queries From Queries

Also in this release, TinyQL queries can now use the result of another query as their source. This lets you build complex results in small, readable steps - for example, first finding all dogs, and then querying that result to find just the brown ones:

import {createQueries} from 'tinybase';

const queryStore = createStore().setTable('pets', {
  fido: {species: 'dog', color: 'brown'},
  felix: {species: 'cat', color: 'black'},
  cujo: {species: 'dog', color: 'black'},
});

const queryQueries = createQueries(queryStore)
  .setQueryDefinition('dogs', 'pets', ({select, where}) => {
    select('color');
    where('species', 'dog');
  })
  .setQueryDefinition('brownDogs', true, 'dogs', ({select, where}) => {
    select('color');
    where('color', 'brown');
  });

console.log(queryQueries.getResultTable('brownDogs'));
// -> {fido: {color: 'brown'}}

The true argument tells setQueryDefinition that dogs is another query result, not a Table in the underlying Store. This works in query clauses too, so you can select or join from query results as your TinyQL definitions become more modular.

Schematizer Enums

This release addresses enum types in the schematizers. For example, Zod enums are now schematized as string types, rather than being rejected as invalid. This is also now documented accordingly.

There are no intended breaking changes in this release. If you try the new Solid bindings in particular, please let us know how they fit your expectations of building Solid apps - and good luck!

v8.2.0

Choose a tag to compare

@jamesgpearce jamesgpearce released this 18 Apr 05:47

Svelte DOM Components

This release completes TinyBase's Svelte support with two new additions: the ui-svelte-dom module and the ui-svelte-inspector module.

The ui-svelte-dom module provides browser-ready Svelte components for rendering and editing TinyBase data as HTML tables. They mirror the React DOM components, but use Svelte component composition and props throughout:

image
<script>
  import {createStore} from 'tinybase';
  import {TableInHtmlTable} from 'tinybase/ui-svelte-dom';

  const store = createStore().setTable('pets', {
    fido: {species: 'dog', color: 'brown'},
    felix: {species: 'cat', color: 'black'},
  });
</script>

<TableInHtmlTable tableId="pets" {store} editable />

Svelte Inspector

Alongside the table components, the new ui-svelte-inspector module brings the TinyBase development inspector to Svelte apps too, making it easy to inspect and edit Stores, Indexes, Relationships, and Queries during development:

image
<script>
  import {createStore} from 'tinybase';
  import {Provider} from 'tinybase/ui-svelte';
  import {Inspector} from 'tinybase/ui-svelte-inspector';

  const store = createStore().setTable('pets', {
    fido: {species: 'dog'},
  });
</script>

<Provider {store}>
  <Inspector />
</Provider>

Read more in the Using Svelte DOM Components guide and the Inspecting Data guide.

New Demos

This release also adds a complete set of Svelte UI component demos, plus an Inspector demo, so you can see the new modules working across Stores, Indexes, Relationships, Queries, and editable views.

image

These demos intentionally mirror the React set where possible, making it easier to compare implementation patterns across frameworks.

There are no intended breaking changes in this release. If you spot any issues with the new Svelte DOM components or the Svelte inspector, please let us know!

v8.1

Choose a tag to compare

@jamesgpearce jamesgpearce released this 05 Apr 05:51

Svelte Support

This highly-anticipated release introduces the new ui-svelte module, bringing native Svelte 5 runes-based reactive bindings to TinyBase. The module provides reactive functions and view components for building reactive UIs without any additional state management.

Reactive functions return a reactive object backed by Svelte's reactivity. Any component that reads current from it will automatically re-render when the underlying TinyBase data changes:

<script>
  import {createStore} from 'tinybase';
  import {getCell} from 'tinybase/ui-svelte';

  const store = createStore().setCell('pets', 'fido', 'color', 'brown');
  const color = getCell('pets', 'fido', 'color', store);
</script>

<p>Color: {color.current}</p>

The getCell function and the getValue function provide a writable current property that pairs naturally with Svelte's bind: directive for two-way data binding:

<script>
  import {getCell} from 'tinybase/ui-svelte';

  const color = getCell('pets', 'fido', 'color', store);
</script>

<input bind:value={color.current} />

All reactive functions accept reactive getter functions as parameters — the MaybeGetter type (T | (() => T)) — so passing () => rowId from a $state variable causes the function to reactively track which row it reads, without unmounting and remounting.

The module further includes a provider component and context helpers for sharing TinyBase objects across a component tree, and many built-in view components for assembling UIs directly from Store data.

Read more in the ui-svelte module documentation and the Building UIs With Svelte guide.

New Demos

To showcase the new Svelte support, we have created two new Svelte-specific demos: a Hello World (Svelte) demo and a Countries (Svelte) demo. Check them out to see the new module in action.

The create-tinybase CLI tool also now includes an option to create a Svelte demo project, so you can easily get started with Svelte and TinyBase in exactly the same way you can with React:

> npm create tinybase@latest

📦 Creating your project...

Breaking Changes

If you tried the ui-svelte module in earlier beta releases, there are some intentional breaking changes made to ensure the API is more idiomatic for Svelte. What was useX is now a reactive getX or hasX function, so for example useCell has become the getCell function and useHasCell has become the hasCell function. Context lookups also use getX, as with the getMetrics function, but those return TinyBase objects directly from Provider context rather than reactive current-based wrappers. Listener functions now use onX; so for example useCellListener has become the onCell function. The old useBindableCell and useBindableValue beta names have also gone away because the getCell function and getValue function expose the writable scalar accessors directly.

This release also contains a minor breaking change since v8.0. The tinybase/omni module no longer includes the ui-react module, ui-react-dom module, or ui-react-inspector module. Since the ui-svelte module exports many of the same names, including both in a single flat namespace would cause silent name collisions.

If you were importing React UI helpers from tinybase/omni, update your imports:

// Before
import {createStore, useCell, Provider} from 'tinybase/omni';

// After
import {createStore} from 'tinybase/omni';
import {useCell, Provider} from 'tinybase/ui-react';

(Sorry about that!)

We need your help

We hope you enjoy exploring this early new Svelte support. But we really need feedback on how it works and whether or not you find it easy and idiomatic to work with! Please let us know in the issues, discussions, or on social media. Thanks and good luck!

v8.0.2

Choose a tag to compare

@jamesgpearce jamesgpearce released this 18 Mar 04:39

This release updates dependencies and fixes #287 and #288.

v8.0.1

Choose a tag to compare

@jamesgpearce jamesgpearce released this 11 Mar 23:50

This release updates dependencies and restores 100% test coverage.