tinybase 9.4.0-beta.1 → 9.4.0-beta.2

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 (225) hide show
  1. package/@types/persisters/index.d.ts +4 -0
  2. package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +9 -2
  3. package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +9 -2
  4. package/@types/persisters/with-schemas/index.d.ts +4 -0
  5. package/@types/queries/index.d.ts +4 -0
  6. package/@types/queries/with-schemas/index.d.ts +4 -0
  7. package/@types/synchronizers/synchronizer-ws-client/index.d.ts +7 -3
  8. package/@types/synchronizers/synchronizer-ws-client/with-schemas/index.d.ts +7 -3
  9. package/@types/synchronizers/synchronizer-ws-server/index.d.ts +9 -3
  10. package/@types/synchronizers/synchronizer-ws-server/with-schemas/index.d.ts +9 -3
  11. package/@types/synchronizers/synchronizer-ws-server-simple/index.d.ts +6 -3
  12. package/@types/synchronizers/synchronizer-ws-server-simple/with-schemas/index.d.ts +6 -3
  13. package/index.js +191 -74
  14. package/min/index.js +1 -1
  15. package/min/index.js.gz +0 -0
  16. package/min/omni/index.js +1 -1
  17. package/min/omni/index.js.gz +0 -0
  18. package/min/omni/with-schemas/index.js +1 -1
  19. package/min/omni/with-schemas/index.js.gz +0 -0
  20. package/min/persisters/index.js +1 -1
  21. package/min/persisters/index.js.gz +0 -0
  22. package/min/persisters/persister-automerge/index.js +1 -1
  23. package/min/persisters/persister-automerge/index.js.gz +0 -0
  24. package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
  25. package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
  26. package/min/persisters/persister-browser/index.js +1 -1
  27. package/min/persisters/persister-browser/index.js.gz +0 -0
  28. package/min/persisters/persister-browser/with-schemas/index.js +1 -1
  29. package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
  30. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  31. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  32. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  33. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  34. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  35. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  36. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  37. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  38. package/min/persisters/persister-durable-object-storage/index.js +1 -1
  39. package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
  40. package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
  41. package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
  42. package/min/persisters/persister-electric-sql/index.js +1 -1
  43. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  44. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  45. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  46. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  47. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  48. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  49. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  50. package/min/persisters/persister-file/index.js +1 -1
  51. package/min/persisters/persister-file/index.js.gz +0 -0
  52. package/min/persisters/persister-file/with-schemas/index.js +1 -1
  53. package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
  54. package/min/persisters/persister-indexed-db/index.js +1 -1
  55. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  56. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  57. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  58. package/min/persisters/persister-libsql/index.js +1 -1
  59. package/min/persisters/persister-libsql/index.js.gz +0 -0
  60. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  61. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  62. package/min/persisters/persister-partykit-client/index.js +1 -1
  63. package/min/persisters/persister-partykit-client/index.js.gz +0 -0
  64. package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
  65. package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
  66. package/min/persisters/persister-pglite/index.js +1 -1
  67. package/min/persisters/persister-pglite/index.js.gz +0 -0
  68. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  69. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  70. package/min/persisters/persister-postgres/index.js +1 -1
  71. package/min/persisters/persister-postgres/index.js.gz +0 -0
  72. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  73. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  74. package/min/persisters/persister-powersync/index.js +1 -1
  75. package/min/persisters/persister-powersync/index.js.gz +0 -0
  76. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  77. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  78. package/min/persisters/persister-react-native-mmkv/index.js +1 -1
  79. package/min/persisters/persister-react-native-mmkv/index.js.gz +0 -0
  80. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js +1 -1
  81. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js.gz +0 -0
  82. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  83. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  84. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  85. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  86. package/min/persisters/persister-remote/index.js +1 -1
  87. package/min/persisters/persister-remote/index.js.gz +0 -0
  88. package/min/persisters/persister-remote/with-schemas/index.js +1 -1
  89. package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
  90. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  91. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  92. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  93. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  94. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  95. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  96. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  97. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  98. package/min/persisters/persister-sqlite3/index.js +1 -1
  99. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  100. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  101. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  102. package/min/persisters/persister-yjs/index.js +1 -1
  103. package/min/persisters/persister-yjs/index.js.gz +0 -0
  104. package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
  105. package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
  106. package/min/persisters/with-schemas/index.js +1 -1
  107. package/min/persisters/with-schemas/index.js.gz +0 -0
  108. package/min/queries/index.js +1 -1
  109. package/min/queries/index.js.gz +0 -0
  110. package/min/queries/with-schemas/index.js +1 -1
  111. package/min/queries/with-schemas/index.js.gz +0 -0
  112. package/min/synchronizers/index.js +1 -1
  113. package/min/synchronizers/index.js.gz +0 -0
  114. package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
  115. package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
  116. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
  117. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
  118. package/min/synchronizers/synchronizer-local/index.js +1 -1
  119. package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
  120. package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
  121. package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
  122. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  123. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  124. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  125. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  126. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  127. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  128. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  129. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  130. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  131. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  132. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  133. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  134. package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
  135. package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
  136. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
  137. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
  138. package/min/synchronizers/with-schemas/index.js +1 -1
  139. package/min/synchronizers/with-schemas/index.js.gz +0 -0
  140. package/min/ui-react-inspector/index.js +1 -1
  141. package/min/ui-react-inspector/index.js.gz +0 -0
  142. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  143. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  144. package/min/ui-solid-inspector/index.js +1 -1
  145. package/min/ui-solid-inspector/index.js.gz +0 -0
  146. package/min/ui-solid-inspector/with-schemas/index.js +1 -1
  147. package/min/ui-solid-inspector/with-schemas/index.js.gz +0 -0
  148. package/min/ui-svelte-inspector/index.js +1 -1
  149. package/min/ui-svelte-inspector/index.js.gz +0 -0
  150. package/min/ui-svelte-inspector/with-schemas/index.js +1 -1
  151. package/min/ui-svelte-inspector/with-schemas/index.js.gz +0 -0
  152. package/min/with-schemas/index.js +1 -1
  153. package/min/with-schemas/index.js.gz +0 -0
  154. package/omni/index.js +606 -179
  155. package/omni/with-schemas/index.js +606 -179
  156. package/package.json +8 -4
  157. package/persisters/index.js +49 -10
  158. package/persisters/persister-automerge/index.js +29 -5
  159. package/persisters/persister-automerge/with-schemas/index.js +29 -5
  160. package/persisters/persister-browser/index.js +29 -5
  161. package/persisters/persister-browser/with-schemas/index.js +29 -5
  162. package/persisters/persister-cr-sqlite-wasm/index.js +29 -5
  163. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +29 -5
  164. package/persisters/persister-durable-object-sql-storage/index.js +44 -8
  165. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +44 -8
  166. package/persisters/persister-durable-object-storage/index.js +31 -5
  167. package/persisters/persister-durable-object-storage/with-schemas/index.js +31 -5
  168. package/persisters/persister-electric-sql/index.js +29 -5
  169. package/persisters/persister-electric-sql/with-schemas/index.js +29 -5
  170. package/persisters/persister-expo-sqlite/index.js +29 -5
  171. package/persisters/persister-expo-sqlite/with-schemas/index.js +29 -5
  172. package/persisters/persister-file/index.js +31 -5
  173. package/persisters/persister-file/with-schemas/index.js +31 -5
  174. package/persisters/persister-indexed-db/index.js +29 -5
  175. package/persisters/persister-indexed-db/with-schemas/index.js +29 -5
  176. package/persisters/persister-libsql/index.js +29 -5
  177. package/persisters/persister-libsql/with-schemas/index.js +29 -5
  178. package/persisters/persister-partykit-client/index.js +31 -5
  179. package/persisters/persister-partykit-client/with-schemas/index.js +31 -5
  180. package/persisters/persister-pglite/index.js +49 -10
  181. package/persisters/persister-pglite/with-schemas/index.js +49 -10
  182. package/persisters/persister-postgres/index.js +49 -10
  183. package/persisters/persister-postgres/with-schemas/index.js +49 -10
  184. package/persisters/persister-powersync/index.js +29 -5
  185. package/persisters/persister-powersync/with-schemas/index.js +29 -5
  186. package/persisters/persister-react-native-mmkv/index.js +31 -5
  187. package/persisters/persister-react-native-mmkv/with-schemas/index.js +31 -5
  188. package/persisters/persister-react-native-sqlite/index.js +29 -5
  189. package/persisters/persister-react-native-sqlite/with-schemas/index.js +29 -5
  190. package/persisters/persister-remote/index.js +31 -5
  191. package/persisters/persister-remote/with-schemas/index.js +31 -5
  192. package/persisters/persister-sqlite-bun/index.js +29 -5
  193. package/persisters/persister-sqlite-bun/with-schemas/index.js +29 -5
  194. package/persisters/persister-sqlite-wasm/index.js +29 -5
  195. package/persisters/persister-sqlite-wasm/with-schemas/index.js +29 -5
  196. package/persisters/persister-sqlite3/index.js +29 -5
  197. package/persisters/persister-sqlite3/with-schemas/index.js +29 -5
  198. package/persisters/persister-yjs/index.js +29 -5
  199. package/persisters/persister-yjs/with-schemas/index.js +29 -5
  200. package/persisters/with-schemas/index.js +49 -10
  201. package/queries/index.js +205 -74
  202. package/queries/with-schemas/index.js +205 -74
  203. package/readme.md +3 -3
  204. package/releases.md +22 -5
  205. package/synchronizers/index.js +31 -5
  206. package/synchronizers/synchronizer-broadcast-channel/index.js +39 -13
  207. package/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +39 -13
  208. package/synchronizers/synchronizer-local/index.js +31 -5
  209. package/synchronizers/synchronizer-local/with-schemas/index.js +31 -5
  210. package/synchronizers/synchronizer-ws-client/index.js +220 -61
  211. package/synchronizers/synchronizer-ws-client/with-schemas/index.js +220 -61
  212. package/synchronizers/synchronizer-ws-server/index.js +253 -73
  213. package/synchronizers/synchronizer-ws-server/with-schemas/index.js +253 -73
  214. package/synchronizers/synchronizer-ws-server-durable-object/index.js +88 -40
  215. package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +88 -40
  216. package/synchronizers/synchronizer-ws-server-simple/index.js +184 -60
  217. package/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +184 -60
  218. package/synchronizers/with-schemas/index.js +31 -5
  219. package/ui-react-inspector/index.js +29 -5
  220. package/ui-react-inspector/with-schemas/index.js +29 -5
  221. package/ui-solid-inspector/index.js +29 -5
  222. package/ui-solid-inspector/with-schemas/index.js +29 -5
  223. package/ui-svelte-inspector/index.js +29 -5
  224. package/ui-svelte-inspector/with-schemas/index.js +29 -5
  225. package/with-schemas/index.js +191 -74
@@ -1340,6 +1340,10 @@ export interface Persister<Persist extends Persists = Persists.StoreOnly> {
1340
1340
  * This simply runs the startAutoLoad and startAutoSave methods in sequence,
1341
1341
  * and returns a Promise that resolves when both have completed.
1342
1342
  *
1343
+ * If the second method fails after either half has partially started, both
1344
+ * halves are stopped again before this method rejects. The original startup
1345
+ * error is preserved if that cleanup also fails.
1346
+ *
1343
1347
  * This method can take `initialContent` to pass to the startAutoLoad method.
1344
1348
  * See its documentation for more details.
1345
1349
  *
@@ -78,8 +78,15 @@ export type DpcFragmented = {
78
78
  * This is useful when you have multiple stores or applications sharing the
79
79
  * same Durable Object SQL storage and want to avoid table name conflicts.
80
80
  *
81
- * The prefix will be sanitized to only include alphanumeric characters and
82
- * underscores. For example, a prefix of 'my-app!' becomes 'my_app_'.
81
+ * Empty prefixes and those containing only lower-case ASCII letters, digits,
82
+ * and underscores are used as-is. Other prefixes are encoded so that distinct
83
+ * prefixes cannot resolve to the same tables, including under SQLite's
84
+ * case-insensitive identifier rules.
85
+ *
86
+ * Tables created for prefixes that are now encoded, including uppercase
87
+ * prefixes and those previously changed by lossy sanitization, are not copied
88
+ * automatically because their names may legitimately belong to another
89
+ * prefix. Migrate those tables explicitly before using the new prefix.
83
90
  * @example
84
91
  * This example shows a configuration using the storagePrefix setting. With a
85
92
  * `storagePrefix` of 'user_data_', it creates `user_data_tinybase_tables` and
@@ -86,8 +86,15 @@ export type DpcFragmented = {
86
86
  * This is useful when you have multiple stores or applications sharing the
87
87
  * same Durable Object SQL storage and want to avoid table name conflicts.
88
88
  *
89
- * The prefix will be sanitized to only include alphanumeric characters and
90
- * underscores. For example, a prefix of 'my-app!' becomes 'my_app_'.
89
+ * Empty prefixes and those containing only lower-case ASCII letters, digits,
90
+ * and underscores are used as-is. Other prefixes are encoded so that distinct
91
+ * prefixes cannot resolve to the same tables, including under SQLite's
92
+ * case-insensitive identifier rules.
93
+ *
94
+ * Tables created for prefixes that are now encoded, including uppercase
95
+ * prefixes and those previously changed by lossy sanitization, are not copied
96
+ * automatically because their names may legitimately belong to another
97
+ * prefix. Migrate those tables explicitly before using the new prefix.
91
98
  * @example
92
99
  * This example shows a configuration using the storagePrefix setting. With a
93
100
  * `storagePrefix` of 'user_data_', it creates `user_data_tinybase_tables` and
@@ -1461,6 +1461,10 @@ export interface Persister<
1461
1461
  * This simply runs the startAutoLoad and startAutoSave methods in sequence,
1462
1462
  * and returns a Promise that resolves when both have completed.
1463
1463
  *
1464
+ * If the second method fails after either half has partially started, both
1465
+ * halves are stopped again before this method rejects. The original startup
1466
+ * error is preserved if that cleanup also fails.
1467
+ *
1464
1468
  * This method can take `initialContent` to pass to the startAutoLoad method.
1465
1469
  * See its documentation for more details.
1466
1470
  *
@@ -1039,6 +1039,10 @@ export type CellIdMapper = (cellId: Id) => Id;
1039
1039
  * is expanded into individual selections. The query is rebuilt if this union
1040
1040
  * changes, and any selected Cell that is not grouped becomes a grouping
1041
1041
  * dimension as usual.
1042
+ *
1043
+ * A cycle between query results is allowed when every SelectAll clause retains
1044
+ * its source Cell Ids. A prefix or CellIdMapper callback in such a cycle is
1045
+ * rejected because it could expand Cell Ids indefinitely.
1042
1046
  * @category Definition
1043
1047
  * @since v9.4.0
1044
1048
  */
@@ -1186,6 +1186,10 @@ export type CellIdMapper<CellId extends Id = Id> = (cellId: CellId) => Id;
1186
1186
  * is expanded into individual selections. The query is rebuilt if this union
1187
1187
  * changes, and any selected Cell that is not grouped becomes a grouping
1188
1188
  * dimension as usual.
1189
+ *
1190
+ * A cycle between query results is allowed when every SelectAll clause retains
1191
+ * its source Cell Ids. A prefix or CellIdMapper callback in such a cycle is
1192
+ * rejected because it could expand Cell Ids indefinitely.
1189
1193
  * @category Definition
1190
1194
  * @since v9.4.0
1191
1195
  */
@@ -177,7 +177,9 @@ export function createWsSynchronizer<WebSocketType extends WebSocketTypes>(
177
177
  * Construct the WebSocket with the `tinybase` subprotocol and provide a
178
178
  * channel Id as the third argument. The channel Id is appended to the
179
179
  * WebSocket URL path to form the server path; it is not taken from the
180
- * MergeableStore Id.
180
+ * MergeableStore Id. It can contain at most 1,024 UTF-8 bytes, and one shared
181
+ * WebSocket can have at most 100 channels. Creating a 101st channel is
182
+ * rejected without closing the existing shared WebSocket.
181
183
  *
182
184
  * Destroying the WsSynchronizer unsubscribes its channel. The shared
183
185
  * WebSocket is closed only when the last WsSynchronizer using it is
@@ -188,8 +190,10 @@ export function createWsSynchronizer<WebSocketType extends WebSocketTypes>(
188
190
  * later. It is not supported by WsServerDurableObject.
189
191
  * @param store The MergeableStore to synchronize.
190
192
  * @param webSocket The shared WebSocket to send synchronization messages
191
- * over, constructed with the `tinybase` subprotocol.
192
- * @param channelId The channel Id to append to the WebSocket URL path.
193
+ * over, constructed with the `tinybase` subprotocol and shared by at most 100
194
+ * channels.
195
+ * @param channelId The channel Id to append to the WebSocket URL path, with
196
+ * at most 1,024 UTF-8 bytes.
193
197
  * @param requestTimeoutSeconds An optional time in seconds that the
194
198
  * Synchronizer will wait for responses to request messages, defaulting to 1.
195
199
  * @param onSend An optional handler for the messages that this Synchronizer
@@ -211,7 +211,9 @@ export function createWsSynchronizer<
211
211
  * Construct the WebSocket with the `tinybase` subprotocol and provide a
212
212
  * channel Id as the third argument. The channel Id is appended to the
213
213
  * WebSocket URL path to form the server path; it is not taken from the
214
- * MergeableStore Id.
214
+ * MergeableStore Id. It can contain at most 1,024 UTF-8 bytes, and one shared
215
+ * WebSocket can have at most 100 channels. Creating a 101st channel is
216
+ * rejected without closing the existing shared WebSocket.
215
217
  *
216
218
  * Destroying the WsSynchronizer unsubscribes its channel. The shared
217
219
  * WebSocket is closed only when the last WsSynchronizer using it is
@@ -222,8 +224,10 @@ export function createWsSynchronizer<
222
224
  * later. It is not supported by WsServerDurableObject.
223
225
  * @param store The MergeableStore to synchronize.
224
226
  * @param webSocket The shared WebSocket to send synchronization messages
225
- * over, constructed with the `tinybase` subprotocol.
226
- * @param channelId The channel Id to append to the WebSocket URL path.
227
+ * over, constructed with the `tinybase` subprotocol and shared by at most 100
228
+ * channels.
229
+ * @param channelId The channel Id to append to the WebSocket URL path, with
230
+ * at most 1,024 UTF-8 bytes.
227
231
  * @param requestTimeoutSeconds An optional time in seconds that the
228
232
  * Synchronizer will wait for responses to request messages, defaulting to 1.
229
233
  * @param onSend An optional handler for the messages that this Synchronizer
@@ -519,7 +519,11 @@ export interface WsServer {
519
519
  * Since v9.3, multiple WsSynchronizer instances can share one WebSocket by
520
520
  * using channel Ids. Each channel Id is appended to the WebSocket URL path and
521
521
  * treated as an ordinary server path. This means a multiplexed channel can
522
- * interoperate with legacy clients connected directly to that full path.
522
+ * interoperate with legacy clients connected directly to that full path. A
523
+ * channel Id can contain at most 1,024 UTF-8 bytes, and each multiplexed
524
+ * WebSocket can have at most 100 subscribed channels. Pending setup and
525
+ * teardown resources are also bounded. Fragment reassembly and traffic
526
+ * buffered while paths start share limits across the physical WebSocket.
523
527
  *
524
528
  * The WsServer does not authenticate or authorize URL paths or channel Ids.
525
529
  * Once a client WebSocket is accepted on a base path, it can subscribe to any
@@ -608,7 +612,7 @@ export interface WsServer {
608
612
  * client connects, it picks up the data the previous two were using.
609
613
  *
610
614
  * ```js
611
- * import {rmSync} from 'fs';
615
+ * import {mkdirSync, rmSync} from 'fs';
612
616
  * import {createMergeableStore} from 'tinybase';
613
617
  * import {createFilePersister} from 'tinybase/persisters/persister-file';
614
618
  * import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';
@@ -616,6 +620,7 @@ export interface WsServer {
616
620
  * import {WebSocket, WebSocketServer} from 'ws';
617
621
  *
618
622
  * // Server
623
+ * mkdirSync('./tmp', {recursive: true});
619
624
  * const server = createWsServer(
620
625
  * new WebSocketServer({port: 8047}),
621
626
  * (pathId) =>
@@ -679,7 +684,7 @@ export interface WsServer {
679
684
  * data once synchronization has started.
680
685
  *
681
686
  * ```js
682
- * import {rmSync} from 'fs';
687
+ * import {mkdirSync, rmSync} from 'fs';
683
688
  * import {createMergeableStore} from 'tinybase';
684
689
  * import {createFilePersister} from 'tinybase/persisters/persister-file';
685
690
  * import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';
@@ -687,6 +692,7 @@ export interface WsServer {
687
692
  * import {WebSocket, WebSocketServer} from 'ws';
688
693
  *
689
694
  * // Server
695
+ * mkdirSync('./tmp', {recursive: true});
690
696
  * const server = createWsServer(
691
697
  * new WebSocketServer({port: 8047}),
692
698
  * (pathId) => [
@@ -548,7 +548,11 @@ export interface WsServer {
548
548
  * Since v9.3, multiple WsSynchronizer instances can share one WebSocket by
549
549
  * using channel Ids. Each channel Id is appended to the WebSocket URL path and
550
550
  * treated as an ordinary server path. This means a multiplexed channel can
551
- * interoperate with legacy clients connected directly to that full path.
551
+ * interoperate with legacy clients connected directly to that full path. A
552
+ * channel Id can contain at most 1,024 UTF-8 bytes, and each multiplexed
553
+ * WebSocket can have at most 100 subscribed channels. Pending setup and
554
+ * teardown resources are also bounded. Fragment reassembly and traffic
555
+ * buffered while paths start share limits across the physical WebSocket.
552
556
  *
553
557
  * The WsServer does not authenticate or authorize URL paths or channel Ids.
554
558
  * Once a client WebSocket is accepted on a base path, it can subscribe to any
@@ -637,7 +641,7 @@ export interface WsServer {
637
641
  * client connects, it picks up the data the previous two were using.
638
642
  *
639
643
  * ```js
640
- * import {rmSync} from 'fs';
644
+ * import {mkdirSync, rmSync} from 'fs';
641
645
  * import {createMergeableStore} from 'tinybase';
642
646
  * import {createFilePersister} from 'tinybase/persisters/persister-file';
643
647
  * import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';
@@ -645,6 +649,7 @@ export interface WsServer {
645
649
  * import {WebSocket, WebSocketServer} from 'ws';
646
650
  *
647
651
  * // Server
652
+ * mkdirSync('./tmp', {recursive: true});
648
653
  * const server = createWsServer(
649
654
  * new WebSocketServer({port: 8047}),
650
655
  * (pathId) =>
@@ -708,7 +713,7 @@ export interface WsServer {
708
713
  * data once synchronization has started.
709
714
  *
710
715
  * ```js
711
- * import {rmSync} from 'fs';
716
+ * import {mkdirSync, rmSync} from 'fs';
712
717
  * import {createMergeableStore} from 'tinybase';
713
718
  * import {createFilePersister} from 'tinybase/persisters/persister-file';
714
719
  * import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';
@@ -716,6 +721,7 @@ export interface WsServer {
716
721
  * import {WebSocket, WebSocketServer} from 'ws';
717
722
  *
718
723
  * // Server
724
+ * mkdirSync('./tmp', {recursive: true});
719
725
  * const server = createWsServer(
720
726
  * new WebSocketServer({port: 8047}),
721
727
  * (pathId) => [
@@ -96,9 +96,12 @@ export interface WsServerSimple {
96
96
  * Since v9.3, it also supports multiple channel-based WsSynchronizer instances
97
97
  * sharing one WebSocket. Once a client WebSocket is accepted on a base path,
98
98
  * it can subscribe to any valid channel beneath that path: WsServerSimple does
99
- * not authenticate or authorize channel Ids. For untrusted clients, use a
100
- * separate authenticated WebSocket for each authorized path unless access to
101
- * all descendants is acceptable.
99
+ * not authenticate or authorize channel Ids. A channel Id can contain at most
100
+ * 1,024 UTF-8 bytes, and each multiplexed WebSocket can have at most 100
101
+ * subscribed channels. Pending channel cleanup is also bounded, and fragment
102
+ * reassembly limits are shared across the physical WebSocket. For untrusted
103
+ * clients, use a separate authenticated WebSocket for each authorized path
104
+ * unless access to all descendants is acceptable.
102
105
  * @param webSocketServer A WebSocketServer object from your server environment.
103
106
  * @returns A reference to the new WsServerSimple object.
104
107
  * @example
@@ -96,9 +96,12 @@ export interface WsServerSimple {
96
96
  * Since v9.3, it also supports multiple channel-based WsSynchronizer instances
97
97
  * sharing one WebSocket. Once a client WebSocket is accepted on a base path,
98
98
  * it can subscribe to any valid channel beneath that path: WsServerSimple does
99
- * not authenticate or authorize channel Ids. For untrusted clients, use a
100
- * separate authenticated WebSocket for each authorized path unless access to
101
- * all descendants is acceptable.
99
+ * not authenticate or authorize channel Ids. A channel Id can contain at most
100
+ * 1,024 UTF-8 bytes, and each multiplexed WebSocket can have at most 100
101
+ * subscribed channels. Pending channel cleanup is also bounded, and fragment
102
+ * reassembly limits are shared across the physical WebSocket. For untrusted
103
+ * clients, use a separate authenticated WebSocket for each authorized path
104
+ * unless access to all descendants is acceptable.
102
105
  * @param webSocketServer A WebSocketServer object from your server environment.
103
106
  * @returns A reference to the new WsServerSimple object.
104
107
  * @example
package/index.js CHANGED
@@ -261,6 +261,7 @@ const collForEach = (coll, cb) => coll?.forEach(cb);
261
261
  const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
262
262
 
263
263
  const ERROR_HLC = 13;
264
+ const ERROR_QUERY_SELECT_ALL_CYCLE = 16;
264
265
  const errorNew = (code, details) =>
265
266
  new Error(
266
267
  TINYBASE +
@@ -4477,7 +4478,50 @@ const createQueries = getCreateFunction((store) => {
4477
4478
  const preStoreListenerIds = mapNew();
4478
4479
  const sourceStoreListenerIds = mapNew();
4479
4480
  const selectAllListenerIds = mapNew();
4481
+ const selectAllRetryListenerIds = mapNew();
4482
+ const selectAllQuerySources = mapNew();
4480
4483
  const referencedQueryIds = mapNew();
4484
+ const assertNoMappedSelectAllCycle = (queryId, nextQuerySources) => {
4485
+ const getQuerySources = (sourceQueryId) =>
4486
+ sourceQueryId === queryId
4487
+ ? nextQuerySources
4488
+ : mapGet(selectAllQuerySources, sourceQueryId);
4489
+ const reachesQuery = (
4490
+ sourceQueryId,
4491
+ targetQueryId,
4492
+ visitedQueryIds = setNew(),
4493
+ ) => {
4494
+ if (sourceQueryId === targetQueryId) {
4495
+ return true;
4496
+ }
4497
+ if (collHas(visitedQueryIds, sourceQueryId)) {
4498
+ return false;
4499
+ }
4500
+ setAdd(visitedQueryIds, sourceQueryId);
4501
+ return !collEvery(
4502
+ getQuerySources(sourceQueryId),
4503
+ (_changesCellId, nextQueryId) =>
4504
+ !reachesQuery(nextQueryId, targetQueryId, visitedQueryIds),
4505
+ );
4506
+ };
4507
+ const hasMappedCycle = (sourceQueryId, querySources) =>
4508
+ !collEvery(
4509
+ querySources,
4510
+ (changesCellId, nextQueryId) =>
4511
+ !changesCellId || !reachesQuery(nextQueryId, sourceQueryId),
4512
+ );
4513
+ if (
4514
+ hasMappedCycle(queryId, nextQuerySources) ||
4515
+ !collEvery(
4516
+ selectAllQuerySources,
4517
+ (querySources, sourceQueryId) =>
4518
+ sourceQueryId === queryId ||
4519
+ !hasMappedCycle(sourceQueryId, querySources),
4520
+ )
4521
+ ) {
4522
+ errorThrow(ERROR_QUERY_SELECT_ALL_CYCLE);
4523
+ }
4524
+ };
4481
4525
  const {
4482
4526
  _: [, addListener, callListeners],
4483
4527
  delListener: delListenerImpl,
@@ -4551,6 +4595,30 @@ const createQueries = getCreateFunction((store) => {
4551
4595
  resetStoreListeners(mapGet(sourceStoreListenerIds, queryId));
4552
4596
  mapSet(sourceStoreListenerIds, queryId);
4553
4597
  };
4598
+ const clearSelectAllRetryListeners = (queryId) =>
4599
+ ifNotUndefined(
4600
+ mapGet(selectAllRetryListenerIds, queryId),
4601
+ (listenerIds) => {
4602
+ mapForEach(listenerIds, (sourceStore, listenerIdsByTableId) =>
4603
+ mapForEach(listenerIdsByTableId, (_sourceTableId, listenerId) =>
4604
+ sourceStore.delListener(listenerId),
4605
+ ),
4606
+ );
4607
+ mapSet(selectAllRetryListenerIds, queryId);
4608
+ },
4609
+ );
4610
+ const addSelectAllRetryListener = (queryId, sourceStore, sourceTableId) => {
4611
+ const listenerIdsByTableId = mapEnsure(
4612
+ mapEnsure(selectAllRetryListenerIds, queryId, mapNew),
4613
+ sourceStore,
4614
+ mapNew,
4615
+ );
4616
+ mapEnsure(listenerIdsByTableId, sourceTableId, () =>
4617
+ sourceStore.addTableListener(sourceTableId, () =>
4618
+ setQueryDefinitionImpl(queryId),
4619
+ ),
4620
+ );
4621
+ };
4554
4622
  const syncSelectAllListeners = (queryId, nextSources = mapNew()) => {
4555
4623
  const listenerIds = mapEnsure(selectAllListenerIds, queryId, mapNew);
4556
4624
  mapForEach(listenerIds, (sourceStore, listenerIdsByTableId) => {
@@ -4569,7 +4637,13 @@ const createQueries = getCreateFunction((store) => {
4569
4637
  collForEach(tableIds, (sourceTableId) =>
4570
4638
  mapEnsure(listenerIdsByTableId, sourceTableId, () =>
4571
4639
  sourceStore.addTableCellIdsListener(sourceTableId, () =>
4572
- setQueryDefinitionImpl(queryId),
4640
+ tryCatchSync(
4641
+ () => setQueryDefinitionImpl(queryId),
4642
+ (error) => {
4643
+ addSelectAllRetryListener(queryId, sourceStore, sourceTableId);
4644
+ throw error;
4645
+ },
4646
+ ),
4573
4647
  ),
4574
4648
  ),
4575
4649
  );
@@ -4728,6 +4802,10 @@ const createQueries = getCreateFunction((store) => {
4728
4802
  isFunction(prefixOrMapper)
4729
4803
  ? prefixOrMapper
4730
4804
  : (cellId) => (prefixOrMapper ?? EMPTY_STRING) + cellId,
4805
+ isFunction(prefixOrMapper) ||
4806
+ (!isUndefined(prefixOrMapper) && prefixOrMapper !== EMPTY_STRING)
4807
+ ? 1
4808
+ : 0,
4731
4809
  ]);
4732
4810
  };
4733
4811
  const join = (arg1, arg2, arg3, arg4) => {
@@ -4801,28 +4879,49 @@ const createQueries = getCreateFunction((store) => {
4801
4879
  getSelectedOrGroupedCell(arg1) === arg2,
4802
4880
  );
4803
4881
  build({select, selectAll, join, where, group, having, param});
4804
- resultStore2.delTable(queryId);
4805
- if (isEmpty(selectionEntries)) {
4806
- commit?.();
4807
- return queries;
4808
- }
4809
4882
  const joins = mapNew(joinEntries);
4810
4883
  mapForEach(joins, (joinAlias, [, fromJoinAlias]) =>
4811
4884
  ifNotUndefined(mapGet(joins, fromJoinAlias), ({3: toJoinAliases2}) =>
4812
4885
  isUndefined(joinAlias) ? 0 : arrayPush(toJoinAliases2, joinAlias),
4813
4886
  ),
4814
4887
  );
4815
- const groups = mapNew(groupEntries);
4816
- const hasGroupsOrHavings = !collIsEmpty(groups) || !isEmpty(havings);
4817
- const selectEntries = [];
4818
- const selectAllSources = stagedDefinition[3];
4888
+ const isRootSelectAll = (joinedTableId) => isUndefined(joinedTableId);
4819
4889
  const getSelectAllJoin = (joinedTableId) =>
4820
4890
  mapGet(
4821
4891
  joins,
4822
- isUndefined(joinedTableId) || joinedTableId === tableId
4823
- ? void 0
4824
- : joinedTableId,
4892
+ isRootSelectAll(joinedTableId) ? void 0 : joinedTableId,
4825
4893
  );
4894
+ const nextSelectAllQuerySources = stagedDefinition[5];
4895
+ arrayForEach(
4896
+ selectionEntries,
4897
+ ([selectAll2, selectedOrJoinedTableId, , changesCellId = 0]) =>
4898
+ selectAll2
4899
+ ? ifNotUndefined(
4900
+ getSelectAllJoin(selectedOrJoinedTableId),
4901
+ ([sourceQueryId, , , , , sourceStore]) =>
4902
+ sourceStore === store
4903
+ ? 0
4904
+ : mapSet(
4905
+ nextSelectAllQuerySources,
4906
+ sourceQueryId,
4907
+ mapGet(nextSelectAllQuerySources, sourceQueryId) ||
4908
+ changesCellId
4909
+ ? 1
4910
+ : 0,
4911
+ ),
4912
+ )
4913
+ : 0,
4914
+ );
4915
+ assertNoMappedSelectAllCycle(queryId, nextSelectAllQuerySources);
4916
+ resultStore2.delTable(queryId);
4917
+ if (isEmpty(selectionEntries)) {
4918
+ commit?.();
4919
+ return queries;
4920
+ }
4921
+ const groups = mapNew(groupEntries);
4922
+ const hasGroupsOrHavings = !collIsEmpty(groups) || !isEmpty(havings);
4923
+ const selectEntries = [];
4924
+ const selectAllSources = stagedDefinition[3];
4826
4925
  arrayForEach(
4827
4926
  selectionEntries,
4828
4927
  ([selectAll2, selectedOrJoinedTableId, clauseOrMapper]) => {
@@ -4840,11 +4939,7 @@ const createQueries = getCreateFunction((store) => {
4840
4939
  (cellId) =>
4841
4940
  arrayPush(selectEntries, [
4842
4941
  clauseOrMapper(cellId),
4843
- (getTableCell) =>
4844
- isUndefined(selectedOrJoinedTableId) ||
4845
- selectedOrJoinedTableId === tableId
4846
- ? getTableCell(cellId)
4847
- : getTableCell(selectedOrJoinedTableId, cellId),
4942
+ getUndefined,
4848
4943
  ]),
4849
4944
  );
4850
4945
  },
@@ -5041,67 +5136,74 @@ const createQueries = getCreateFunction((store) => {
5041
5136
  joinedCellId,
5042
5137
  );
5043
5138
  };
5139
+ const writeSelectAllRow = () => {
5140
+ const resultRow = objNew();
5141
+ arrayForEach(
5142
+ selectionEntries,
5143
+ ([selectAll2, selectedOrJoinedTableId, clauseOrMapper]) => {
5144
+ if (selectAll2) {
5145
+ ifNotUndefined(
5146
+ getSelectAllJoin(selectedOrJoinedTableId),
5147
+ ([realTableId, , , , remoteIdPairs, sourceStore]) => {
5148
+ const sourceRowId = isRootSelectAll(
5149
+ selectedOrJoinedTableId,
5150
+ )
5151
+ ? rootRowId
5152
+ : mapGet(remoteIdPairs, rootRowId)?.[0];
5153
+ ifNotUndefined(sourceRowId, (sourceRowId2) =>
5154
+ arrayForEach(
5155
+ arraySort(
5156
+ sourceStore.getCellIds(realTableId, sourceRowId2),
5157
+ ),
5158
+ (cellId) =>
5159
+ objSet(
5160
+ resultRow,
5161
+ clauseOrMapper(cellId),
5162
+ sourceStore.getCell(
5163
+ realTableId,
5164
+ sourceRowId2,
5165
+ cellId,
5166
+ ),
5167
+ ),
5168
+ ),
5169
+ );
5170
+ },
5171
+ );
5172
+ } else {
5173
+ const selectedCell = clauseOrMapper(getJoinCell, rootRowId);
5174
+ const selectedCellId = selectedOrJoinedTableId;
5175
+ if (isUndefined(selectedCell)) {
5176
+ objDel(resultRow, selectedCellId);
5177
+ } else {
5178
+ objSet(resultRow, selectedCellId, selectedCell);
5179
+ }
5180
+ }
5181
+ },
5182
+ );
5183
+ if (objIsEmpty(resultRow)) {
5184
+ selectJoinWhereStore.delRow(queryId, rootRowId);
5185
+ } else {
5186
+ selectJoinWhereStore.setRow(queryId, rootRowId, resultRow);
5187
+ }
5188
+ };
5044
5189
  selectJoinWhereStore.transaction(() => {
5045
5190
  if (!arrayEvery(wheres, (where2) => where2(getJoinCell))) {
5046
5191
  selectJoinWhereStore.delRow(queryId, rootRowId);
5047
5192
  } else if (hasGroupsOrHavings) {
5048
- mapForEach(selects, (asCellId, tableCellGetter) =>
5049
- selectJoinWhereStore._[5](
5050
- queryId,
5051
- rootRowId,
5052
- asCellId,
5053
- tableCellGetter(getJoinCell, rootRowId),
5054
- ),
5055
- );
5056
- } else if (hasSelectAll) {
5057
- const resultRow = objNew();
5058
- arrayForEach(
5059
- selectionEntries,
5060
- ([selectAll2, selectedOrJoinedTableId, clauseOrMapper]) => {
5061
- if (selectAll2) {
5062
- ifNotUndefined(
5063
- getSelectAllJoin(selectedOrJoinedTableId),
5064
- ([realTableId, , , , remoteIdPairs, sourceStore]) => {
5065
- const sourceRowId =
5066
- isUndefined(selectedOrJoinedTableId) ||
5067
- selectedOrJoinedTableId === tableId
5068
- ? rootRowId
5069
- : mapGet(remoteIdPairs, rootRowId)?.[0];
5070
- ifNotUndefined(sourceRowId, (sourceRowId2) =>
5071
- arrayForEach(
5072
- arraySort(
5073
- sourceStore.getCellIds(realTableId, sourceRowId2),
5074
- ),
5075
- (cellId) =>
5076
- objSet(
5077
- resultRow,
5078
- clauseOrMapper(cellId),
5079
- sourceStore.getCell(
5080
- realTableId,
5081
- sourceRowId2,
5082
- cellId,
5083
- ),
5084
- ),
5085
- ),
5086
- );
5087
- },
5088
- );
5089
- } else {
5090
- const selectedCell = clauseOrMapper(getJoinCell, rootRowId);
5091
- const selectedCellId = selectedOrJoinedTableId;
5092
- if (isUndefined(selectedCell)) {
5093
- objDel(resultRow, selectedCellId);
5094
- } else {
5095
- objSet(resultRow, selectedCellId, selectedCell);
5096
- }
5097
- }
5098
- },
5099
- );
5100
- if (objIsEmpty(resultRow)) {
5101
- selectJoinWhereStore.delRow(queryId, rootRowId);
5193
+ if (hasSelectAll) {
5194
+ writeSelectAllRow();
5102
5195
  } else {
5103
- selectJoinWhereStore.setRow(queryId, rootRowId, resultRow);
5196
+ mapForEach(selects, (asCellId, tableCellGetter) =>
5197
+ selectJoinWhereStore._[5](
5198
+ queryId,
5199
+ rootRowId,
5200
+ asCellId,
5201
+ tableCellGetter(getJoinCell, rootRowId),
5202
+ ),
5203
+ );
5104
5204
  }
5205
+ } else if (hasSelectAll) {
5206
+ writeSelectAllRow();
5105
5207
  } else {
5106
5208
  mapForEach(selects, (asCellId, tableCellGetter) =>
5107
5209
  selectJoinWhereStore._[5](
@@ -5231,7 +5333,14 @@ const createQueries = getCreateFunction((store) => {
5231
5333
  }),
5232
5334
  );
5233
5335
  const setQueryDefinitionImpl = (queryId, definition) => {
5234
- const stagedDefinition = [mapNew(), mapNew(), void 0, mapNew(), setNew()];
5336
+ const stagedDefinition = [
5337
+ mapNew(),
5338
+ mapNew(),
5339
+ void 0,
5340
+ mapNew(),
5341
+ setNew(),
5342
+ mapNew(),
5343
+ ];
5235
5344
  let committed = false;
5236
5345
  tryCatchSync(
5237
5346
  () =>
@@ -5253,11 +5362,17 @@ const createQueries = getCreateFunction((store) => {
5253
5362
  );
5254
5363
  mapSet(preStores, queryId, stagedDefinition[2]);
5255
5364
  syncSelectAllListeners(queryId, stagedDefinition[3]);
5365
+ clearSelectAllRetryListeners(queryId);
5256
5366
  mapSet(
5257
5367
  referencedQueryIds,
5258
5368
  queryId,
5259
5369
  collIsEmpty(stagedDefinition[4]) ? void 0 : stagedDefinition[4],
5260
5370
  );
5371
+ mapSet(
5372
+ selectAllQuerySources,
5373
+ queryId,
5374
+ collIsEmpty(stagedDefinition[5]) ? void 0 : stagedDefinition[5],
5375
+ );
5261
5376
  committed = true;
5262
5377
  }),
5263
5378
  (error) => {
@@ -5308,7 +5423,9 @@ const createQueries = getCreateFunction((store) => {
5308
5423
  resetPreStores(queryId);
5309
5424
  resetSourceStores(queryId);
5310
5425
  syncSelectAllListeners(queryId);
5426
+ clearSelectAllRetryListeners(queryId);
5311
5427
  mapSet(referencedQueryIds, queryId);
5428
+ mapSet(selectAllQuerySources, queryId);
5312
5429
  delDefinition(queryId);
5313
5430
  cleanStores();
5314
5431
  return queries;