tinybase 9.1.0-beta.0 → 9.1.0-beta.1

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 (96) hide show
  1. package/@types/common/index.d.ts +17 -2
  2. package/@types/common/with-schemas/index.d.ts +18 -2
  3. package/@types/store/index.d.ts +37 -7
  4. package/@types/store/with-schemas/index.d.ts +38 -7
  5. package/@types/ui-react/index.d.ts +6 -2
  6. package/@types/ui-react/with-schemas/index.d.ts +7 -2
  7. package/@types/ui-solid/index.d.ts +9 -2
  8. package/@types/ui-solid/with-schemas/index.d.ts +10 -2
  9. package/@types/ui-svelte/index.d.ts +50 -2
  10. package/@types/ui-svelte/with-schemas/index.d.ts +88 -25
  11. package/index.js +34 -6
  12. package/mergeable-store/index.js +34 -6
  13. package/mergeable-store/with-schemas/index.js +34 -6
  14. package/min/index.js +1 -1
  15. package/min/index.js.gz +0 -0
  16. package/min/mergeable-store/index.js +1 -1
  17. package/min/mergeable-store/index.js.gz +0 -0
  18. package/min/mergeable-store/with-schemas/index.js +1 -1
  19. package/min/mergeable-store/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/index.js +1 -1
  29. package/min/ui-react/index.js.gz +0 -0
  30. package/min/ui-react/with-schemas/index.js +1 -1
  31. package/min/ui-react/with-schemas/index.js.gz +0 -0
  32. package/min/ui-react-dom/index.js +1 -1
  33. package/min/ui-react-dom/index.js.gz +0 -0
  34. package/min/ui-react-dom/with-schemas/index.js +1 -1
  35. package/min/ui-react-dom/with-schemas/index.js.gz +0 -0
  36. package/min/ui-react-dom-charts/index.js +1 -1
  37. package/min/ui-react-dom-charts/index.js.gz +0 -0
  38. package/min/ui-react-dom-charts/with-schemas/index.js +1 -1
  39. package/min/ui-react-dom-charts/with-schemas/index.js.gz +0 -0
  40. package/min/ui-react-inspector/index.js +1 -1
  41. package/min/ui-react-inspector/index.js.gz +0 -0
  42. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  43. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  44. package/min/ui-solid/index.js +1 -1
  45. package/min/ui-solid/index.js.gz +0 -0
  46. package/min/ui-solid/with-schemas/index.js +1 -1
  47. package/min/ui-solid/with-schemas/index.js.gz +0 -0
  48. package/min/ui-solid-dom/index.js +1 -1
  49. package/min/ui-solid-dom/index.js.gz +0 -0
  50. package/min/ui-solid-dom/with-schemas/index.js +1 -1
  51. package/min/ui-solid-dom/with-schemas/index.js.gz +0 -0
  52. package/min/ui-solid-inspector/index.js +1 -1
  53. package/min/ui-solid-inspector/index.js.gz +0 -0
  54. package/min/ui-solid-inspector/with-schemas/index.js +1 -1
  55. package/min/ui-solid-inspector/with-schemas/index.js.gz +0 -0
  56. package/min/ui-svelte/index.js +1 -1
  57. package/min/ui-svelte/index.js.gz +0 -0
  58. package/min/ui-svelte/with-schemas/index.js +1 -1
  59. package/min/ui-svelte/with-schemas/index.js.gz +0 -0
  60. package/min/ui-svelte-dom/index.js +1 -1
  61. package/min/ui-svelte-dom/index.js.gz +0 -0
  62. package/min/ui-svelte-dom/with-schemas/index.js +1 -1
  63. package/min/ui-svelte-dom/with-schemas/index.js.gz +0 -0
  64. package/min/ui-svelte-inspector/index.js +1 -1
  65. package/min/ui-svelte-inspector/index.js.gz +0 -0
  66. package/min/ui-svelte-inspector/with-schemas/index.js +1 -1
  67. package/min/ui-svelte-inspector/with-schemas/index.js.gz +0 -0
  68. package/min/with-schemas/index.js +1 -1
  69. package/min/with-schemas/index.js.gz +0 -0
  70. package/omni/index.js +34 -6
  71. package/omni/with-schemas/index.js +34 -6
  72. package/package.json +3 -3
  73. package/releases.md +1 -1
  74. package/store/index.js +34 -6
  75. package/store/with-schemas/index.js +34 -6
  76. package/ui-react/index.js +36 -6
  77. package/ui-react/with-schemas/index.js +36 -6
  78. package/ui-react-dom/index.js +24 -4
  79. package/ui-react-dom/with-schemas/index.js +24 -4
  80. package/ui-react-dom-charts/index.js +24 -4
  81. package/ui-react-dom-charts/with-schemas/index.js +24 -4
  82. package/ui-react-inspector/index.js +58 -10
  83. package/ui-react-inspector/with-schemas/index.js +58 -10
  84. package/ui-solid/index.js +32 -2
  85. package/ui-solid/with-schemas/index.js +32 -2
  86. package/ui-solid-dom/index.js +17 -1
  87. package/ui-solid-dom/with-schemas/index.js +17 -1
  88. package/ui-solid-inspector/index.js +51 -7
  89. package/ui-solid-inspector/with-schemas/index.js +51 -7
  90. package/ui-svelte/index.js +73 -31
  91. package/ui-svelte/with-schemas/index.js +73 -31
  92. package/ui-svelte-dom/index.js +35 -14
  93. package/ui-svelte-dom/with-schemas/index.js +35 -14
  94. package/ui-svelte-inspector/index.js +69 -20
  95. package/ui-svelte-inspector/with-schemas/index.js +69 -20
  96. package/with-schemas/index.js +34 -6
package/omni/index.js CHANGED
@@ -2206,13 +2206,36 @@ const createStore = () => {
2206
2206
  listener,
2207
2207
  mutator,
2208
2208
  ) => {
2209
- let sortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
2209
+ const [descending, offset, limit, sorter] = otherArgs;
2210
+ let sortedRowIds = getSortedRowIds(
2211
+ tableId,
2212
+ cellId,
2213
+ descending,
2214
+ offset,
2215
+ limit,
2216
+ sorter,
2217
+ );
2210
2218
  return addListener(
2211
2219
  () => {
2212
- const newSortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
2220
+ const newSortedRowIds = getSortedRowIds(
2221
+ tableId,
2222
+ cellId,
2223
+ descending,
2224
+ offset,
2225
+ limit,
2226
+ sorter,
2227
+ );
2213
2228
  if (!arrayIsEqual(newSortedRowIds, sortedRowIds)) {
2214
2229
  sortedRowIds = newSortedRowIds;
2215
- listener(store, tableId, cellId, ...otherArgs, sortedRowIds);
2230
+ listener(
2231
+ store,
2232
+ tableId,
2233
+ cellId,
2234
+ descending,
2235
+ offset,
2236
+ limit,
2237
+ sortedRowIds,
2238
+ );
2216
2239
  }
2217
2240
  },
2218
2241
  sortedRowIdsListeners[mutator ? 1 : 0],
@@ -2266,6 +2289,7 @@ const createStore = () => {
2266
2289
  descending,
2267
2290
  offset = 0,
2268
2291
  limit,
2292
+ sorter = defaultSorter,
2269
2293
  ) =>
2270
2294
  isObject(tableIdOrArgs)
2271
2295
  ? getSortedRowIds(
@@ -2274,16 +2298,19 @@ const createStore = () => {
2274
2298
  tableIdOrArgs.descending,
2275
2299
  tableIdOrArgs.offset,
2276
2300
  tableIdOrArgs.limit,
2301
+ tableIdOrArgs.sorter,
2277
2302
  )
2278
2303
  : arrayMap(
2279
2304
  slice(
2280
2305
  arraySort(
2281
2306
  mapMap(mapGet(tablesMap, id(tableIdOrArgs)), (row, rowId) => [
2282
- isUndefined(cellId) ? rowId : mapGet(row, id(cellId)),
2307
+ isUndefined(cellId)
2308
+ ? rowId
2309
+ : decodeIfJson(mapGet(row, id(cellId))),
2283
2310
  rowId,
2284
2311
  ]),
2285
2312
  ([cell1], [cell2]) =>
2286
- defaultSorter(cell1, cell2) * (descending ? -1 : 1),
2313
+ sorter(cell1, cell2) * (descending ? -1 : 1),
2287
2314
  ),
2288
2315
  offset,
2289
2316
  isUndefined(limit) ? limit : offset + limit,
@@ -2761,6 +2788,7 @@ const createStore = () => {
2761
2788
  tableIdOrArgs.descending ?? false,
2762
2789
  tableIdOrArgs.offset ?? 0,
2763
2790
  tableIdOrArgs.limit,
2791
+ tableIdOrArgs.sorter,
2764
2792
  ],
2765
2793
  cellIdOrListener,
2766
2794
  descendingOrMutator,
@@ -2768,7 +2796,7 @@ const createStore = () => {
2768
2796
  : addSortedRowIdsListenerImpl(
2769
2797
  tableIdOrArgs,
2770
2798
  cellIdOrListener,
2771
- [descendingOrMutator, offset, limit],
2799
+ [descendingOrMutator, offset, limit, void 0],
2772
2800
  listener,
2773
2801
  mutator,
2774
2802
  );
@@ -2206,13 +2206,36 @@ const createStore = () => {
2206
2206
  listener,
2207
2207
  mutator,
2208
2208
  ) => {
2209
- let sortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
2209
+ const [descending, offset, limit, sorter] = otherArgs;
2210
+ let sortedRowIds = getSortedRowIds(
2211
+ tableId,
2212
+ cellId,
2213
+ descending,
2214
+ offset,
2215
+ limit,
2216
+ sorter,
2217
+ );
2210
2218
  return addListener(
2211
2219
  () => {
2212
- const newSortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
2220
+ const newSortedRowIds = getSortedRowIds(
2221
+ tableId,
2222
+ cellId,
2223
+ descending,
2224
+ offset,
2225
+ limit,
2226
+ sorter,
2227
+ );
2213
2228
  if (!arrayIsEqual(newSortedRowIds, sortedRowIds)) {
2214
2229
  sortedRowIds = newSortedRowIds;
2215
- listener(store, tableId, cellId, ...otherArgs, sortedRowIds);
2230
+ listener(
2231
+ store,
2232
+ tableId,
2233
+ cellId,
2234
+ descending,
2235
+ offset,
2236
+ limit,
2237
+ sortedRowIds,
2238
+ );
2216
2239
  }
2217
2240
  },
2218
2241
  sortedRowIdsListeners[mutator ? 1 : 0],
@@ -2266,6 +2289,7 @@ const createStore = () => {
2266
2289
  descending,
2267
2290
  offset = 0,
2268
2291
  limit,
2292
+ sorter = defaultSorter,
2269
2293
  ) =>
2270
2294
  isObject(tableIdOrArgs)
2271
2295
  ? getSortedRowIds(
@@ -2274,16 +2298,19 @@ const createStore = () => {
2274
2298
  tableIdOrArgs.descending,
2275
2299
  tableIdOrArgs.offset,
2276
2300
  tableIdOrArgs.limit,
2301
+ tableIdOrArgs.sorter,
2277
2302
  )
2278
2303
  : arrayMap(
2279
2304
  slice(
2280
2305
  arraySort(
2281
2306
  mapMap(mapGet(tablesMap, id(tableIdOrArgs)), (row, rowId) => [
2282
- isUndefined(cellId) ? rowId : mapGet(row, id(cellId)),
2307
+ isUndefined(cellId)
2308
+ ? rowId
2309
+ : decodeIfJson(mapGet(row, id(cellId))),
2283
2310
  rowId,
2284
2311
  ]),
2285
2312
  ([cell1], [cell2]) =>
2286
- defaultSorter(cell1, cell2) * (descending ? -1 : 1),
2313
+ sorter(cell1, cell2) * (descending ? -1 : 1),
2287
2314
  ),
2288
2315
  offset,
2289
2316
  isUndefined(limit) ? limit : offset + limit,
@@ -2761,6 +2788,7 @@ const createStore = () => {
2761
2788
  tableIdOrArgs.descending ?? false,
2762
2789
  tableIdOrArgs.offset ?? 0,
2763
2790
  tableIdOrArgs.limit,
2791
+ tableIdOrArgs.sorter,
2764
2792
  ],
2765
2793
  cellIdOrListener,
2766
2794
  descendingOrMutator,
@@ -2768,7 +2796,7 @@ const createStore = () => {
2768
2796
  : addSortedRowIdsListenerImpl(
2769
2797
  tableIdOrArgs,
2770
2798
  cellIdOrListener,
2771
- [descendingOrMutator, offset, limit],
2799
+ [descendingOrMutator, offset, limit, void 0],
2772
2800
  listener,
2773
2801
  mutator,
2774
2802
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinybase",
3
- "version": "9.1.0-beta.0",
3
+ "version": "9.1.0-beta.1",
4
4
  "author": "jamesgpearce",
5
5
  "repository": "github:tinyplex/tinybase",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  "sideEffects": false,
25
25
  "peerDependencies": {
26
26
  "@automerge/automerge-repo": "^2.5.6",
27
- "@cloudflare/workers-types": "^5.20260703.1",
27
+ "@cloudflare/workers-types": "^5.20260704.1",
28
28
  "@electric-sql/pglite": "^0.5.4",
29
29
  "@libsql/client": "^0.17.4",
30
30
  "@powersync/common": "^1.57.0",
@@ -35,7 +35,7 @@
35
35
  "bun": "^1.3.14",
36
36
  "effect": "^3.21.4",
37
37
  "electric-sql": "^0.12.1",
38
- "expo": "^57.0.1",
38
+ "expo": "^57.0.2",
39
39
  "expo-sqlite": "^57.0.0",
40
40
  "partykit": "^0.0.115",
41
41
  "partysocket": "^1.3.0",
package/releases.md CHANGED
@@ -13,7 +13,7 @@ console.log(valuesSubsetDatabasePersisterConfig.values.load);
13
13
  // -> ['selectedPet', 'open']
14
14
  ```
15
15
 
16
- <p>When a subset is configured, unlisted <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> in the <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> are not saved, and unlisted columns in the <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> database table are left untouched.</p><h2 id="websocket-synchronization-fixes">WebSocket <a href="https://tinybase.org/guides/synchronization/">Synchronization</a> Fixes</h2><p>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 (<a href="https://github.com/tinyplex/tinybase/issues/261">#261</a>).</p><p>The createWsSynchronizer and <a href="https://tinybase.org/api/synchronizer-ws-server/functions/creation/createwsserver/"><code>createWsServer</code></a> 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 <a href="https://tinybase.org/api/synchronizer-ws-server-durable-object/classes/creation/wsserverdurableobject/methods/getter/getrequesttimeoutseconds/"><code>getRequestTimeoutSeconds</code></a> methods to set the same behavior for messages they send.</p><p>The WebSocket <a href="https://tinybase.org/api/the-essentials/synchronizing-stores/synchronizer/"><code>Synchronizer</code></a> documentation now also clarifies that <a href="https://tinybase.org/api/synchronizer-ws-server/interfaces/server/wsserver/"><code>WsServer</code></a> paths come from WebSocket URL paths, not <a href="https://tinybase.org/api/mergeable-store/interfaces/mergeable/mergeablestore/"><code>MergeableStore</code></a> <a href="https://tinybase.org/api/common/type-aliases/identity/ids/"><code>Ids</code></a>, so clients that need separate synchronization groups should connect to different URL paths (<a href="https://github.com/tinyplex/tinybase/issues/206">#206</a>).</p><p>When a persisted <a href="https://tinybase.org/api/synchronizer-ws-server/interfaces/server/wsserver/"><code>WsServer</code></a> path starts after having no connected clients, it now loads its persisted <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> before starting synchronization. This means the first client to reconnect is sent only the data it is missing, instead of receiving the whole persisted <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> as a fresh change (<a href="https://github.com/tinyplex/tinybase/issues/205">#205</a>).</p><h2 id="schema-default-synchronization-fixes">Schema Default <a href="https://tinybase.org/guides/synchronization/">Synchronization</a> Fixes</h2><p>Schema defaults inserted automatically into MergeableStores now use neutral timestamps, so defaulted <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> and Cells no longer overwrite newer synced data from another peer. Explicit writes of default values still receive normal timestamps (<a href="https://github.com/tinyplex/tinybase/issues/167">#167</a>).</p><h2 id="powersync-persistence-fixes">PowerSync <a href="https://tinybase.org/guides/persistence/">Persistence</a> Fixes</h2><p>The PowerSync <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> 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 (<a href="https://github.com/tinyplex/tinybase/issues/262">#262</a>).</p><h2 id="custom-persister-loading-fixes">Custom <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> Loading Fixes</h2><p>Custom Persisters can now return <code>undefined</code> from <code>getPersisted</code> to indicate that there is no persisted content. Loading then uses <code>initialContent</code> if it was provided, or otherwise leaves the <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> unchanged without invoking the ignored error handler (<a href="https://github.com/tinyplex/tinybase/issues/161">#161</a>).</p><h2 id="durable-object-persistence-fixes">Durable Object <a href="https://tinybase.org/guides/persistence/">Persistence</a> Fixes</h2><p>The Durable Object SQL Storage <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a>&#x27;s fragmented mode now stores table row data as one SQL row per TinyBase <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a>, instead of one SQL row per <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a>. This reduces the number of SQLite writes for wide Rows while preserving the fragmented mode&#x27;s protection from Cloudflare&#x27;s 2MB row limit. Existing cell-level fragmented data is still loaded and is cleaned up when the <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> is next saved (<a href="https://github.com/tinyplex/tinybase/issues/268">#268</a>).</p><h2 id="query-transaction-fixes">Query Transaction Fixes</h2><p>Grouped queries, including those with having clauses, now correctly return their current result when a query definition is added during an active <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> transaction (<a href="https://github.com/tinyplex/tinybase/issues/259">#259</a>).</p><h2 id="query-documentation-clarifications">Query <a href="https://tinybase.org/guides/how-tinybase-is-built/documentation/">Documentation</a> Clarifications</h2><p>The <a href="https://tinybase.org/guides/using-queries/tinyql/">TinyQL</a> documentation now explicitly describes that a <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> only appears in a query result when at least one selected <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> or calculated value is defined. If all selected values for a <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> resolve to <code>undefined</code>, no <a href="https://tinybase.org/api/queries/type-aliases/result/resultrow/"><code>ResultRow</code></a> is created for that <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> (<a href="https://github.com/tinyplex/tinybase/issues/183">#183</a>).</p><h2 id="type-fixes">Type Fixes</h2><p>The schema-aware <a href="https://tinybase.org/api/mergeable-store/type-aliases/mergeable/mergeablecontent/"><code>MergeableContent</code></a>, <a href="https://tinybase.org/api/mergeable-store/type-aliases/mergeable/mergeablechanges/"><code>MergeableChanges</code></a>, persisted content, and <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> listener types now validate content being set or loaded in the same way as <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> setters. This catches invalid <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> or <a href="https://tinybase.org/api/store/type-aliases/store/value/"><code>Value</code></a> <a href="https://tinybase.org/api/common/type-aliases/identity/ids/"><code>Ids</code></a> and values in custom Persisters and <a href="https://tinybase.org/api/mergeable-store/interfaces/mergeable/mergeablestore/"><code>MergeableStore</code></a> setters (<a href="https://github.com/tinyplex/tinybase/issues/178">#178</a>).</p><h2 id="breaking-change">Breaking Change</h2><p>This release is a major version because the Durable Object SQL Storage <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a>&#x27;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.</p><h2 id="thank-you">Thank You</h2><p>Thanks to everyone whose reports and fixes shaped this release:</p><p><a href="https://github.com/dheerajvs">Dheeraj</a>, <a href="https://github.com/jakubriedl">Jakub Riedl</a>, <a href="https://github.com/patrykwegrzyn">Patryk Wegrzyn</a>, <a href="https://github.com/dsrominiyi">Damilola Romniyi</a>, <a href="https://github.com/a11rew">Andrew Glago</a>, <a href="https://github.com/wattroll">wattroll</a>, <a href="https://github.com/willhoney7">Will Honey</a>, and <a href="https://github.com/danielberndt">Daniel Berndt</a>.</p><p>Couldn&#x27;t do it without you!</p><hr><h1 id="v8-5">v8.5</h1><h2 id="react-chart-components">React Chart Components</h2><p>This release adds the new <a href="https://tinybase.org/api/ui-react-dom-charts/"><code>ui-react-dom-charts</code></a> module, providing reactive SVG chart components for React apps.</p><p>The <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/linechart/"><code>LineChart</code></a> component and <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/barchart/"><code>BarChart</code></a> component can render data directly from a <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> <a href="https://tinybase.org/api/store/type-aliases/store/table/"><code>Table</code></a>, or from a <a href="https://tinybase.org/api/queries/interfaces/queries/queries/"><code>Queries</code></a> <a href="https://tinybase.org/api/queries/type-aliases/result/resulttable/"><code>ResultTable</code></a>, using the same Provider context patterns as the rest of the React UI modules. For more complex charts, the <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/cartesianchart/"><code>CartesianChart</code></a> component can compose multiple <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/lineseries/"><code>LineSeries</code></a> component children and <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/barseries/"><code>BarSeries</code></a> component children in one SVG.</p><p><img src="https://tinybase.org/shots/styled-chart-react-demo.png" alt="LineChart component (React)" title="[`LineChart`](/api/ui-react-dom-charts/functions/store-components/linechart/)
16
+ <p>When a subset is configured, unlisted <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> in the <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> are not saved, and unlisted columns in the <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> database table are left untouched.</p><h2 id="websocket-synchronization-fixes">WebSocket <a href="https://tinybase.org/guides/synchronization/">Synchronization</a> Fixes</h2><p>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 (<a href="https://github.com/tinyplex/tinybase/issues/261">#261</a>).</p><p>The createWsSynchronizer and <a href="https://tinybase.org/api/synchronizer-ws-server/functions/creation/createwsserver/"><code>createWsServer</code></a> 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 <a href="https://tinybase.org/api/synchronizer-ws-server-durable-object/classes/creation/wsserverdurableobject/methods/getter/getrequesttimeoutseconds/"><code>getRequestTimeoutSeconds</code></a> methods to set the same behavior for messages they send.</p><p>The WebSocket <a href="https://tinybase.org/api/the-essentials/synchronizing-stores/synchronizer/"><code>Synchronizer</code></a> documentation now also clarifies that <a href="https://tinybase.org/api/synchronizer-ws-server/interfaces/server/wsserver/"><code>WsServer</code></a> paths come from WebSocket URL paths, not <a href="https://tinybase.org/api/mergeable-store/interfaces/mergeable/mergeablestore/"><code>MergeableStore</code></a> <a href="https://tinybase.org/api/common/type-aliases/identity/ids/"><code>Ids</code></a>, so clients that need separate synchronization groups should connect to different URL paths (<a href="https://github.com/tinyplex/tinybase/issues/206">#206</a>).</p><p>When a persisted <a href="https://tinybase.org/api/synchronizer-ws-server/interfaces/server/wsserver/"><code>WsServer</code></a> path starts after having no connected clients, it now loads its persisted <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> before starting synchronization. This means the first client to reconnect is sent only the data it is missing, instead of receiving the whole persisted <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> as a fresh change (<a href="https://github.com/tinyplex/tinybase/issues/205">#205</a>).</p><h2 id="schema-default-synchronization-fixes">Schema Default <a href="https://tinybase.org/guides/synchronization/">Synchronization</a> Fixes</h2><p>Schema defaults inserted automatically into MergeableStores now use neutral timestamps, so defaulted <a href="https://tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> and Cells no longer overwrite newer synced data from another peer. Explicit writes of default values still receive normal timestamps (<a href="https://github.com/tinyplex/tinybase/issues/167">#167</a>).</p><h2 id="powersync-persistence-fixes">PowerSync <a href="https://tinybase.org/guides/persistence/">Persistence</a> Fixes</h2><p>The PowerSync <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> 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 (<a href="https://github.com/tinyplex/tinybase/issues/262">#262</a>).</p><h2 id="custom-persister-loading-fixes">Custom <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> Loading Fixes</h2><p>Custom Persisters can now return <code>undefined</code> from <code>getPersisted</code> to indicate that there is no persisted content. Loading then uses <code>initialContent</code> if it was provided, or otherwise leaves the <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> unchanged without invoking the ignored error handler (<a href="https://github.com/tinyplex/tinybase/issues/161">#161</a>).</p><h2 id="durable-object-persistence-fixes">Durable Object <a href="https://tinybase.org/guides/persistence/">Persistence</a> Fixes</h2><p>The Durable Object SQL Storage <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a>&#x27;s fragmented mode now stores table row data as one SQL row per TinyBase <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a>, instead of one SQL row per <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a>. This reduces the number of SQLite writes for wide Rows while preserving the fragmented mode&#x27;s protection from Cloudflare&#x27;s 2MB row limit. Existing cell-level fragmented data is still loaded and is cleaned up when the <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> is next saved (<a href="https://github.com/tinyplex/tinybase/issues/268">#268</a>).</p><h2 id="query-transaction-fixes">Query Transaction Fixes</h2><p>Grouped queries, including those with having clauses, now correctly return their current result when a query definition is added during an active <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> transaction (<a href="https://github.com/tinyplex/tinybase/issues/259">#259</a>).</p><h2 id="query-documentation-clarifications">Query <a href="https://tinybase.org/guides/how-tinybase-is-built/documentation/">Documentation</a> Clarifications</h2><p>The <a href="https://tinybase.org/guides/using-queries/tinyql/">TinyQL</a> documentation now explicitly describes that a <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> only appears in a query result when at least one selected <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> or calculated value is defined. If all selected values for a <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> resolve to <code>undefined</code>, no <a href="https://tinybase.org/api/queries/type-aliases/result/resultrow/"><code>ResultRow</code></a> is created for that <a href="https://tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> (<a href="https://github.com/tinyplex/tinybase/issues/183">#183</a>).</p><h2 id="type-fixes">Type Fixes</h2><p>The schema-aware <a href="https://tinybase.org/api/mergeable-store/type-aliases/mergeable/mergeablecontent/"><code>MergeableContent</code></a>, <a href="https://tinybase.org/api/mergeable-store/type-aliases/mergeable/mergeablechanges/"><code>MergeableChanges</code></a>, persisted content, and <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> listener types now validate content being set or loaded in the same way as <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> setters. This catches invalid <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> or <a href="https://tinybase.org/api/store/type-aliases/store/value/"><code>Value</code></a> <a href="https://tinybase.org/api/common/type-aliases/identity/ids/"><code>Ids</code></a> and values in custom Persisters and <a href="https://tinybase.org/api/mergeable-store/interfaces/mergeable/mergeablestore/"><code>MergeableStore</code></a> setters (<a href="https://github.com/tinyplex/tinybase/issues/178">#178</a>).</p><h2 id="breaking-change">Breaking Change</h2><p>This release is a major version because the Durable Object SQL Storage <a href="https://tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a>&#x27;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.</p><h2 id="thank-you">Thank You</h2><p>Thanks to everyone whose reports and fixes shaped this release:</p><p><a href="https://github.com/dheerajvs">Dheeraj</a>, <a href="https://github.com/jakubriedl">Jakub Riedl</a>, <a href="https://github.com/patrykwegrzyn">Patryk Wegrzyn</a>, <a href="https://github.com/dsrominiyi">Damilola Romniyi</a>, <a href="https://github.com/a11rew">Andrew Glago</a>, <a href="https://github.com/wattroll">wattroll</a>, <a href="https://github.com/willhoney7">Will Honey</a>, and <a href="https://github.com/danielberndt">Daniel Berndt</a>.</p><p>Could not do it without you!</p><hr><h1 id="v8-5">v8.5</h1><h2 id="react-chart-components">React Chart Components</h2><p>This release adds the new <a href="https://tinybase.org/api/ui-react-dom-charts/"><code>ui-react-dom-charts</code></a> module, providing reactive SVG chart components for React apps.</p><p>The <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/linechart/"><code>LineChart</code></a> component and <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/barchart/"><code>BarChart</code></a> component can render data directly from a <a href="https://tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> <a href="https://tinybase.org/api/store/type-aliases/store/table/"><code>Table</code></a>, or from a <a href="https://tinybase.org/api/queries/interfaces/queries/queries/"><code>Queries</code></a> <a href="https://tinybase.org/api/queries/type-aliases/result/resulttable/"><code>ResultTable</code></a>, using the same Provider context patterns as the rest of the React UI modules. For more complex charts, the <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/cartesianchart/"><code>CartesianChart</code></a> component can compose multiple <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/lineseries/"><code>LineSeries</code></a> component children and <a href="https://tinybase.org/api/ui-react-dom-charts/functions/store-components/barseries/"><code>BarSeries</code></a> component children in one SVG.</p><p><img src="https://tinybase.org/shots/styled-chart-react-demo.png" alt="LineChart component (React)" title="[`LineChart`](/api/ui-react-dom-charts/functions/store-components/linechart/)
17
17
  component (React)"></p><p>A chart can be bound to a <a href="https://tinybase.org/api/store/type-aliases/store/table/"><code>Table</code></a> with just the <a href="https://tinybase.org/api/store/type-aliases/store/table/"><code>Table</code></a> <a href="https://tinybase.org/api/common/type-aliases/identity/id/"><code>Id</code></a> and the <a href="https://tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> <a href="https://tinybase.org/api/common/type-aliases/identity/ids/"><code>Ids</code></a> to use for the x and y values:</p>
18
18
 
19
19
  ```jsx
package/store/index.js CHANGED
@@ -1314,13 +1314,36 @@ const createStore = () => {
1314
1314
  listener,
1315
1315
  mutator,
1316
1316
  ) => {
1317
- let sortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
1317
+ const [descending, offset, limit, sorter] = otherArgs;
1318
+ let sortedRowIds = getSortedRowIds(
1319
+ tableId,
1320
+ cellId,
1321
+ descending,
1322
+ offset,
1323
+ limit,
1324
+ sorter,
1325
+ );
1318
1326
  return addListener(
1319
1327
  () => {
1320
- const newSortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
1328
+ const newSortedRowIds = getSortedRowIds(
1329
+ tableId,
1330
+ cellId,
1331
+ descending,
1332
+ offset,
1333
+ limit,
1334
+ sorter,
1335
+ );
1321
1336
  if (!arrayIsEqual(newSortedRowIds, sortedRowIds)) {
1322
1337
  sortedRowIds = newSortedRowIds;
1323
- listener(store, tableId, cellId, ...otherArgs, sortedRowIds);
1338
+ listener(
1339
+ store,
1340
+ tableId,
1341
+ cellId,
1342
+ descending,
1343
+ offset,
1344
+ limit,
1345
+ sortedRowIds,
1346
+ );
1324
1347
  }
1325
1348
  },
1326
1349
  sortedRowIdsListeners[mutator ? 1 : 0],
@@ -1374,6 +1397,7 @@ const createStore = () => {
1374
1397
  descending,
1375
1398
  offset = 0,
1376
1399
  limit,
1400
+ sorter = defaultSorter,
1377
1401
  ) =>
1378
1402
  isObject(tableIdOrArgs)
1379
1403
  ? getSortedRowIds(
@@ -1382,16 +1406,19 @@ const createStore = () => {
1382
1406
  tableIdOrArgs.descending,
1383
1407
  tableIdOrArgs.offset,
1384
1408
  tableIdOrArgs.limit,
1409
+ tableIdOrArgs.sorter,
1385
1410
  )
1386
1411
  : arrayMap(
1387
1412
  slice(
1388
1413
  arraySort(
1389
1414
  mapMap(mapGet(tablesMap, id(tableIdOrArgs)), (row, rowId) => [
1390
- isUndefined(cellId) ? rowId : mapGet(row, id(cellId)),
1415
+ isUndefined(cellId)
1416
+ ? rowId
1417
+ : decodeIfJson(mapGet(row, id(cellId))),
1391
1418
  rowId,
1392
1419
  ]),
1393
1420
  ([cell1], [cell2]) =>
1394
- defaultSorter(cell1, cell2) * (descending ? -1 : 1),
1421
+ sorter(cell1, cell2) * (descending ? -1 : 1),
1395
1422
  ),
1396
1423
  offset,
1397
1424
  isUndefined(limit) ? limit : offset + limit,
@@ -1869,6 +1896,7 @@ const createStore = () => {
1869
1896
  tableIdOrArgs.descending ?? false,
1870
1897
  tableIdOrArgs.offset ?? 0,
1871
1898
  tableIdOrArgs.limit,
1899
+ tableIdOrArgs.sorter,
1872
1900
  ],
1873
1901
  cellIdOrListener,
1874
1902
  descendingOrMutator,
@@ -1876,7 +1904,7 @@ const createStore = () => {
1876
1904
  : addSortedRowIdsListenerImpl(
1877
1905
  tableIdOrArgs,
1878
1906
  cellIdOrListener,
1879
- [descendingOrMutator, offset, limit],
1907
+ [descendingOrMutator, offset, limit, void 0],
1880
1908
  listener,
1881
1909
  mutator,
1882
1910
  );
@@ -1314,13 +1314,36 @@ const createStore = () => {
1314
1314
  listener,
1315
1315
  mutator,
1316
1316
  ) => {
1317
- let sortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
1317
+ const [descending, offset, limit, sorter] = otherArgs;
1318
+ let sortedRowIds = getSortedRowIds(
1319
+ tableId,
1320
+ cellId,
1321
+ descending,
1322
+ offset,
1323
+ limit,
1324
+ sorter,
1325
+ );
1318
1326
  return addListener(
1319
1327
  () => {
1320
- const newSortedRowIds = getSortedRowIds(tableId, cellId, ...otherArgs);
1328
+ const newSortedRowIds = getSortedRowIds(
1329
+ tableId,
1330
+ cellId,
1331
+ descending,
1332
+ offset,
1333
+ limit,
1334
+ sorter,
1335
+ );
1321
1336
  if (!arrayIsEqual(newSortedRowIds, sortedRowIds)) {
1322
1337
  sortedRowIds = newSortedRowIds;
1323
- listener(store, tableId, cellId, ...otherArgs, sortedRowIds);
1338
+ listener(
1339
+ store,
1340
+ tableId,
1341
+ cellId,
1342
+ descending,
1343
+ offset,
1344
+ limit,
1345
+ sortedRowIds,
1346
+ );
1324
1347
  }
1325
1348
  },
1326
1349
  sortedRowIdsListeners[mutator ? 1 : 0],
@@ -1374,6 +1397,7 @@ const createStore = () => {
1374
1397
  descending,
1375
1398
  offset = 0,
1376
1399
  limit,
1400
+ sorter = defaultSorter,
1377
1401
  ) =>
1378
1402
  isObject(tableIdOrArgs)
1379
1403
  ? getSortedRowIds(
@@ -1382,16 +1406,19 @@ const createStore = () => {
1382
1406
  tableIdOrArgs.descending,
1383
1407
  tableIdOrArgs.offset,
1384
1408
  tableIdOrArgs.limit,
1409
+ tableIdOrArgs.sorter,
1385
1410
  )
1386
1411
  : arrayMap(
1387
1412
  slice(
1388
1413
  arraySort(
1389
1414
  mapMap(mapGet(tablesMap, id(tableIdOrArgs)), (row, rowId) => [
1390
- isUndefined(cellId) ? rowId : mapGet(row, id(cellId)),
1415
+ isUndefined(cellId)
1416
+ ? rowId
1417
+ : decodeIfJson(mapGet(row, id(cellId))),
1391
1418
  rowId,
1392
1419
  ]),
1393
1420
  ([cell1], [cell2]) =>
1394
- defaultSorter(cell1, cell2) * (descending ? -1 : 1),
1421
+ sorter(cell1, cell2) * (descending ? -1 : 1),
1395
1422
  ),
1396
1423
  offset,
1397
1424
  isUndefined(limit) ? limit : offset + limit,
@@ -1869,6 +1896,7 @@ const createStore = () => {
1869
1896
  tableIdOrArgs.descending ?? false,
1870
1897
  tableIdOrArgs.offset ?? 0,
1871
1898
  tableIdOrArgs.limit,
1899
+ tableIdOrArgs.sorter,
1872
1900
  ],
1873
1901
  cellIdOrListener,
1874
1902
  descendingOrMutator,
@@ -1876,7 +1904,7 @@ const createStore = () => {
1876
1904
  : addSortedRowIdsListenerImpl(
1877
1905
  tableIdOrArgs,
1878
1906
  cellIdOrListener,
1879
- [descendingOrMutator, offset, limit],
1907
+ [descendingOrMutator, offset, limit, void 0],
1880
1908
  listener,
1881
1909
  mutator,
1882
1910
  );
package/ui-react/index.js CHANGED
@@ -536,33 +536,56 @@ const useSortedRowIdsImpl = (
536
536
  descending,
537
537
  offset,
538
538
  limit,
539
+ sorter,
539
540
  storeOrStoreId,
540
- ) =>
541
- useListenable(
541
+ ) => {
542
+ const sortedRowIdsArgs = useMemo(
543
+ () => ({
544
+ tableId,
545
+ cellId,
546
+ descending: descending ?? false,
547
+ offset: offset ?? 0,
548
+ limit,
549
+ sorter,
550
+ }),
551
+ [tableId, cellId, descending, offset, limit, sorter],
552
+ );
553
+ return useListenable(
542
554
  SORTED_ROW_IDS,
543
555
  useStoreOrStoreById(storeOrStoreId),
544
556
  1 /* Array */,
545
- [tableId, cellId, descending, offset, limit],
557
+ isUndefined(sorter)
558
+ ? [tableId, cellId, descending, offset, limit]
559
+ : [sortedRowIdsArgs],
546
560
  );
561
+ };
547
562
  const useSortedRowIdsListenerImpl = (
548
563
  tableId,
549
564
  cellId,
550
565
  descending,
551
566
  offset,
552
567
  limit,
568
+ sorter,
553
569
  listener,
554
570
  listenerDeps,
555
571
  mutator,
556
572
  storeOrStoreId,
557
- ) =>
573
+ ) => {
574
+ const sortedRowIdsArgs = useMemo(
575
+ () => ({tableId, cellId, descending, offset, limit, sorter}),
576
+ [tableId, cellId, descending, offset, limit, sorter],
577
+ );
558
578
  useListener(
559
579
  SORTED_ROW_IDS,
560
580
  useStoreOrStoreById(storeOrStoreId),
561
581
  listener,
562
582
  listenerDeps,
563
- [tableId, cellId, descending, offset, limit],
583
+ isUndefined(sorter)
584
+ ? [tableId, cellId, descending, offset, limit]
585
+ : [sortedRowIdsArgs],
564
586
  mutator,
565
587
  );
588
+ };
566
589
  const useCreateStore = (create, createDeps = EMPTY_ARRAY) =>
567
590
  useMemo(create, createDeps);
568
591
  const useStoreIds = () => useThingIds(OFFSET_STORE);
@@ -632,6 +655,7 @@ const useSortedRowIds = (
632
655
  descending,
633
656
  offset,
634
657
  limit,
658
+ sorterOrStoreOrStoreId,
635
659
  storeOrStoreId,
636
660
  ) =>
637
661
  useSortedRowIdsImpl(
@@ -642,6 +666,7 @@ const useSortedRowIds = (
642
666
  tableIdOrArgs.descending ?? false,
643
667
  tableIdOrArgs.offset ?? 0,
644
668
  tableIdOrArgs.limit,
669
+ tableIdOrArgs.sorter,
645
670
  cellIdOrStoreOrStoreId,
646
671
  ]
647
672
  : [
@@ -650,7 +675,10 @@ const useSortedRowIds = (
650
675
  descending,
651
676
  offset,
652
677
  limit,
653
- storeOrStoreId,
678
+ isFunction(sorterOrStoreOrStoreId) ? sorterOrStoreOrStoreId : void 0,
679
+ isFunction(sorterOrStoreOrStoreId)
680
+ ? storeOrStoreId
681
+ : sorterOrStoreOrStoreId,
654
682
  ]),
655
683
  );
656
684
  const useHasRow = (tableId, rowId, storeOrStoreId) =>
@@ -1057,6 +1085,7 @@ const useSortedRowIdsListener = (
1057
1085
  tableIdOrArgs.descending ?? false,
1058
1086
  tableIdOrArgs.offset ?? 0,
1059
1087
  tableIdOrArgs.limit,
1088
+ tableIdOrArgs.sorter,
1060
1089
  cellIdOrListener,
1061
1090
  descendingOrListenerDeps,
1062
1091
  offsetOrMutator,
@@ -1068,6 +1097,7 @@ const useSortedRowIdsListener = (
1068
1097
  descendingOrListenerDeps,
1069
1098
  offsetOrMutator,
1070
1099
  limitOrStoreOrStoreId,
1100
+ void 0,
1071
1101
  listener,
1072
1102
  listenerDeps,
1073
1103
  mutator,
@@ -536,33 +536,56 @@ const useSortedRowIdsImpl = (
536
536
  descending,
537
537
  offset,
538
538
  limit,
539
+ sorter,
539
540
  storeOrStoreId,
540
- ) =>
541
- useListenable(
541
+ ) => {
542
+ const sortedRowIdsArgs = useMemo(
543
+ () => ({
544
+ tableId,
545
+ cellId,
546
+ descending: descending ?? false,
547
+ offset: offset ?? 0,
548
+ limit,
549
+ sorter,
550
+ }),
551
+ [tableId, cellId, descending, offset, limit, sorter],
552
+ );
553
+ return useListenable(
542
554
  SORTED_ROW_IDS,
543
555
  useStoreOrStoreById(storeOrStoreId),
544
556
  1 /* Array */,
545
- [tableId, cellId, descending, offset, limit],
557
+ isUndefined(sorter)
558
+ ? [tableId, cellId, descending, offset, limit]
559
+ : [sortedRowIdsArgs],
546
560
  );
561
+ };
547
562
  const useSortedRowIdsListenerImpl = (
548
563
  tableId,
549
564
  cellId,
550
565
  descending,
551
566
  offset,
552
567
  limit,
568
+ sorter,
553
569
  listener,
554
570
  listenerDeps,
555
571
  mutator,
556
572
  storeOrStoreId,
557
- ) =>
573
+ ) => {
574
+ const sortedRowIdsArgs = useMemo(
575
+ () => ({tableId, cellId, descending, offset, limit, sorter}),
576
+ [tableId, cellId, descending, offset, limit, sorter],
577
+ );
558
578
  useListener(
559
579
  SORTED_ROW_IDS,
560
580
  useStoreOrStoreById(storeOrStoreId),
561
581
  listener,
562
582
  listenerDeps,
563
- [tableId, cellId, descending, offset, limit],
583
+ isUndefined(sorter)
584
+ ? [tableId, cellId, descending, offset, limit]
585
+ : [sortedRowIdsArgs],
564
586
  mutator,
565
587
  );
588
+ };
566
589
  const useCreateStore = (create, createDeps = EMPTY_ARRAY) =>
567
590
  useMemo(create, createDeps);
568
591
  const useStoreIds = () => useThingIds(OFFSET_STORE);
@@ -632,6 +655,7 @@ const useSortedRowIds = (
632
655
  descending,
633
656
  offset,
634
657
  limit,
658
+ sorterOrStoreOrStoreId,
635
659
  storeOrStoreId,
636
660
  ) =>
637
661
  useSortedRowIdsImpl(
@@ -642,6 +666,7 @@ const useSortedRowIds = (
642
666
  tableIdOrArgs.descending ?? false,
643
667
  tableIdOrArgs.offset ?? 0,
644
668
  tableIdOrArgs.limit,
669
+ tableIdOrArgs.sorter,
645
670
  cellIdOrStoreOrStoreId,
646
671
  ]
647
672
  : [
@@ -650,7 +675,10 @@ const useSortedRowIds = (
650
675
  descending,
651
676
  offset,
652
677
  limit,
653
- storeOrStoreId,
678
+ isFunction(sorterOrStoreOrStoreId) ? sorterOrStoreOrStoreId : void 0,
679
+ isFunction(sorterOrStoreOrStoreId)
680
+ ? storeOrStoreId
681
+ : sorterOrStoreOrStoreId,
654
682
  ]),
655
683
  );
656
684
  const useHasRow = (tableId, rowId, storeOrStoreId) =>
@@ -1057,6 +1085,7 @@ const useSortedRowIdsListener = (
1057
1085
  tableIdOrArgs.descending ?? false,
1058
1086
  tableIdOrArgs.offset ?? 0,
1059
1087
  tableIdOrArgs.limit,
1088
+ tableIdOrArgs.sorter,
1060
1089
  cellIdOrListener,
1061
1090
  descendingOrListenerDeps,
1062
1091
  offsetOrMutator,
@@ -1068,6 +1097,7 @@ const useSortedRowIdsListener = (
1068
1097
  descendingOrListenerDeps,
1069
1098
  offsetOrMutator,
1070
1099
  limitOrStoreOrStoreId,
1100
+ void 0,
1071
1101
  listener,
1072
1102
  listenerDeps,
1073
1103
  mutator,