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.
Files changed (292) hide show
  1. package/@types/common/index.d.ts +1 -1
  2. package/@types/mergeable-store/index.d.ts +1 -1
  3. package/@types/mergeable-store/with-schemas/index.d.ts +1 -1
  4. package/@types/persisters/persister-cr-sqlite-wasm/index.d.ts +4 -0
  5. package/@types/persisters/persister-cr-sqlite-wasm/with-schemas/index.d.ts +4 -0
  6. package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +4 -0
  7. package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +4 -0
  8. package/@types/persisters/persister-electric-sql/index.d.ts +4 -0
  9. package/@types/persisters/persister-electric-sql/with-schemas/index.d.ts +4 -0
  10. package/@types/persisters/persister-expo-sqlite/index.d.ts +4 -0
  11. package/@types/persisters/persister-expo-sqlite/with-schemas/index.d.ts +4 -0
  12. package/@types/persisters/persister-libsql/index.d.ts +4 -0
  13. package/@types/persisters/persister-libsql/with-schemas/index.d.ts +4 -0
  14. package/@types/persisters/persister-pglite/index.d.ts +5 -1
  15. package/@types/persisters/persister-pglite/with-schemas/index.d.ts +5 -1
  16. package/@types/persisters/persister-postgres/index.d.ts +4 -0
  17. package/@types/persisters/persister-postgres/with-schemas/index.d.ts +4 -0
  18. package/@types/persisters/persister-powersync/index.d.ts +4 -0
  19. package/@types/persisters/persister-powersync/with-schemas/index.d.ts +4 -0
  20. package/@types/persisters/persister-react-native-sqlite/index.d.ts +4 -0
  21. package/@types/persisters/persister-react-native-sqlite/with-schemas/index.d.ts +4 -0
  22. package/@types/persisters/persister-sqlite-bun/index.d.ts +4 -0
  23. package/@types/persisters/persister-sqlite-bun/with-schemas/index.d.ts +4 -0
  24. package/@types/persisters/persister-sqlite-wasm/index.d.ts +4 -0
  25. package/@types/persisters/persister-sqlite-wasm/with-schemas/index.d.ts +4 -0
  26. package/@types/persisters/persister-sqlite3/index.d.ts +4 -0
  27. package/@types/persisters/persister-sqlite3/with-schemas/index.d.ts +4 -0
  28. package/@types/queries/index.d.ts +1 -1
  29. package/@types/store/index.d.ts +21 -19
  30. package/@types/store/with-schemas/index.d.ts +15 -13
  31. package/agents.md +62 -0
  32. package/checkpoints/index.js +8 -6
  33. package/checkpoints/with-schemas/index.js +8 -6
  34. package/common/index.js +4 -3
  35. package/common/with-schemas/index.js +4 -3
  36. package/index.js +60 -30
  37. package/indexes/index.js +7 -5
  38. package/indexes/with-schemas/index.js +7 -5
  39. package/mergeable-store/index.js +51 -21
  40. package/mergeable-store/with-schemas/index.js +51 -21
  41. package/metrics/index.js +7 -5
  42. package/metrics/with-schemas/index.js +7 -5
  43. package/min/checkpoints/index.js +1 -1
  44. package/min/checkpoints/index.js.gz +0 -0
  45. package/min/checkpoints/with-schemas/index.js +1 -1
  46. package/min/checkpoints/with-schemas/index.js.gz +0 -0
  47. package/min/common/index.js +1 -1
  48. package/min/common/index.js.gz +0 -0
  49. package/min/common/with-schemas/index.js +1 -1
  50. package/min/common/with-schemas/index.js.gz +0 -0
  51. package/min/index.js +1 -1
  52. package/min/index.js.gz +0 -0
  53. package/min/indexes/index.js +1 -1
  54. package/min/indexes/index.js.gz +0 -0
  55. package/min/indexes/with-schemas/index.js +1 -1
  56. package/min/indexes/with-schemas/index.js.gz +0 -0
  57. package/min/mergeable-store/index.js +1 -1
  58. package/min/mergeable-store/index.js.gz +0 -0
  59. package/min/mergeable-store/with-schemas/index.js +1 -1
  60. package/min/mergeable-store/with-schemas/index.js.gz +0 -0
  61. package/min/metrics/index.js +1 -1
  62. package/min/metrics/index.js.gz +0 -0
  63. package/min/metrics/with-schemas/index.js +1 -1
  64. package/min/metrics/with-schemas/index.js.gz +0 -0
  65. package/min/omni/index.js +1 -1
  66. package/min/omni/index.js.gz +0 -0
  67. package/min/omni/with-schemas/index.js +1 -1
  68. package/min/omni/with-schemas/index.js.gz +0 -0
  69. package/min/persisters/index.js +1 -1
  70. package/min/persisters/index.js.gz +0 -0
  71. package/min/persisters/persister-automerge/index.js +1 -1
  72. package/min/persisters/persister-automerge/index.js.gz +0 -0
  73. package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
  74. package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
  75. package/min/persisters/persister-browser/index.js +1 -1
  76. package/min/persisters/persister-browser/index.js.gz +0 -0
  77. package/min/persisters/persister-browser/with-schemas/index.js +1 -1
  78. package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
  79. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  80. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  81. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  82. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  83. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  84. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  85. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  86. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  87. package/min/persisters/persister-durable-object-storage/index.js +1 -1
  88. package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
  89. package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
  90. package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
  91. package/min/persisters/persister-electric-sql/index.js +1 -1
  92. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  93. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  94. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  95. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  96. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  97. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  98. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  99. package/min/persisters/persister-file/index.js +1 -1
  100. package/min/persisters/persister-file/index.js.gz +0 -0
  101. package/min/persisters/persister-file/with-schemas/index.js +1 -1
  102. package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
  103. package/min/persisters/persister-indexed-db/index.js +1 -1
  104. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  105. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  106. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  107. package/min/persisters/persister-libsql/index.js +1 -1
  108. package/min/persisters/persister-libsql/index.js.gz +0 -0
  109. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  110. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  111. package/min/persisters/persister-partykit-client/index.js +1 -1
  112. package/min/persisters/persister-partykit-client/index.js.gz +0 -0
  113. package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
  114. package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
  115. package/min/persisters/persister-partykit-server/index.js +1 -1
  116. package/min/persisters/persister-partykit-server/index.js.gz +0 -0
  117. package/min/persisters/persister-partykit-server/with-schemas/index.js +1 -1
  118. package/min/persisters/persister-partykit-server/with-schemas/index.js.gz +0 -0
  119. package/min/persisters/persister-pglite/index.js +1 -1
  120. package/min/persisters/persister-pglite/index.js.gz +0 -0
  121. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  122. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  123. package/min/persisters/persister-postgres/index.js +1 -1
  124. package/min/persisters/persister-postgres/index.js.gz +0 -0
  125. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  126. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  127. package/min/persisters/persister-powersync/index.js +1 -1
  128. package/min/persisters/persister-powersync/index.js.gz +0 -0
  129. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  130. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  131. package/min/persisters/persister-react-native-mmkv/index.js +1 -1
  132. package/min/persisters/persister-react-native-mmkv/index.js.gz +0 -0
  133. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js +1 -1
  134. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js.gz +0 -0
  135. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  136. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  137. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  138. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  139. package/min/persisters/persister-remote/index.js +1 -1
  140. package/min/persisters/persister-remote/index.js.gz +0 -0
  141. package/min/persisters/persister-remote/with-schemas/index.js +1 -1
  142. package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
  143. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  144. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  145. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  146. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  147. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  148. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  149. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  150. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  151. package/min/persisters/persister-sqlite3/index.js +1 -1
  152. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  153. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  154. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  155. package/min/persisters/persister-yjs/index.js +1 -1
  156. package/min/persisters/persister-yjs/index.js.gz +0 -0
  157. package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
  158. package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
  159. package/min/persisters/with-schemas/index.js +1 -1
  160. package/min/persisters/with-schemas/index.js.gz +0 -0
  161. package/min/queries/index.js +1 -1
  162. package/min/queries/index.js.gz +0 -0
  163. package/min/queries/with-schemas/index.js +1 -1
  164. package/min/queries/with-schemas/index.js.gz +0 -0
  165. package/min/relationships/index.js +1 -1
  166. package/min/relationships/index.js.gz +0 -0
  167. package/min/relationships/with-schemas/index.js +1 -1
  168. package/min/relationships/with-schemas/index.js.gz +0 -0
  169. package/min/store/index.js +1 -1
  170. package/min/store/index.js.gz +0 -0
  171. package/min/store/with-schemas/index.js +1 -1
  172. package/min/store/with-schemas/index.js.gz +0 -0
  173. package/min/synchronizers/index.js +1 -1
  174. package/min/synchronizers/index.js.gz +0 -0
  175. package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
  176. package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
  177. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
  178. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
  179. package/min/synchronizers/synchronizer-local/index.js +1 -1
  180. package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
  181. package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
  182. package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
  183. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  184. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  185. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  186. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  187. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  188. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  189. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  190. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  191. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  192. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  193. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  194. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  195. package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
  196. package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
  197. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
  198. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
  199. package/min/synchronizers/with-schemas/index.js +1 -1
  200. package/min/synchronizers/with-schemas/index.js.gz +0 -0
  201. package/min/ui-react/index.js +1 -1
  202. package/min/ui-react/index.js.gz +0 -0
  203. package/min/ui-react/with-schemas/index.js +1 -1
  204. package/min/ui-react/with-schemas/index.js.gz +0 -0
  205. package/min/ui-react-dom/index.js +1 -1
  206. package/min/ui-react-dom/index.js.gz +0 -0
  207. package/min/ui-react-dom/with-schemas/index.js +1 -1
  208. package/min/ui-react-dom/with-schemas/index.js.gz +0 -0
  209. package/min/ui-react-inspector/index.js +1 -1
  210. package/min/ui-react-inspector/index.js.gz +0 -0
  211. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  212. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  213. package/min/with-schemas/index.js +1 -1
  214. package/min/with-schemas/index.js.gz +0 -0
  215. package/omni/index.js +78 -46
  216. package/omni/with-schemas/index.js +78 -46
  217. package/package.json +3 -3
  218. package/persisters/index.js +14 -10
  219. package/persisters/persister-automerge/index.js +12 -8
  220. package/persisters/persister-automerge/with-schemas/index.js +12 -8
  221. package/persisters/persister-browser/index.js +12 -8
  222. package/persisters/persister-browser/with-schemas/index.js +12 -8
  223. package/persisters/persister-cr-sqlite-wasm/index.js +14 -10
  224. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +14 -10
  225. package/persisters/persister-durable-object-sql-storage/index.js +14 -10
  226. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +14 -10
  227. package/persisters/persister-durable-object-storage/index.js +12 -8
  228. package/persisters/persister-durable-object-storage/with-schemas/index.js +12 -8
  229. package/persisters/persister-electric-sql/index.js +14 -10
  230. package/persisters/persister-electric-sql/with-schemas/index.js +14 -10
  231. package/persisters/persister-expo-sqlite/index.js +14 -10
  232. package/persisters/persister-expo-sqlite/with-schemas/index.js +14 -10
  233. package/persisters/persister-file/index.js +12 -8
  234. package/persisters/persister-file/with-schemas/index.js +12 -8
  235. package/persisters/persister-indexed-db/index.js +12 -8
  236. package/persisters/persister-indexed-db/with-schemas/index.js +12 -8
  237. package/persisters/persister-libsql/index.js +14 -10
  238. package/persisters/persister-libsql/with-schemas/index.js +14 -10
  239. package/persisters/persister-partykit-client/index.js +12 -8
  240. package/persisters/persister-partykit-client/with-schemas/index.js +12 -8
  241. package/persisters/persister-partykit-server/index.js +4 -3
  242. package/persisters/persister-partykit-server/with-schemas/index.js +4 -3
  243. package/persisters/persister-pglite/index.js +13 -9
  244. package/persisters/persister-pglite/with-schemas/index.js +13 -9
  245. package/persisters/persister-postgres/index.js +13 -9
  246. package/persisters/persister-postgres/with-schemas/index.js +13 -9
  247. package/persisters/persister-powersync/index.js +14 -10
  248. package/persisters/persister-powersync/with-schemas/index.js +14 -10
  249. package/persisters/persister-react-native-mmkv/index.js +12 -8
  250. package/persisters/persister-react-native-mmkv/with-schemas/index.js +12 -8
  251. package/persisters/persister-react-native-sqlite/index.js +14 -10
  252. package/persisters/persister-react-native-sqlite/with-schemas/index.js +14 -10
  253. package/persisters/persister-remote/index.js +14 -10
  254. package/persisters/persister-remote/with-schemas/index.js +14 -10
  255. package/persisters/persister-sqlite-bun/index.js +14 -10
  256. package/persisters/persister-sqlite-bun/with-schemas/index.js +14 -10
  257. package/persisters/persister-sqlite-wasm/index.js +14 -10
  258. package/persisters/persister-sqlite-wasm/with-schemas/index.js +14 -10
  259. package/persisters/persister-sqlite3/index.js +14 -10
  260. package/persisters/persister-sqlite3/with-schemas/index.js +14 -10
  261. package/persisters/persister-yjs/index.js +18 -12
  262. package/persisters/persister-yjs/with-schemas/index.js +18 -12
  263. package/persisters/with-schemas/index.js +14 -10
  264. package/queries/index.js +17 -12
  265. package/queries/with-schemas/index.js +17 -12
  266. package/readme.md +2 -2
  267. package/relationships/index.js +7 -5
  268. package/relationships/with-schemas/index.js +7 -5
  269. package/releases.md +100 -12
  270. package/store/index.js +48 -18
  271. package/store/with-schemas/index.js +48 -18
  272. package/synchronizers/index.js +13 -9
  273. package/synchronizers/synchronizer-broadcast-channel/index.js +14 -10
  274. package/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +14 -10
  275. package/synchronizers/synchronizer-local/index.js +14 -10
  276. package/synchronizers/synchronizer-local/with-schemas/index.js +14 -10
  277. package/synchronizers/synchronizer-ws-client/index.js +13 -9
  278. package/synchronizers/synchronizer-ws-client/with-schemas/index.js +13 -9
  279. package/synchronizers/synchronizer-ws-server/index.js +13 -9
  280. package/synchronizers/synchronizer-ws-server/with-schemas/index.js +13 -9
  281. package/synchronizers/synchronizer-ws-server-durable-object/index.js +13 -9
  282. package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +13 -9
  283. package/synchronizers/synchronizer-ws-server-simple/index.js +5 -4
  284. package/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +5 -4
  285. package/synchronizers/with-schemas/index.js +13 -9
  286. package/ui-react/index.js +9 -6
  287. package/ui-react/with-schemas/index.js +9 -6
  288. package/ui-react-dom/index.js +14 -7
  289. package/ui-react-dom/with-schemas/index.js +14 -7
  290. package/ui-react-inspector/index.js +53 -23
  291. package/ui-react-inspector/with-schemas/index.js +53 -23
  292. 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="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>
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&#x27;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
- const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
11
- const persister = createOpfsPersister(store, handle);
101
+ store.delTables().setTables({pets: {fido: {species: 'dog'}}});
102
+ const opfsPersister = createOpfsPersister(store, handle);
12
103
 
13
- await persister.save();
104
+ await opfsPersister.save();
14
105
  // Store JSON will be saved to the OPFS file.
15
106
 
16
- await persister.load();
107
+ await opfsPersister.load();
17
108
  // Store JSON will be loaded from the OPFS file.
18
109
 
19
- await persister.destroy();
110
+ await opfsPersister.destroy();
20
111
  ```
21
112
 
22
113
  <p>That&#x27;s it! If you&#x27;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 &#x27;pencil&#x27; 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
- const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
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 &#x27;pets&#x27; 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 isUndefined = (thing) => thing == void 0;
29
- const ifNotUndefined = (value, then, otherwise) =>
30
- isUndefined(value) ? otherwise?.() : then(value);
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
- !isUndefined(obj) &&
91
- ifNotUndefined(
98
+ !isNullish(obj) &&
99
+ ifNotNullish(
92
100
  getPrototypeOf(obj),
93
101
  (objPrototype) =>
94
102
  objPrototype == object.prototype ||
95
- isUndefined(getPrototypeOf(objPrototype)),
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
- isUndefined(obj) ||
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
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
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 (isUndefined(path[index])) {
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 (!objValidate(schema, (_child, id2) => arrayHas([TYPE, DEFAULT], id2))) {
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
- if (getCellOrValueType(schema[DEFAULT]) != type) {
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
- getCellOrValueType(cell) != cellSchema[TYPE]
431
- ? cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
432
- : cell,
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
- getCellOrValueType(value) != valueSchema[TYPE]
458
- ? valueInvalid(valueId, value, valueSchema[DEFAULT])
459
- : value,
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 : null,
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 isUndefined = (thing) => thing == void 0;
29
- const ifNotUndefined = (value, then, otherwise) =>
30
- isUndefined(value) ? otherwise?.() : then(value);
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
- !isUndefined(obj) &&
91
- ifNotUndefined(
98
+ !isNullish(obj) &&
99
+ ifNotNullish(
92
100
  getPrototypeOf(obj),
93
101
  (objPrototype) =>
94
102
  objPrototype == object.prototype ||
95
- isUndefined(getPrototypeOf(objPrototype)),
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
- isUndefined(obj) ||
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
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
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 (isUndefined(path[index])) {
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 (!objValidate(schema, (_child, id2) => arrayHas([TYPE, DEFAULT], id2))) {
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
- if (getCellOrValueType(schema[DEFAULT]) != type) {
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
- getCellOrValueType(cell) != cellSchema[TYPE]
431
- ? cellInvalid(tableId, rowId, cellId, cell, cellSchema[DEFAULT])
432
- : cell,
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
- getCellOrValueType(value) != valueSchema[TYPE]
458
- ? valueInvalid(valueId, value, valueSchema[DEFAULT])
459
- : value,
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 : null,
732
+ count != -addedOrRemoved ? count + addedOrRemoved : void 0,
703
733
  );
704
734
  idsChanged(
705
735
  mapEnsure(mapEnsure(changedCellIds, tableId, mapNew), rowId, mapNew),
@@ -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 isUndefined = (thing) => thing == void 0;
14
- const ifNotUndefined = (value, then, otherwise) =>
15
- isUndefined(value) ? otherwise?.() : then(value);
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
- !isUndefined(obj) &&
50
- ifNotUndefined(
53
+ !isNullish(obj) &&
54
+ ifNotNullish(
51
55
  getPrototypeOf(obj),
52
56
  (objPrototype) =>
53
57
  objPrototype == object.prototype ||
54
- isUndefined(getPrototypeOf(objPrototype)),
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
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
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 (isUndefined(path[index])) {
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
- isUndefined(toClientId) || toClientId == fromClientId
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 isUndefined = (thing) => thing == void 0;
14
- const ifNotUndefined = (value, then, otherwise) =>
15
- isUndefined(value) ? otherwise?.() : then(value);
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
- !isUndefined(obj) &&
50
- ifNotUndefined(
53
+ !isNullish(obj) &&
54
+ ifNotNullish(
51
55
  getPrototypeOf(obj),
52
56
  (objPrototype) =>
53
57
  objPrototype == object.prototype ||
54
- isUndefined(getPrototypeOf(objPrototype)),
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
- isUndefined(value) ? (collDel(map, key), map) : map?.set(key, value);
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 (isUndefined(path[index])) {
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
- isUndefined(toClientId) || toClientId == fromClientId
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
- isUndefined(toClientId) || toClientId == clientId
701
+ isNull(toClientId) || toClientId == clientId
698
702
  ? receive(fromClientId, requestId, message, body)
699
703
  : 0;
700
704
  };