tinybase 7.0.0-beta.2 → 7.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.
- package/@types/common/index.d.ts +1 -1
- package/@types/mergeable-store/index.d.ts +1 -1
- package/@types/mergeable-store/with-schemas/index.d.ts +1 -1
- package/@types/persisters/persister-cr-sqlite-wasm/index.d.ts +4 -0
- package/@types/persisters/persister-cr-sqlite-wasm/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +4 -0
- package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-electric-sql/index.d.ts +4 -0
- package/@types/persisters/persister-electric-sql/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-expo-sqlite/index.d.ts +4 -0
- package/@types/persisters/persister-expo-sqlite/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-libsql/index.d.ts +4 -0
- package/@types/persisters/persister-libsql/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-pglite/index.d.ts +5 -1
- package/@types/persisters/persister-pglite/with-schemas/index.d.ts +5 -1
- package/@types/persisters/persister-postgres/index.d.ts +4 -0
- package/@types/persisters/persister-postgres/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-powersync/index.d.ts +4 -0
- package/@types/persisters/persister-powersync/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-react-native-sqlite/index.d.ts +4 -0
- package/@types/persisters/persister-react-native-sqlite/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-sqlite-bun/index.d.ts +4 -0
- package/@types/persisters/persister-sqlite-bun/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-sqlite-wasm/index.d.ts +4 -0
- package/@types/persisters/persister-sqlite-wasm/with-schemas/index.d.ts +4 -0
- package/@types/persisters/persister-sqlite3/index.d.ts +4 -0
- package/@types/persisters/persister-sqlite3/with-schemas/index.d.ts +4 -0
- package/@types/queries/index.d.ts +1 -1
- package/@types/store/index.d.ts +21 -19
- package/@types/store/with-schemas/index.d.ts +15 -13
- package/agents.md +62 -0
- package/checkpoints/index.js +8 -6
- package/checkpoints/with-schemas/index.js +8 -6
- package/common/index.js +4 -3
- package/common/with-schemas/index.js +4 -3
- package/index.js +60 -30
- package/indexes/index.js +7 -5
- package/indexes/with-schemas/index.js +7 -5
- package/mergeable-store/index.js +51 -21
- package/mergeable-store/with-schemas/index.js +51 -21
- package/metrics/index.js +7 -5
- package/metrics/with-schemas/index.js +7 -5
- package/min/checkpoints/index.js +1 -1
- package/min/checkpoints/index.js.gz +0 -0
- package/min/checkpoints/with-schemas/index.js +1 -1
- package/min/checkpoints/with-schemas/index.js.gz +0 -0
- package/min/common/index.js +1 -1
- package/min/common/index.js.gz +0 -0
- package/min/common/with-schemas/index.js +1 -1
- package/min/common/with-schemas/index.js.gz +0 -0
- package/min/index.js +1 -1
- package/min/index.js.gz +0 -0
- package/min/indexes/index.js +1 -1
- package/min/indexes/index.js.gz +0 -0
- package/min/indexes/with-schemas/index.js +1 -1
- package/min/indexes/with-schemas/index.js.gz +0 -0
- package/min/mergeable-store/index.js +1 -1
- package/min/mergeable-store/index.js.gz +0 -0
- package/min/mergeable-store/with-schemas/index.js +1 -1
- package/min/mergeable-store/with-schemas/index.js.gz +0 -0
- package/min/metrics/index.js +1 -1
- package/min/metrics/index.js.gz +0 -0
- package/min/metrics/with-schemas/index.js +1 -1
- package/min/metrics/with-schemas/index.js.gz +0 -0
- package/min/omni/index.js +1 -1
- package/min/omni/index.js.gz +0 -0
- package/min/omni/with-schemas/index.js +1 -1
- package/min/omni/with-schemas/index.js.gz +0 -0
- package/min/persisters/index.js +1 -1
- package/min/persisters/index.js.gz +0 -0
- package/min/persisters/persister-automerge/index.js +1 -1
- package/min/persisters/persister-automerge/index.js.gz +0 -0
- package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
- package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-browser/index.js +1 -1
- package/min/persisters/persister-browser/index.js.gz +0 -0
- package/min/persisters/persister-browser/with-schemas/index.js +1 -1
- package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
- package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
- package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
- package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
- package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
- package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-storage/index.js +1 -1
- package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
- package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-electric-sql/index.js +1 -1
- package/min/persisters/persister-electric-sql/index.js.gz +0 -0
- package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
- package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-expo-sqlite/index.js +1 -1
- package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
- package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
- package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-file/index.js +1 -1
- package/min/persisters/persister-file/index.js.gz +0 -0
- package/min/persisters/persister-file/with-schemas/index.js +1 -1
- package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-indexed-db/index.js +1 -1
- package/min/persisters/persister-indexed-db/index.js.gz +0 -0
- package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
- package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-libsql/index.js +1 -1
- package/min/persisters/persister-libsql/index.js.gz +0 -0
- package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
- package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-partykit-client/index.js +1 -1
- package/min/persisters/persister-partykit-client/index.js.gz +0 -0
- package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
- package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-partykit-server/index.js +1 -1
- package/min/persisters/persister-partykit-server/index.js.gz +0 -0
- package/min/persisters/persister-partykit-server/with-schemas/index.js +1 -1
- package/min/persisters/persister-partykit-server/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-pglite/index.js +1 -1
- package/min/persisters/persister-pglite/index.js.gz +0 -0
- package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
- package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-postgres/index.js +1 -1
- package/min/persisters/persister-postgres/index.js.gz +0 -0
- package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
- package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-powersync/index.js +1 -1
- package/min/persisters/persister-powersync/index.js.gz +0 -0
- package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
- package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-react-native-mmkv/index.js +1 -1
- package/min/persisters/persister-react-native-mmkv/index.js.gz +0 -0
- package/min/persisters/persister-react-native-mmkv/with-schemas/index.js +1 -1
- package/min/persisters/persister-react-native-mmkv/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-react-native-sqlite/index.js +1 -1
- package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
- package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
- package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-remote/index.js +1 -1
- package/min/persisters/persister-remote/index.js.gz +0 -0
- package/min/persisters/persister-remote/with-schemas/index.js +1 -1
- package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-bun/index.js +1 -1
- package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
- package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-wasm/index.js +1 -1
- package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
- package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-sqlite3/index.js +1 -1
- package/min/persisters/persister-sqlite3/index.js.gz +0 -0
- package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
- package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-yjs/index.js +1 -1
- package/min/persisters/persister-yjs/index.js.gz +0 -0
- package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
- package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
- package/min/persisters/with-schemas/index.js +1 -1
- package/min/persisters/with-schemas/index.js.gz +0 -0
- package/min/queries/index.js +1 -1
- package/min/queries/index.js.gz +0 -0
- package/min/queries/with-schemas/index.js +1 -1
- package/min/queries/with-schemas/index.js.gz +0 -0
- package/min/relationships/index.js +1 -1
- package/min/relationships/index.js.gz +0 -0
- package/min/relationships/with-schemas/index.js +1 -1
- package/min/relationships/with-schemas/index.js.gz +0 -0
- package/min/store/index.js +1 -1
- package/min/store/index.js.gz +0 -0
- package/min/store/with-schemas/index.js +1 -1
- package/min/store/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/index.js +1 -1
- package/min/synchronizers/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
- package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-local/index.js +1 -1
- package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/with-schemas/index.js +1 -1
- package/min/synchronizers/with-schemas/index.js.gz +0 -0
- package/min/ui-react/index.js +1 -1
- package/min/ui-react/index.js.gz +0 -0
- package/min/ui-react/with-schemas/index.js +1 -1
- package/min/ui-react/with-schemas/index.js.gz +0 -0
- package/min/ui-react-dom/index.js +1 -1
- package/min/ui-react-dom/index.js.gz +0 -0
- package/min/ui-react-dom/with-schemas/index.js +1 -1
- package/min/ui-react-dom/with-schemas/index.js.gz +0 -0
- package/min/ui-react-inspector/index.js +1 -1
- package/min/ui-react-inspector/index.js.gz +0 -0
- package/min/ui-react-inspector/with-schemas/index.js +1 -1
- package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
- package/min/with-schemas/index.js +1 -1
- package/min/with-schemas/index.js.gz +0 -0
- package/omni/index.js +78 -46
- package/omni/with-schemas/index.js +78 -46
- package/package.json +3 -3
- package/persisters/index.js +14 -10
- package/persisters/persister-automerge/index.js +12 -8
- package/persisters/persister-automerge/with-schemas/index.js +12 -8
- package/persisters/persister-browser/index.js +12 -8
- package/persisters/persister-browser/with-schemas/index.js +12 -8
- package/persisters/persister-cr-sqlite-wasm/index.js +14 -10
- package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +14 -10
- package/persisters/persister-durable-object-sql-storage/index.js +14 -10
- package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +14 -10
- package/persisters/persister-durable-object-storage/index.js +12 -8
- package/persisters/persister-durable-object-storage/with-schemas/index.js +12 -8
- package/persisters/persister-electric-sql/index.js +14 -10
- package/persisters/persister-electric-sql/with-schemas/index.js +14 -10
- package/persisters/persister-expo-sqlite/index.js +14 -10
- package/persisters/persister-expo-sqlite/with-schemas/index.js +14 -10
- package/persisters/persister-file/index.js +12 -8
- package/persisters/persister-file/with-schemas/index.js +12 -8
- package/persisters/persister-indexed-db/index.js +12 -8
- package/persisters/persister-indexed-db/with-schemas/index.js +12 -8
- package/persisters/persister-libsql/index.js +14 -10
- package/persisters/persister-libsql/with-schemas/index.js +14 -10
- package/persisters/persister-partykit-client/index.js +12 -8
- package/persisters/persister-partykit-client/with-schemas/index.js +12 -8
- package/persisters/persister-partykit-server/index.js +4 -3
- package/persisters/persister-partykit-server/with-schemas/index.js +4 -3
- package/persisters/persister-pglite/index.js +13 -9
- package/persisters/persister-pglite/with-schemas/index.js +13 -9
- package/persisters/persister-postgres/index.js +13 -9
- package/persisters/persister-postgres/with-schemas/index.js +13 -9
- package/persisters/persister-powersync/index.js +14 -10
- package/persisters/persister-powersync/with-schemas/index.js +14 -10
- package/persisters/persister-react-native-mmkv/index.js +12 -8
- package/persisters/persister-react-native-mmkv/with-schemas/index.js +12 -8
- package/persisters/persister-react-native-sqlite/index.js +14 -10
- package/persisters/persister-react-native-sqlite/with-schemas/index.js +14 -10
- package/persisters/persister-remote/index.js +14 -10
- package/persisters/persister-remote/with-schemas/index.js +14 -10
- package/persisters/persister-sqlite-bun/index.js +14 -10
- package/persisters/persister-sqlite-bun/with-schemas/index.js +14 -10
- package/persisters/persister-sqlite-wasm/index.js +14 -10
- package/persisters/persister-sqlite-wasm/with-schemas/index.js +14 -10
- package/persisters/persister-sqlite3/index.js +14 -10
- package/persisters/persister-sqlite3/with-schemas/index.js +14 -10
- package/persisters/persister-yjs/index.js +18 -12
- package/persisters/persister-yjs/with-schemas/index.js +18 -12
- package/persisters/with-schemas/index.js +14 -10
- package/queries/index.js +17 -12
- package/queries/with-schemas/index.js +17 -12
- package/readme.md +2 -2
- package/relationships/index.js +7 -5
- package/relationships/with-schemas/index.js +7 -5
- package/releases.md +100 -12
- package/store/index.js +48 -18
- package/store/with-schemas/index.js +48 -18
- package/synchronizers/index.js +13 -9
- package/synchronizers/synchronizer-broadcast-channel/index.js +14 -10
- package/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +14 -10
- package/synchronizers/synchronizer-local/index.js +14 -10
- package/synchronizers/synchronizer-local/with-schemas/index.js +14 -10
- package/synchronizers/synchronizer-ws-client/index.js +13 -9
- package/synchronizers/synchronizer-ws-client/with-schemas/index.js +13 -9
- package/synchronizers/synchronizer-ws-server/index.js +13 -9
- package/synchronizers/synchronizer-ws-server/with-schemas/index.js +13 -9
- package/synchronizers/synchronizer-ws-server-durable-object/index.js +13 -9
- package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +13 -9
- package/synchronizers/synchronizer-ws-server-simple/index.js +5 -4
- package/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +5 -4
- package/synchronizers/with-schemas/index.js +13 -9
- package/ui-react/index.js +9 -6
- package/ui-react/with-schemas/index.js +9 -6
- package/ui-react-dom/index.js +14 -7
- package/ui-react-dom/with-schemas/index.js +14 -7
- package/ui-react-inspector/index.js +53 -23
- package/ui-react-inspector/with-schemas/index.js +53 -23
- package/with-schemas/index.js +60 -30
package/releases.md
CHANGED
|
@@ -1,22 +1,113 @@
|
|
|
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="
|
|
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="v7-0">v7.0</h1><p>This important (and slightly breaking!) release adds support for <code>null</code> as a valid <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> and <a href="https://beta.tinybase.org/api/store/type-aliases/store/value/"><code>Value</code></a> type, alongside <code>string</code>, <code>number</code>, and <code>boolean</code>.</p><h2 id="null-type-support">Null Type Support</h2><p>You can now set Cells and <a href="https://beta.tinybase.org/api/store/type-aliases/store/values/"><code>Values</code></a> to <code>null</code>:</p>
|
|
2
2
|
|
|
3
3
|
```js
|
|
4
4
|
import {createStore} from 'tinybase';
|
|
5
|
+
|
|
6
|
+
const store = createStore();
|
|
7
|
+
store.setCell('pets', 'fido', 'species', 'dog');
|
|
8
|
+
store.setCell('pets', 'fido', 'color', null);
|
|
9
|
+
|
|
10
|
+
console.log(store.getCell('pets', 'fido', 'color'));
|
|
11
|
+
// -> null
|
|
12
|
+
|
|
13
|
+
console.log(store.hasCell('pets', 'fido', 'color'));
|
|
14
|
+
// -> true
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
<p>To allow <code>null</code> values in your schema, use the new <code>allowNull</code> property:</p>
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
store.setTablesSchema({
|
|
21
|
+
pets: {
|
|
22
|
+
species: {type: 'string'},
|
|
23
|
+
color: {type: 'string', allowNull: true},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
store.setCell('pets', 'fido', 'color', null);
|
|
28
|
+
// Valid because allowNull is true
|
|
29
|
+
|
|
30
|
+
store.setCell('pets', 'fido', 'species', null);
|
|
31
|
+
// Invalid - species does not allow null
|
|
32
|
+
|
|
33
|
+
store.delSchema();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
<h2 id="important-distinction-null-vs-undefined">Important Distinction: <code>null</code> vs <code>undefined</code></h2><p>It's crucial to understand the difference between <code>null</code> and <code>undefined</code> in TinyBase:</p><ul><li><code>null</code> is an explicit value. A <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> set to <code>null</code> exists in the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>.</li><li><code>undefined</code> means the <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> does not exist in the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>.</li></ul><p>This means that the <a href="https://beta.tinybase.org/api/store/interfaces/store/store/methods/getter/hascell/"><code>hasCell</code></a> method will return <code>true</code> for a <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> with a <code>null</code> value:</p>
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
store.setCell('pets', 'fido', 'color', null);
|
|
40
|
+
console.log(store.hasCell('pets', 'fido', 'color'));
|
|
41
|
+
// -> true
|
|
42
|
+
|
|
43
|
+
store.delCell('pets', 'fido', 'color');
|
|
44
|
+
console.log(store.hasCell('pets', 'fido', 'color'));
|
|
45
|
+
// -> false
|
|
46
|
+
|
|
47
|
+
store.delTables();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
<h2 id="breaking-change-database-persistence">Breaking Change: <a href="https://beta.tinybase.org/guides/persistence/database-persistence/">Database Persistence</a></h2><p><strong>Important:</strong> This release includes a breaking change for applications using database persisters (the <a href="https://beta.tinybase.org/api/persister-sqlite3/interfaces/persister/sqlite3persister/"><code>Sqlite3Persister</code></a>, <a href="https://beta.tinybase.org/api/persister-postgres/interfaces/persister/postgrespersister/"><code>PostgresPersister</code></a>, or <a href="https://beta.tinybase.org/api/persister-pglite/interfaces/persister/pglitepersister/"><code>PglitePersister</code></a> interfaces, for example).</p><p>SQL <code>NULL</code> values are now loaded as TinyBase <code>null</code> values. Previously, SQL <code>NULL</code> would result in Cells being absent from the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a>. Now, SQL <code>NULL</code> maps directly to TinyBase <code>null</code>, which means:</p><ul><li><a href="https://beta.tinybase.org/api/store/type-aliases/store/tables/"><code>Tables</code></a> loaded from SQL databases will be <strong>dense</strong> rather than <strong>sparse</strong></li><li>Every <a href="https://beta.tinybase.org/api/store/type-aliases/store/row/"><code>Row</code></a> will have every <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> <a href="https://beta.tinybase.org/api/common/type-aliases/identity/id/"><code>Id</code></a> present in the table schema</li><li>Cells that were SQL <code>NULL</code> will have the value <code>null</code></li></ul><p>Example of the roundtrip transformation via a SQLite database:</p>
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
|
|
54
|
+
import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';
|
|
55
|
+
|
|
56
|
+
const sqlite3 = await sqlite3InitModule();
|
|
57
|
+
let db = new sqlite3.oo1.DB(':memory:', 'c');
|
|
58
|
+
|
|
59
|
+
store.setTable('pets', {
|
|
60
|
+
fido: {species: 'dog'},
|
|
61
|
+
felix: {species: 'cat', color: 'black'},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const tabularPersister = createSqliteWasmPersister(store, sqlite3, db, {
|
|
65
|
+
mode: 'tabular',
|
|
66
|
+
tables: {save: {pets: 'pets'}, load: {pets: 'pets'}},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await tabularPersister.save();
|
|
70
|
+
// After saving the the SQL database:
|
|
71
|
+
// SQL table: fido (species: 'dog', color: NULL)
|
|
72
|
+
// felix (species: 'cat', color: 'black')
|
|
73
|
+
|
|
74
|
+
await tabularPersister.load();
|
|
75
|
+
// After loading again, the Store now has a dense table with an explicit null:
|
|
76
|
+
|
|
77
|
+
console.log(store.getRow('pets', 'fido'));
|
|
78
|
+
// -> {species: 'dog', color: null}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
<p>This is the correct semantic mapping since SQL databases have fixed schemas where every row must account for every column. See the <a href="https://beta.tinybase.org/guides/persistence/database-persistence/">Database Persistence</a> guide for more details.</p><h2 id="migration-guide">Migration Guide</h2><p>If you are using database persisters, you should:</p><ol><li><p><strong>Review your data access patterns</strong>: If you were checking <code>hasCell(...) === false</code> to detect missing data, you now need to check <code>getCell(...) === null</code> for null values.</p></li><li><p><strong>Update your schemas</strong>: Add <code>allowNull: true</code> to <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> definitions that should permit null values:</p></li></ol>
|
|
82
|
+
|
|
83
|
+
```js yolo
|
|
84
|
+
store.setTablesSchema({
|
|
85
|
+
pets: {
|
|
86
|
+
species: {type: 'string'},
|
|
87
|
+
color: {type: 'string', allowNull: true},
|
|
88
|
+
age: {type: 'number', allowNull: true},
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
<ol start="3"><li><strong>Consider memory implications</strong>: Dense tables consume more memory than sparse tables. If you have large tables with many optional Cells, this could be significant.</li></ol><hr><h1 id="v6-7">v6.7</h1><p>This release includes support for the Origin Private File System (OPFS) in a browser. The <a href="https://beta.tinybase.org/api/persister-browser/functions/creation/createopfspersister/"><code>createOpfsPersister</code></a> function is the main entry point, and is available in the existing <a href="https://beta.tinybase.org/api/persister-browser/"><code>persister-browser</code></a> module:</p>
|
|
94
|
+
|
|
95
|
+
```js
|
|
5
96
|
import {createOpfsPersister} from 'tinybase/persisters/persister-browser';
|
|
6
97
|
|
|
7
98
|
const opfs = await navigator.storage.getDirectory();
|
|
8
99
|
const handle = await opfs.getFileHandle('tinybase.json', {create: true});
|
|
9
100
|
|
|
10
|
-
|
|
11
|
-
const
|
|
101
|
+
store.delTables().setTables({pets: {fido: {species: 'dog'}}});
|
|
102
|
+
const opfsPersister = createOpfsPersister(store, handle);
|
|
12
103
|
|
|
13
|
-
await
|
|
104
|
+
await opfsPersister.save();
|
|
14
105
|
// Store JSON will be saved to the OPFS file.
|
|
15
106
|
|
|
16
|
-
await
|
|
107
|
+
await opfsPersister.load();
|
|
17
108
|
// Store JSON will be loaded from the OPFS file.
|
|
18
109
|
|
|
19
|
-
await
|
|
110
|
+
await opfsPersister.destroy();
|
|
20
111
|
```
|
|
21
112
|
|
|
22
113
|
<p>That's it! If you've used other TinyBase persisters, this API should be easy and familiar to use.</p><p>One caveat: observability in OPFS is not yet standardized in browsers. This means that the auto-load functionality of the persister may not work as expected, although a best effort is made using the experimental FileSystemObserverAPI, so please let us know how that works!</p><hr><h1 id="v6-6">v6.6</h1><p>This release improves the Inspector tool, making it easier to debug, inspect, and mutate your TinyBase stores.</p><p><img src="https://beta.tinybase.org/inspector.webp" alt="Inspector" title="Inspector"></p><p>As well as a modernized UI, new in this release is the ability to create, duplicate, or delete tables, rows, values and cells directly within the Inspector. Press the 'pencil' icon to start editing items, and then hover over the new icons to see how to manipulate the data.</p><p>See the <a href="https://beta.tinybase.org/guides/inspecting-data/">Inspecting Data</a> guide for more information about how to use the Inspector in your application during development.</p><h1 id="v6-5">v6.5</h1><p>This release includes the new <a href="https://beta.tinybase.org/api/persister-react-native-mmkv/"><code>persister-react-native-mmkv</code></a> module, which allows you to persist data in a React Native MMKV store via the <a href="https://github.com/mrousavy/react-native-mmkv">react-native-mmkv</a> library.</p><p>Usage should be as simple as this:</p>
|
|
@@ -26,7 +117,7 @@ import {createMMKV} from 'react-native-mmkv';
|
|
|
26
117
|
import {createReactNativeMmkvPersister} from 'tinybase/persisters/persister-react-native-mmkv';
|
|
27
118
|
|
|
28
119
|
const storage = createMMKV();
|
|
29
|
-
|
|
120
|
+
store.setTables({pets: {fido: {species: 'dog'}}});
|
|
30
121
|
const persister = createReactNativeMmkvPersister(store, storage);
|
|
31
122
|
|
|
32
123
|
await persister.save();
|
|
@@ -422,12 +513,9 @@ root.unmount();
|
|
|
422
513
|
<p>The <a href="https://beta.tinybase.org/api/ui-react-dom/functions/store-components/editablecellview/"><code>EditableCellView</code></a> component and <a href="https://beta.tinybase.org/api/ui-react-dom/functions/store-components/editablevalueview/"><code>EditableValueView</code></a> component are interactive input controls for updating <a href="https://beta.tinybase.org/api/store/type-aliases/store/cell/"><code>Cell</code></a> and <a href="https://beta.tinybase.org/api/store/type-aliases/store/value/"><code>Value</code></a> content respectively. You can generally use them across your table views by adding the <code>editable</code> prop to your table component.</p><h2 id="the-new-inspector">The new Inspector</h2><p><img src="https://beta.tinybase.org/store-inspector.webp" alt="Inspector" title="Inspector"></p><p>The new <a href="https://beta.tinybase.org/api/the-essentials/using-react/inspector/"><code>Inspector</code></a> component allows you to view, understand, and edit the content of a <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> in a debug web environment. Try it out in most of the demos on the site, including the <a href="https://beta.tinybase.org/demos/movie-database/">Movie Database</a> demo, pictured. This requires a debug build of the new <a href="https://beta.tinybase.org/api/ui-react-dom/"><code>ui-react-dom</code></a> module, which is now also included in the UMD distribution.</p><p>Also in this release, the <a href="https://beta.tinybase.org/api/queries/interfaces/queries/queries/methods/result/getresulttablecellids/"><code>getResultTableCellIds</code></a> method and <a href="https://beta.tinybase.org/api/queries/interfaces/queries/queries/methods/listener/addresulttablecellidslistener/"><code>addResultTableCellIdsListener</code></a> method have been added to the <a href="https://beta.tinybase.org/api/queries/interfaces/queries/queries/"><code>Queries</code></a> object. The equivalent <a href="https://beta.tinybase.org/api/ui-react/functions/queries-hooks/useresulttablecellids/"><code>useResultTableCellIds</code></a> hook and <a href="https://beta.tinybase.org/api/ui-react/functions/queries-hooks/useresulttablecellidslistener/"><code>useResultTableCellIdsListener</code></a> hook have also been added to <a href="https://beta.tinybase.org/api/ui-react/"><code>ui-react</code></a> module. A number of other minor React hooks have been added to support the components above.</p><p><a href="https://beta.tinybase.org/demos/">Demos</a> have been updated to demonstrate the <a href="https://beta.tinybase.org/api/ui-react-dom/"><code>ui-react-dom</code></a> module and the <a href="https://beta.tinybase.org/api/the-essentials/using-react/inspector/"><code>Inspector</code></a> component where appropriate.</p><p>(NB: Previous to v5.0, this component was called <code>StoreInspector</code>.)</p><hr><h1 id="v4-0">v4.0</h1><p>This major release provides <a href="https://beta.tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> modules that connect TinyBase to SQLite databases (in both browser and server contexts), and CRDT frameworks that can provide synchronization and local-first reconciliation:</p><div class="table"><table><thead><tr><th>Module</th><th>Function</th><th>Storage</th></tr></thead><tbody><tr><td><a href="https://beta.tinybase.org/api/persister-sqlite3/"><code>persister-sqlite3</code></a></td><td><a href="https://beta.tinybase.org/api/persister-sqlite3/functions/creation/createsqlite3persister/"><code>createSqlite3Persister</code></a></td><td>SQLite in Node, via <a href="https://github.com/TryGhost/node-sqlite3">sqlite3</a></td></tr><tr><td><a href="https://beta.tinybase.org/api/persister-sqlite-wasm/"><code>persister-sqlite-wasm</code></a></td><td><a href="https://beta.tinybase.org/api/the-essentials/persisting-stores/createsqlitewasmpersister/"><code>createSqliteWasmPersister</code></a></td><td>SQLite in a browser, via <a href="https://github.com/tomayac/sqlite-wasm">sqlite-wasm</a></td></tr><tr><td><a href="https://beta.tinybase.org/api/persister-cr-sqlite-wasm/"><code>persister-cr-sqlite-wasm</code></a></td><td><a href="https://beta.tinybase.org/api/persister-cr-sqlite-wasm/functions/creation/createcrsqlitewasmpersister/"><code>createCrSqliteWasmPersister</code></a></td><td>SQLite CRDTs, via <a href="https://github.com/vlcn-io/cr-sqlite">cr-sqlite-wasm</a></td></tr><tr><td><a href="https://beta.tinybase.org/api/persister-yjs/"><code>persister-yjs</code></a></td><td><a href="https://beta.tinybase.org/api/persister-yjs/functions/creation/createyjspersister/"><code>createYjsPersister</code></a></td><td>Yjs CRDTs, via <a href="https://github.com/yjs/yjs">yjs</a></td></tr><tr><td><a href="https://beta.tinybase.org/api/persister-automerge/"><code>persister-automerge</code></a></td><td><a href="https://beta.tinybase.org/api/the-essentials/persisting-stores/createsqlitewasmpersister/"><code>createSqliteWasmPersister</code></a></td><td>Automerge CRDTs, via <a href="https://github.com/automerge/automerge-repo">automerge-repo</a></td></tr></tbody></table></div><p>See the <a href="https://beta.tinybase.org/guides/persistence/database-persistence/">Database Persistence</a> guide for details on how to work with SQLite databases, and the <a href="https://beta.tinybase.org/guides/schemas-and-persistence/synchronizing-data/">Synchronizing Data</a> guide for more complex synchronization with the CRDT frameworks.</p><h2 id="sqlite-databases">SQLite databases</h2><p>You can persist <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> data to a database with either a JSON serialization or tabular mapping. (See the <a href="https://beta.tinybase.org/api/persisters/type-aliases/configuration/databasepersisterconfig/"><code>DatabasePersisterConfig</code></a> documentation for more details).</p><p>For example, this creates a <a href="https://beta.tinybase.org/api/the-essentials/persisting-stores/persister/"><code>Persister</code></a> object and saves and loads the <a href="https://beta.tinybase.org/api/the-essentials/creating-stores/store/"><code>Store</code></a> to and from a local SQLite database. It uses an explicit tabular one-to-one mapping for the 'pets' table:</p>
|
|
423
514
|
|
|
424
515
|
```js
|
|
425
|
-
import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
|
|
426
|
-
import {createSqliteWasmPersister} from 'tinybase/persisters/persister-sqlite-wasm';
|
|
427
|
-
|
|
428
|
-
const sqlite3 = await sqlite3InitModule();
|
|
429
|
-
const db = new sqlite3.oo1.DB(':memory:', 'c');
|
|
430
516
|
store.setTables({pets: {fido: {species: 'dog'}}});
|
|
517
|
+
|
|
518
|
+
db = new sqlite3.oo1.DB(':memory:', 'c');
|
|
431
519
|
const sqlitePersister = createSqliteWasmPersister(store, sqlite3, db, {
|
|
432
520
|
mode: 'tabular',
|
|
433
521
|
tables: {load: {pets: 'pets'}, save: {pets: 'pets'}},
|
package/store/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const NUMBER = getTypeOf(0);
|
|
|
6
6
|
const FUNCTION = getTypeOf(getTypeOf);
|
|
7
7
|
const TYPE = 'type';
|
|
8
8
|
const DEFAULT = 'default';
|
|
9
|
+
const ALLOW_NULL = 'allowNull';
|
|
9
10
|
const LISTENER = 'Listener';
|
|
10
11
|
const ADD = 'add';
|
|
11
12
|
const HAS = 'Has';
|
|
@@ -23,11 +24,15 @@ const VALUES = VALUE + 's';
|
|
|
23
24
|
const VALUE_IDS = VALUE + IDS;
|
|
24
25
|
const id = (key) => EMPTY_STRING + key;
|
|
25
26
|
|
|
27
|
+
const getIfNotFunction = (predicate) => (value, then, otherwise) =>
|
|
28
|
+
predicate(value) ? otherwise?.() : then(value);
|
|
26
29
|
const isFiniteNumber = isFinite;
|
|
27
30
|
const isInstanceOf = (thing, cls) => thing instanceof cls;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
+
const isNullish = (thing) => thing == null;
|
|
32
|
+
const isUndefined = (thing) => thing === void 0;
|
|
33
|
+
const isNull = (thing) => thing === null;
|
|
34
|
+
const ifNotNullish = getIfNotFunction(isNullish);
|
|
35
|
+
const ifNotUndefined = getIfNotFunction(isUndefined);
|
|
31
36
|
const isTypeStringOrBoolean = (type) => type == STRING || type == BOOLEAN;
|
|
32
37
|
const isFunction = (thing) => getTypeOf(thing) == FUNCTION;
|
|
33
38
|
const isArray = (thing) => Array.isArray(thing);
|
|
@@ -56,6 +61,9 @@ const arrayPush = (array, ...values) => array.push(...values);
|
|
|
56
61
|
const arrayShift = (array) => array.shift();
|
|
57
62
|
|
|
58
63
|
const getCellOrValueType = (cellOrValue) => {
|
|
64
|
+
if (isNull(cellOrValue)) {
|
|
65
|
+
return 'null';
|
|
66
|
+
}
|
|
59
67
|
const type = getTypeOf(cellOrValue);
|
|
60
68
|
return isTypeStringOrBoolean(type) ||
|
|
61
69
|
(type == NUMBER && isFiniteNumber(cellOrValue))
|
|
@@ -87,12 +95,12 @@ const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
|
|
|
87
95
|
const objFrozen = object.isFrozen;
|
|
88
96
|
const objEntries = object.entries;
|
|
89
97
|
const isObject = (obj) =>
|
|
90
|
-
!
|
|
91
|
-
|
|
98
|
+
!isNullish(obj) &&
|
|
99
|
+
ifNotNullish(
|
|
92
100
|
getPrototypeOf(obj),
|
|
93
101
|
(objPrototype) =>
|
|
94
102
|
objPrototype == object.prototype ||
|
|
95
|
-
|
|
103
|
+
isNullish(getPrototypeOf(objPrototype)),
|
|
96
104
|
|
|
97
105
|
/* istanbul ignore next */
|
|
98
106
|
() => true,
|
|
@@ -115,7 +123,7 @@ const objSize = (obj) => size(objIds(obj));
|
|
|
115
123
|
const objIsEmpty = (obj) => isObject(obj) && objSize(obj) == 0;
|
|
116
124
|
const objValidate = (obj, validateChild, onInvalidObj, emptyIsValid = 0) => {
|
|
117
125
|
if (
|
|
118
|
-
|
|
126
|
+
isNullish(obj) ||
|
|
119
127
|
!isObject(obj) ||
|
|
120
128
|
(!emptyIsValid && objIsEmpty(obj)) ||
|
|
121
129
|
objFrozen(obj)
|
|
@@ -139,7 +147,7 @@ const mapForEach = (map, cb) =>
|
|
|
139
147
|
const mapMap = (coll, cb) =>
|
|
140
148
|
arrayMap([...(coll?.entries() ?? [])], ([key, value]) => cb(value, key));
|
|
141
149
|
const mapSet = (map, key, value) =>
|
|
142
|
-
|
|
150
|
+
value === void 0 ? (collDel(map, key), map) : map?.set(key, value);
|
|
143
151
|
const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
|
|
144
152
|
if (!collHas(map, key)) {
|
|
145
153
|
mapSet(map, key, getDefaultValue());
|
|
@@ -298,7 +306,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
298
306
|
const index = size(ids);
|
|
299
307
|
if (index == size(path)) {
|
|
300
308
|
listener(thing, ...ids, ...extraArgsGetter(ids));
|
|
301
|
-
} else if (
|
|
309
|
+
} else if (isNull(path[index])) {
|
|
302
310
|
arrayForEach(pathGetters[index]?.(...ids) ?? [], (id2) =>
|
|
303
311
|
callWithIds(...ids, id2),
|
|
304
312
|
);
|
|
@@ -383,14 +391,22 @@ const createStore = () => {
|
|
|
383
391
|
const validateValuesSchema = (valuesSchema) =>
|
|
384
392
|
objValidate(valuesSchema, validateCellOrValueSchema);
|
|
385
393
|
const validateCellOrValueSchema = (schema) => {
|
|
386
|
-
if (
|
|
394
|
+
if (
|
|
395
|
+
!objValidate(schema, (_child, id2) =>
|
|
396
|
+
arrayHas([TYPE, DEFAULT, ALLOW_NULL], id2),
|
|
397
|
+
)
|
|
398
|
+
) {
|
|
387
399
|
return false;
|
|
388
400
|
}
|
|
389
401
|
const type = schema[TYPE];
|
|
390
402
|
if (!isTypeStringOrBoolean(type) && type != NUMBER) {
|
|
391
403
|
return false;
|
|
392
404
|
}
|
|
393
|
-
|
|
405
|
+
const defaultValue = schema[DEFAULT];
|
|
406
|
+
if (isNull(defaultValue) && !schema[ALLOW_NULL]) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
if (!isNull(defaultValue) && getCellOrValueType(defaultValue) != type) {
|
|
394
410
|
objDel(schema, DEFAULT);
|
|
395
411
|
}
|
|
396
412
|
return true;
|
|
@@ -427,9 +443,19 @@ const createStore = () => {
|
|
|
427
443
|
? ifNotUndefined(
|
|
428
444
|
mapGet(mapGet(tablesSchemaMap, tableId), cellId),
|
|
429
445
|
(cellSchema) =>
|
|
430
|
-
|
|
431
|
-
?
|
|
432
|
-
|
|
446
|
+
isNull(cell)
|
|
447
|
+
? cellSchema[ALLOW_NULL]
|
|
448
|
+
? cell
|
|
449
|
+
: cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
|
|
450
|
+
: getCellOrValueType(cell) == cellSchema[TYPE]
|
|
451
|
+
? cell
|
|
452
|
+
: cellInvalid(
|
|
453
|
+
tableId,
|
|
454
|
+
rowId,
|
|
455
|
+
cellId,
|
|
456
|
+
cell,
|
|
457
|
+
cellSchema[DEFAULT],
|
|
458
|
+
),
|
|
433
459
|
() => cellInvalid(tableId, rowId, cellId, cell),
|
|
434
460
|
)
|
|
435
461
|
: isUndefined(getCellOrValueType(cell))
|
|
@@ -454,9 +480,13 @@ const createStore = () => {
|
|
|
454
480
|
? ifNotUndefined(
|
|
455
481
|
mapGet(valuesSchemaMap, valueId),
|
|
456
482
|
(valueSchema) =>
|
|
457
|
-
|
|
458
|
-
?
|
|
459
|
-
|
|
483
|
+
isNull(value)
|
|
484
|
+
? valueSchema[ALLOW_NULL]
|
|
485
|
+
? value
|
|
486
|
+
: valueInvalid(valueId, value, valueSchema[DEFAULT])
|
|
487
|
+
: getCellOrValueType(value) == valueSchema[TYPE]
|
|
488
|
+
? value
|
|
489
|
+
: valueInvalid(valueId, value, valueSchema[DEFAULT]),
|
|
460
490
|
() => valueInvalid(valueId, value),
|
|
461
491
|
)
|
|
462
492
|
: isUndefined(getCellOrValueType(value))
|
|
@@ -699,7 +729,7 @@ const createStore = () => {
|
|
|
699
729
|
mapSet(
|
|
700
730
|
cellIds,
|
|
701
731
|
cellId,
|
|
702
|
-
count != -addedOrRemoved ? count + addedOrRemoved :
|
|
732
|
+
count != -addedOrRemoved ? count + addedOrRemoved : void 0,
|
|
703
733
|
);
|
|
704
734
|
idsChanged(
|
|
705
735
|
mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, mapNew),
|
|
@@ -6,6 +6,7 @@ const NUMBER = getTypeOf(0);
|
|
|
6
6
|
const FUNCTION = getTypeOf(getTypeOf);
|
|
7
7
|
const TYPE = 'type';
|
|
8
8
|
const DEFAULT = 'default';
|
|
9
|
+
const ALLOW_NULL = 'allowNull';
|
|
9
10
|
const LISTENER = 'Listener';
|
|
10
11
|
const ADD = 'add';
|
|
11
12
|
const HAS = 'Has';
|
|
@@ -23,11 +24,15 @@ const VALUES = VALUE + 's';
|
|
|
23
24
|
const VALUE_IDS = VALUE + IDS;
|
|
24
25
|
const id = (key) => EMPTY_STRING + key;
|
|
25
26
|
|
|
27
|
+
const getIfNotFunction = (predicate) => (value, then, otherwise) =>
|
|
28
|
+
predicate(value) ? otherwise?.() : then(value);
|
|
26
29
|
const isFiniteNumber = isFinite;
|
|
27
30
|
const isInstanceOf = (thing, cls) => thing instanceof cls;
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
+
const isNullish = (thing) => thing == null;
|
|
32
|
+
const isUndefined = (thing) => thing === void 0;
|
|
33
|
+
const isNull = (thing) => thing === null;
|
|
34
|
+
const ifNotNullish = getIfNotFunction(isNullish);
|
|
35
|
+
const ifNotUndefined = getIfNotFunction(isUndefined);
|
|
31
36
|
const isTypeStringOrBoolean = (type) => type == STRING || type == BOOLEAN;
|
|
32
37
|
const isFunction = (thing) => getTypeOf(thing) == FUNCTION;
|
|
33
38
|
const isArray = (thing) => Array.isArray(thing);
|
|
@@ -56,6 +61,9 @@ const arrayPush = (array, ...values) => array.push(...values);
|
|
|
56
61
|
const arrayShift = (array) => array.shift();
|
|
57
62
|
|
|
58
63
|
const getCellOrValueType = (cellOrValue) => {
|
|
64
|
+
if (isNull(cellOrValue)) {
|
|
65
|
+
return 'null';
|
|
66
|
+
}
|
|
59
67
|
const type = getTypeOf(cellOrValue);
|
|
60
68
|
return isTypeStringOrBoolean(type) ||
|
|
61
69
|
(type == NUMBER && isFiniteNumber(cellOrValue))
|
|
@@ -87,12 +95,12 @@ const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
|
|
|
87
95
|
const objFrozen = object.isFrozen;
|
|
88
96
|
const objEntries = object.entries;
|
|
89
97
|
const isObject = (obj) =>
|
|
90
|
-
!
|
|
91
|
-
|
|
98
|
+
!isNullish(obj) &&
|
|
99
|
+
ifNotNullish(
|
|
92
100
|
getPrototypeOf(obj),
|
|
93
101
|
(objPrototype) =>
|
|
94
102
|
objPrototype == object.prototype ||
|
|
95
|
-
|
|
103
|
+
isNullish(getPrototypeOf(objPrototype)),
|
|
96
104
|
|
|
97
105
|
/* istanbul ignore next */
|
|
98
106
|
() => true,
|
|
@@ -115,7 +123,7 @@ const objSize = (obj) => size(objIds(obj));
|
|
|
115
123
|
const objIsEmpty = (obj) => isObject(obj) && objSize(obj) == 0;
|
|
116
124
|
const objValidate = (obj, validateChild, onInvalidObj, emptyIsValid = 0) => {
|
|
117
125
|
if (
|
|
118
|
-
|
|
126
|
+
isNullish(obj) ||
|
|
119
127
|
!isObject(obj) ||
|
|
120
128
|
(!emptyIsValid && objIsEmpty(obj)) ||
|
|
121
129
|
objFrozen(obj)
|
|
@@ -139,7 +147,7 @@ const mapForEach = (map, cb) =>
|
|
|
139
147
|
const mapMap = (coll, cb) =>
|
|
140
148
|
arrayMap([...(coll?.entries() ?? [])], ([key, value]) => cb(value, key));
|
|
141
149
|
const mapSet = (map, key, value) =>
|
|
142
|
-
|
|
150
|
+
value === void 0 ? (collDel(map, key), map) : map?.set(key, value);
|
|
143
151
|
const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
|
|
144
152
|
if (!collHas(map, key)) {
|
|
145
153
|
mapSet(map, key, getDefaultValue());
|
|
@@ -298,7 +306,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
298
306
|
const index = size(ids);
|
|
299
307
|
if (index == size(path)) {
|
|
300
308
|
listener(thing, ...ids, ...extraArgsGetter(ids));
|
|
301
|
-
} else if (
|
|
309
|
+
} else if (isNull(path[index])) {
|
|
302
310
|
arrayForEach(pathGetters[index]?.(...ids) ?? [], (id2) =>
|
|
303
311
|
callWithIds(...ids, id2),
|
|
304
312
|
);
|
|
@@ -383,14 +391,22 @@ const createStore = () => {
|
|
|
383
391
|
const validateValuesSchema = (valuesSchema) =>
|
|
384
392
|
objValidate(valuesSchema, validateCellOrValueSchema);
|
|
385
393
|
const validateCellOrValueSchema = (schema) => {
|
|
386
|
-
if (
|
|
394
|
+
if (
|
|
395
|
+
!objValidate(schema, (_child, id2) =>
|
|
396
|
+
arrayHas([TYPE, DEFAULT, ALLOW_NULL], id2),
|
|
397
|
+
)
|
|
398
|
+
) {
|
|
387
399
|
return false;
|
|
388
400
|
}
|
|
389
401
|
const type = schema[TYPE];
|
|
390
402
|
if (!isTypeStringOrBoolean(type) && type != NUMBER) {
|
|
391
403
|
return false;
|
|
392
404
|
}
|
|
393
|
-
|
|
405
|
+
const defaultValue = schema[DEFAULT];
|
|
406
|
+
if (isNull(defaultValue) && !schema[ALLOW_NULL]) {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
if (!isNull(defaultValue) && getCellOrValueType(defaultValue) != type) {
|
|
394
410
|
objDel(schema, DEFAULT);
|
|
395
411
|
}
|
|
396
412
|
return true;
|
|
@@ -427,9 +443,19 @@ const createStore = () => {
|
|
|
427
443
|
? ifNotUndefined(
|
|
428
444
|
mapGet(mapGet(tablesSchemaMap, tableId), cellId),
|
|
429
445
|
(cellSchema) =>
|
|
430
|
-
|
|
431
|
-
?
|
|
432
|
-
|
|
446
|
+
isNull(cell)
|
|
447
|
+
? cellSchema[ALLOW_NULL]
|
|
448
|
+
? cell
|
|
449
|
+
: cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
|
|
450
|
+
: getCellOrValueType(cell) == cellSchema[TYPE]
|
|
451
|
+
? cell
|
|
452
|
+
: cellInvalid(
|
|
453
|
+
tableId,
|
|
454
|
+
rowId,
|
|
455
|
+
cellId,
|
|
456
|
+
cell,
|
|
457
|
+
cellSchema[DEFAULT],
|
|
458
|
+
),
|
|
433
459
|
() => cellInvalid(tableId, rowId, cellId, cell),
|
|
434
460
|
)
|
|
435
461
|
: isUndefined(getCellOrValueType(cell))
|
|
@@ -454,9 +480,13 @@ const createStore = () => {
|
|
|
454
480
|
? ifNotUndefined(
|
|
455
481
|
mapGet(valuesSchemaMap, valueId),
|
|
456
482
|
(valueSchema) =>
|
|
457
|
-
|
|
458
|
-
?
|
|
459
|
-
|
|
483
|
+
isNull(value)
|
|
484
|
+
? valueSchema[ALLOW_NULL]
|
|
485
|
+
? value
|
|
486
|
+
: valueInvalid(valueId, value, valueSchema[DEFAULT])
|
|
487
|
+
: getCellOrValueType(value) == valueSchema[TYPE]
|
|
488
|
+
? value
|
|
489
|
+
: valueInvalid(valueId, value, valueSchema[DEFAULT]),
|
|
460
490
|
() => valueInvalid(valueId, value),
|
|
461
491
|
)
|
|
462
492
|
: isUndefined(getCellOrValueType(value))
|
|
@@ -699,7 +729,7 @@ const createStore = () => {
|
|
|
699
729
|
mapSet(
|
|
700
730
|
cellIds,
|
|
701
731
|
cellId,
|
|
702
|
-
count != -addedOrRemoved ? count + addedOrRemoved :
|
|
732
|
+
count != -addedOrRemoved ? count + addedOrRemoved : void 0,
|
|
703
733
|
);
|
|
704
734
|
idsChanged(
|
|
705
735
|
mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, mapNew),
|
package/synchronizers/index.js
CHANGED
|
@@ -4,15 +4,19 @@ const strSplit = (str, separator = EMPTY_STRING, limit) =>
|
|
|
4
4
|
str.split(separator, limit);
|
|
5
5
|
|
|
6
6
|
const promise = Promise;
|
|
7
|
+
const getIfNotFunction = (predicate) => (value, then, otherwise) =>
|
|
8
|
+
predicate(value) ? otherwise?.() : then(value);
|
|
7
9
|
const GLOBAL = globalThis;
|
|
8
10
|
const THOUSAND = 1e3;
|
|
9
11
|
const startTimeout = (callback, sec = 0) =>
|
|
10
12
|
setTimeout(callback, sec * THOUSAND);
|
|
11
13
|
const math = Math;
|
|
12
14
|
const mathFloor = math.floor;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
15
|
+
const isNullish = (thing) => thing == null;
|
|
16
|
+
const isUndefined = (thing) => thing === void 0;
|
|
17
|
+
const isNull = (thing) => thing === null;
|
|
18
|
+
const ifNotNullish = getIfNotFunction(isNullish);
|
|
19
|
+
const ifNotUndefined = getIfNotFunction(isUndefined);
|
|
16
20
|
const isArray = (thing) => Array.isArray(thing);
|
|
17
21
|
const size = (arrayOrString) => arrayOrString.length;
|
|
18
22
|
const test = (regex, subject) => regex.test(subject);
|
|
@@ -46,12 +50,12 @@ const object = Object;
|
|
|
46
50
|
const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
|
|
47
51
|
const objEntries = object.entries;
|
|
48
52
|
const isObject = (obj) =>
|
|
49
|
-
!
|
|
50
|
-
|
|
53
|
+
!isNullish(obj) &&
|
|
54
|
+
ifNotNullish(
|
|
51
55
|
getPrototypeOf(obj),
|
|
52
56
|
(objPrototype) =>
|
|
53
57
|
objPrototype == object.prototype ||
|
|
54
|
-
|
|
58
|
+
isNullish(getPrototypeOf(objPrototype)),
|
|
55
59
|
|
|
56
60
|
/* istanbul ignore next */
|
|
57
61
|
() => true,
|
|
@@ -74,7 +78,7 @@ const objEnsure = (obj, id, getDefaultValue) => {
|
|
|
74
78
|
const mapNew = (entries) => new Map(entries);
|
|
75
79
|
const mapGet = (map, key) => map?.get(key);
|
|
76
80
|
const mapSet = (map, key, value) =>
|
|
77
|
-
|
|
81
|
+
value === void 0 ? (collDel(map, key), map) : map?.set(key, value);
|
|
78
82
|
const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
|
|
79
83
|
if (!collHas(map, key)) {
|
|
80
84
|
mapSet(map, key, getDefaultValue());
|
|
@@ -207,7 +211,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
207
211
|
const index = size(ids);
|
|
208
212
|
if (index == size(path)) {
|
|
209
213
|
listener(thing, ...ids, ...extraArgsGetter(ids));
|
|
210
|
-
} else if (
|
|
214
|
+
} else if (isNull(path[index])) {
|
|
211
215
|
arrayForEach(pathGetters[index]?.(...ids) ?? [], (id2) =>
|
|
212
216
|
callWithIds(...ids, id2),
|
|
213
217
|
);
|
|
@@ -644,7 +648,7 @@ const createCustomSynchronizer = (
|
|
|
644
648
|
ifNotUndefined(
|
|
645
649
|
mapGet(pendingRequests, transactionOrRequestId),
|
|
646
650
|
([toClientId, handleResponse]) =>
|
|
647
|
-
|
|
651
|
+
isNull(toClientId) || toClientId == fromClientId
|
|
648
652
|
? handleResponse(body, fromClientId)
|
|
649
653
|
: /* istanbul ignore next */
|
|
650
654
|
0,
|
|
@@ -4,15 +4,19 @@ const strSplit = (str, separator = EMPTY_STRING, limit) =>
|
|
|
4
4
|
str.split(separator, limit);
|
|
5
5
|
|
|
6
6
|
const promise = Promise;
|
|
7
|
+
const getIfNotFunction = (predicate) => (value, then, otherwise) =>
|
|
8
|
+
predicate(value) ? otherwise?.() : then(value);
|
|
7
9
|
const GLOBAL = globalThis;
|
|
8
10
|
const THOUSAND = 1e3;
|
|
9
11
|
const startTimeout = (callback, sec = 0) =>
|
|
10
12
|
setTimeout(callback, sec * THOUSAND);
|
|
11
13
|
const math = Math;
|
|
12
14
|
const mathFloor = math.floor;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
15
|
+
const isNullish = (thing) => thing == null;
|
|
16
|
+
const isUndefined = (thing) => thing === void 0;
|
|
17
|
+
const isNull = (thing) => thing === null;
|
|
18
|
+
const ifNotNullish = getIfNotFunction(isNullish);
|
|
19
|
+
const ifNotUndefined = getIfNotFunction(isUndefined);
|
|
16
20
|
const isArray = (thing) => Array.isArray(thing);
|
|
17
21
|
const size = (arrayOrString) => arrayOrString.length;
|
|
18
22
|
const test = (regex, subject) => regex.test(subject);
|
|
@@ -46,12 +50,12 @@ const object = Object;
|
|
|
46
50
|
const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
|
|
47
51
|
const objEntries = object.entries;
|
|
48
52
|
const isObject = (obj) =>
|
|
49
|
-
!
|
|
50
|
-
|
|
53
|
+
!isNullish(obj) &&
|
|
54
|
+
ifNotNullish(
|
|
51
55
|
getPrototypeOf(obj),
|
|
52
56
|
(objPrototype) =>
|
|
53
57
|
objPrototype == object.prototype ||
|
|
54
|
-
|
|
58
|
+
isNullish(getPrototypeOf(objPrototype)),
|
|
55
59
|
|
|
56
60
|
/* istanbul ignore next */
|
|
57
61
|
() => true,
|
|
@@ -74,7 +78,7 @@ const objEnsure = (obj, id, getDefaultValue) => {
|
|
|
74
78
|
const mapNew = (entries) => new Map(entries);
|
|
75
79
|
const mapGet = (map, key) => map?.get(key);
|
|
76
80
|
const mapSet = (map, key, value) =>
|
|
77
|
-
|
|
81
|
+
value === void 0 ? (collDel(map, key), map) : map?.set(key, value);
|
|
78
82
|
const mapEnsure = (map, key, getDefaultValue, hadExistingValue) => {
|
|
79
83
|
if (!collHas(map, key)) {
|
|
80
84
|
mapSet(map, key, getDefaultValue());
|
|
@@ -207,7 +211,7 @@ const getListenerFunctions = (getThing) => {
|
|
|
207
211
|
const index = size(ids);
|
|
208
212
|
if (index == size(path)) {
|
|
209
213
|
listener(thing, ...ids, ...extraArgsGetter(ids));
|
|
210
|
-
} else if (
|
|
214
|
+
} else if (isNull(path[index])) {
|
|
211
215
|
arrayForEach(pathGetters[index]?.(...ids) ?? [], (id2) =>
|
|
212
216
|
callWithIds(...ids, id2),
|
|
213
217
|
);
|
|
@@ -634,7 +638,7 @@ const createCustomSynchronizer = (
|
|
|
634
638
|
ifNotUndefined(
|
|
635
639
|
mapGet(pendingRequests, transactionOrRequestId),
|
|
636
640
|
([toClientId, handleResponse]) =>
|
|
637
|
-
|
|
641
|
+
isNull(toClientId) || toClientId == fromClientId
|
|
638
642
|
? handleResponse(body, fromClientId)
|
|
639
643
|
: /* istanbul ignore next */
|
|
640
644
|
0,
|
|
@@ -694,7 +698,7 @@ const createBroadcastChannelSynchronizer = (
|
|
|
694
698
|
channel.onmessage = ({
|
|
695
699
|
data: [fromClientId, toClientId, requestId, message, body],
|
|
696
700
|
}) =>
|
|
697
|
-
|
|
701
|
+
isNull(toClientId) || toClientId == clientId
|
|
698
702
|
? receive(fromClientId, requestId, message, body)
|
|
699
703
|
: 0;
|
|
700
704
|
};
|