@uwdata/mosaic-core 0.16.2 → 0.18.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 (206) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +0 -1
  3. package/dist/src/Coordinator.d.ts +147 -0
  4. package/dist/src/Coordinator.d.ts.map +1 -0
  5. package/dist/src/Coordinator.js +269 -0
  6. package/dist/src/Coordinator.js.map +1 -0
  7. package/dist/{types → src}/MosaicClient.d.ts +37 -42
  8. package/dist/src/MosaicClient.d.ts.map +1 -0
  9. package/dist/src/MosaicClient.js +213 -0
  10. package/dist/src/MosaicClient.js.map +1 -0
  11. package/dist/src/Param.d.ts +56 -0
  12. package/dist/src/Param.d.ts.map +1 -0
  13. package/dist/src/Param.js +89 -0
  14. package/dist/src/Param.js.map +1 -0
  15. package/dist/src/QueryConsolidator.d.ts +11 -0
  16. package/dist/src/QueryConsolidator.d.ts.map +1 -0
  17. package/dist/src/QueryConsolidator.js +249 -0
  18. package/dist/src/QueryConsolidator.js.map +1 -0
  19. package/dist/src/QueryManager.d.ts +77 -0
  20. package/dist/src/QueryManager.d.ts.map +1 -0
  21. package/dist/src/QueryManager.js +174 -0
  22. package/dist/src/QueryManager.js.map +1 -0
  23. package/dist/src/Selection.d.ts +222 -0
  24. package/dist/src/Selection.d.ts.map +1 -0
  25. package/dist/src/Selection.js +319 -0
  26. package/dist/src/Selection.js.map +1 -0
  27. package/dist/src/SelectionClause.d.ts +192 -0
  28. package/dist/src/SelectionClause.d.ts.map +1 -0
  29. package/dist/src/SelectionClause.js +126 -0
  30. package/dist/src/SelectionClause.js.map +1 -0
  31. package/dist/{types → src}/connectors/Connector.d.ts +6 -5
  32. package/dist/src/connectors/Connector.d.ts.map +1 -0
  33. package/dist/src/connectors/Connector.js +2 -0
  34. package/dist/src/connectors/Connector.js.map +1 -0
  35. package/dist/src/connectors/rest.d.ts +24 -0
  36. package/dist/src/connectors/rest.d.ts.map +1 -0
  37. package/dist/src/connectors/rest.js +37 -0
  38. package/dist/src/connectors/rest.js.map +1 -0
  39. package/dist/src/connectors/socket.d.ts +40 -0
  40. package/dist/src/connectors/socket.d.ts.map +1 -0
  41. package/dist/src/connectors/socket.js +115 -0
  42. package/dist/src/connectors/socket.js.map +1 -0
  43. package/dist/src/connectors/wasm.d.ts +53 -0
  44. package/dist/src/connectors/wasm.d.ts.map +1 -0
  45. package/dist/src/connectors/wasm.js +113 -0
  46. package/dist/src/connectors/wasm.js.map +1 -0
  47. package/dist/src/index.d.ts +28 -0
  48. package/dist/src/index.d.ts.map +1 -0
  49. package/dist/src/index.js +25 -0
  50. package/dist/src/index.js.map +1 -0
  51. package/dist/src/make-client.d.ts +35 -0
  52. package/dist/src/make-client.d.ts.map +1 -0
  53. package/dist/src/make-client.js +52 -0
  54. package/dist/src/make-client.js.map +1 -0
  55. package/dist/{types → src}/preagg/PreAggregator.d.ts +64 -94
  56. package/dist/src/preagg/PreAggregator.d.ts.map +1 -0
  57. package/dist/src/preagg/PreAggregator.js +382 -0
  58. package/dist/src/preagg/PreAggregator.js.map +1 -0
  59. package/dist/{types → src}/preagg/preagg-columns.d.ts +10 -8
  60. package/dist/src/preagg/preagg-columns.d.ts.map +1 -0
  61. package/dist/src/preagg/preagg-columns.js +95 -0
  62. package/dist/src/preagg/preagg-columns.js.map +1 -0
  63. package/dist/src/preagg/sufficient-statistics.d.ts +14 -0
  64. package/dist/src/preagg/sufficient-statistics.d.ts.map +1 -0
  65. package/dist/src/preagg/sufficient-statistics.js +446 -0
  66. package/dist/src/preagg/sufficient-statistics.js.map +1 -0
  67. package/dist/{types → src}/types.d.ts +23 -9
  68. package/dist/src/types.d.ts.map +1 -0
  69. package/dist/src/types.js +2 -0
  70. package/dist/src/types.js.map +1 -0
  71. package/dist/{types → src}/util/AsyncDispatch.d.ts +53 -32
  72. package/dist/src/util/AsyncDispatch.d.ts.map +1 -0
  73. package/dist/src/util/AsyncDispatch.js +188 -0
  74. package/dist/src/util/AsyncDispatch.js.map +1 -0
  75. package/dist/src/util/cache.d.ts +19 -0
  76. package/dist/src/util/cache.d.ts.map +1 -0
  77. package/dist/src/util/cache.js +66 -0
  78. package/dist/src/util/cache.js.map +1 -0
  79. package/dist/src/util/decode-ipc.d.ts +12 -0
  80. package/dist/src/util/decode-ipc.d.ts.map +1 -0
  81. package/{src → dist/src}/util/decode-ipc.js +5 -6
  82. package/dist/src/util/decode-ipc.js.map +1 -0
  83. package/dist/src/util/distinct.d.ts +3 -0
  84. package/dist/src/util/distinct.d.ts.map +1 -0
  85. package/dist/src/util/distinct.js +16 -0
  86. package/dist/src/util/distinct.js.map +1 -0
  87. package/dist/src/util/field-info.d.ts +26 -0
  88. package/dist/src/util/field-info.d.ts.map +1 -0
  89. package/dist/src/util/field-info.js +91 -0
  90. package/dist/src/util/field-info.js.map +1 -0
  91. package/dist/src/util/hash.d.ts +2 -0
  92. package/dist/src/util/hash.d.ts.map +1 -0
  93. package/dist/src/util/hash.js +26 -0
  94. package/dist/src/util/hash.js.map +1 -0
  95. package/dist/src/util/is-activatable.d.ts +8 -0
  96. package/dist/src/util/is-activatable.d.ts.map +1 -0
  97. package/dist/src/util/is-activatable.js +10 -0
  98. package/dist/src/util/is-activatable.js.map +1 -0
  99. package/dist/src/util/is-arrow-table.d.ts +9 -0
  100. package/dist/src/util/is-arrow-table.d.ts.map +1 -0
  101. package/dist/src/util/is-arrow-table.js +11 -0
  102. package/dist/src/util/is-arrow-table.js.map +1 -0
  103. package/dist/src/util/js-type.d.ts +9 -0
  104. package/dist/src/util/js-type.d.ts.map +1 -0
  105. package/dist/src/util/js-type.js +59 -0
  106. package/dist/src/util/js-type.js.map +1 -0
  107. package/dist/{types → src}/util/priority-queue.d.ts +12 -14
  108. package/dist/src/util/priority-queue.d.ts.map +1 -0
  109. package/dist/src/util/priority-queue.js +81 -0
  110. package/dist/src/util/priority-queue.js.map +1 -0
  111. package/dist/src/util/query-result.d.ts +47 -0
  112. package/dist/src/util/query-result.d.ts.map +1 -0
  113. package/dist/src/util/query-result.js +83 -0
  114. package/dist/src/util/query-result.js.map +1 -0
  115. package/dist/src/util/synchronizer.d.ts +36 -0
  116. package/dist/src/util/synchronizer.d.ts.map +1 -0
  117. package/dist/src/util/synchronizer.js +52 -0
  118. package/dist/src/util/synchronizer.js.map +1 -0
  119. package/dist/src/util/throttle.d.ts +12 -0
  120. package/dist/src/util/throttle.d.ts.map +1 -0
  121. package/dist/src/util/throttle.js +51 -0
  122. package/dist/src/util/throttle.js.map +1 -0
  123. package/dist/src/util/to-data-columns.d.ts +22 -0
  124. package/dist/src/util/to-data-columns.d.ts.map +1 -0
  125. package/dist/src/util/to-data-columns.js +51 -0
  126. package/dist/src/util/to-data-columns.js.map +1 -0
  127. package/dist/src/util/void-logger.d.ts +13 -0
  128. package/dist/src/util/void-logger.d.ts.map +1 -0
  129. package/dist/src/util/void-logger.js +13 -0
  130. package/dist/src/util/void-logger.js.map +1 -0
  131. package/package.json +15 -13
  132. package/src/Coordinator.ts +367 -0
  133. package/src/{MosaicClient.js → MosaicClient.ts} +49 -43
  134. package/src/{Param.js → Param.ts} +29 -28
  135. package/src/{QueryConsolidator.js → QueryConsolidator.ts} +81 -58
  136. package/src/{QueryManager.js → QueryManager.ts} +61 -54
  137. package/src/Selection.ts +388 -0
  138. package/src/SelectionClause.ts +275 -0
  139. package/src/connectors/Connector.ts +6 -6
  140. package/src/connectors/rest.ts +56 -0
  141. package/src/connectors/{socket.js → socket.ts} +53 -42
  142. package/src/connectors/{wasm.js → wasm.ts} +46 -62
  143. package/src/{index.js → index.ts} +13 -1
  144. package/src/make-client.ts +93 -0
  145. package/src/preagg/{PreAggregator.js → PreAggregator.ts} +164 -145
  146. package/src/preagg/{preagg-columns.js → preagg-columns.ts} +27 -24
  147. package/src/preagg/{sufficient-statistics.js → sufficient-statistics.ts} +161 -110
  148. package/src/types.ts +24 -9
  149. package/src/util/{AsyncDispatch.js → AsyncDispatch.ts} +62 -43
  150. package/src/util/{cache.js → cache.ts} +25 -15
  151. package/src/util/decode-ipc.ts +15 -0
  152. package/src/util/{distinct.js → distinct.ts} +3 -3
  153. package/src/util/{field-info.js → field-info.ts} +31 -32
  154. package/src/util/{hash.js → hash.ts} +4 -4
  155. package/src/util/is-activatable.ts +11 -0
  156. package/src/util/is-arrow-table.ts +12 -0
  157. package/src/util/{js-type.js → js-type.ts} +7 -5
  158. package/src/util/{priority-queue.js → priority-queue.ts} +32 -20
  159. package/src/util/{query-result.js → query-result.ts} +24 -17
  160. package/src/util/synchronizer.ts +56 -0
  161. package/src/util/throttle.ts +59 -0
  162. package/src/util/to-data-columns.ts +65 -0
  163. package/src/util/void-logger.ts +23 -0
  164. package/dist/types/Coordinator.d.ts +0 -164
  165. package/dist/types/Param.d.ts +0 -47
  166. package/dist/types/QueryConsolidator.d.ts +0 -9
  167. package/dist/types/QueryManager.d.ts +0 -91
  168. package/dist/types/Selection.d.ts +0 -235
  169. package/dist/types/SelectionClause.d.ts +0 -105
  170. package/dist/types/connectors/rest.d.ts +0 -13
  171. package/dist/types/connectors/socket.d.ts +0 -100
  172. package/dist/types/connectors/wasm.d.ts +0 -135
  173. package/dist/types/index-types.d.ts +0 -4
  174. package/dist/types/index.d.ts +0 -19
  175. package/dist/types/make-client.d.ts +0 -78
  176. package/dist/types/preagg/sufficient-statistics.d.ts +0 -13
  177. package/dist/types/util/cache.d.ts +0 -17
  178. package/dist/types/util/decode-ipc.d.ts +0 -12
  179. package/dist/types/util/distinct.d.ts +0 -2
  180. package/dist/types/util/field-info.d.ts +0 -23
  181. package/dist/types/util/hash.d.ts +0 -1
  182. package/dist/types/util/is-activatable.d.ts +0 -6
  183. package/dist/types/util/is-arrow-table.d.ts +0 -8
  184. package/dist/types/util/js-type.d.ts +0 -7
  185. package/dist/types/util/query-result.d.ts +0 -44
  186. package/dist/types/util/selection-types.d.ts +0 -114
  187. package/dist/types/util/synchronizer.d.ts +0 -29
  188. package/dist/types/util/throttle.d.ts +0 -13
  189. package/dist/types/util/to-data-columns.d.ts +0 -29
  190. package/dist/types/util/void-logger.d.ts +0 -10
  191. package/jsconfig.json +0 -11
  192. package/src/Coordinator.js +0 -337
  193. package/src/Selection.js +0 -380
  194. package/src/SelectionClause.js +0 -159
  195. package/src/connectors/rest.js +0 -38
  196. package/src/index-types.ts +0 -4
  197. package/src/make-client.js +0 -101
  198. package/src/util/is-activatable.js +0 -8
  199. package/src/util/is-arrow-table.js +0 -10
  200. package/src/util/selection-types.ts +0 -137
  201. package/src/util/synchronizer.js +0 -47
  202. package/src/util/throttle.js +0 -54
  203. package/src/util/to-data-columns.js +0 -60
  204. package/src/util/void-logger.js +0 -13
  205. package/tsconfig.json +0 -11
  206. package/vitest.config.ts +0 -3
@@ -0,0 +1,51 @@
1
+ import { isArrowTable } from './is-arrow-table.js';
2
+ /**
3
+ * Convert input data to a set of column arrays.
4
+ * @param data The input data.
5
+ * @returns An object with named column arrays.
6
+ */
7
+ export function toDataColumns(data) {
8
+ if (isArrowTable(data)) {
9
+ return arrowToColumns(data);
10
+ }
11
+ else if (Array.isArray(data)) {
12
+ return arrayToColumns(data);
13
+ }
14
+ else {
15
+ throw new Error('Unrecognized data format.');
16
+ }
17
+ }
18
+ /**
19
+ * Convert an Arrow table to a set of column arrays.
20
+ * @param data An Arrow Table.
21
+ * @returns An object with named column arrays.
22
+ */
23
+ function arrowToColumns(data) {
24
+ const { numRows } = data;
25
+ return { numRows, columns: data.toColumns() };
26
+ }
27
+ /**
28
+ * Convert an array of values to a set of column arrays.
29
+ * If the array values are objects, build out named columns.
30
+ * We use the keys of the first object as the column names.
31
+ * Otherwise, use a special "values" array.
32
+ * @param data An array of data objects.
33
+ * @returns An object with named column arrays.
34
+ */
35
+ function arrayToColumns(data) {
36
+ const numRows = data.length;
37
+ if (typeof data[0] === 'object') {
38
+ const names = numRows ? Object.keys(data[0]) : [];
39
+ const columns = {};
40
+ if (names.length > 0) {
41
+ names.forEach(name => {
42
+ columns[name] = data.map(d => d[name]);
43
+ });
44
+ }
45
+ return { numRows, columns };
46
+ }
47
+ else {
48
+ return { numRows, values: data };
49
+ }
50
+ }
51
+ //# sourceMappingURL=to-data-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-data-columns.js","sourceRoot":"","sources":["../../../src/util/to-data-columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAiBnD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAW;IACjC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,EAA8B,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,cAAc,CAAC,IAA8B;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,MAAM,OAAO,GAA8B,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACnB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ interface Logger {
2
+ debug(...args: unknown[]): void;
3
+ info(...args: unknown[]): void;
4
+ log(...args: unknown[]): void;
5
+ warn(...args: unknown[]): void;
6
+ error(...args: unknown[]): void;
7
+ group(label?: string): void;
8
+ groupCollapsed(label?: string): void;
9
+ groupEnd(): void;
10
+ }
11
+ export declare function voidLogger(): Logger;
12
+ export {};
13
+ //# sourceMappingURL=void-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"void-logger.d.ts","sourceRoot":"","sources":["../../../src/util/void-logger.ts"],"names":[],"mappings":"AAAA,UAAU,MAAM;IACd,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED,wBAAgB,UAAU,IAAI,MAAM,CAWnC"}
@@ -0,0 +1,13 @@
1
+ export function voidLogger() {
2
+ return {
3
+ debug() { },
4
+ info() { },
5
+ log() { },
6
+ warn() { },
7
+ error() { },
8
+ group() { },
9
+ groupCollapsed() { },
10
+ groupEnd() { }
11
+ };
12
+ }
13
+ //# sourceMappingURL=void-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"void-logger.js","sourceRoot":"","sources":["../../../src/util/void-logger.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,KAAK,KAAU,CAAC;QAChB,IAAI,KAAU,CAAC;QACf,GAAG,KAAU,CAAC;QACd,IAAI,KAAU,CAAC;QACf,KAAK,KAAU,CAAC;QAChB,KAAK,KAAU,CAAC;QAChB,cAAc,KAAU,CAAC;QACzB,QAAQ,KAAU,CAAC;KACpB,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uwdata/mosaic-core",
3
- "version": "0.16.2",
3
+ "version": "0.18.0",
4
4
  "description": "Scalable and extensible linked data views.",
5
5
  "keywords": [
6
6
  "mosaic",
@@ -13,29 +13,31 @@
13
13
  "author": "Jeffrey Heer (https://idl.uw.edu)",
14
14
  "type": "module",
15
15
  "exports": {
16
- "types": "./dist/types/index-types.d.ts",
17
- "default": "./src/index.js"
16
+ "types": "./dist/src/index.d.ts",
17
+ "default": "./dist/src/index.js"
18
18
  },
19
+ "files": [
20
+ "dist",
21
+ "!dist/tsconfig.tsbuildinfo",
22
+ "src"
23
+ ],
19
24
  "repository": {
20
25
  "type": "git",
21
26
  "url": "https://github.com/uwdata/mosaic.git"
22
27
  },
23
28
  "scripts": {
24
- "prebuild": "rimraf dist && mkdir dist",
25
- "build": "npm run types",
26
- "types": "tsc",
29
+ "clean": "rimraf dist && mkdir dist",
27
30
  "lint": "eslint src test",
28
- "test": "vitest run && npm run tsc",
29
- "tsc": "tsc -p jsconfig.json",
30
- "prepublishOnly": "npm run test && npm run lint && npm run build"
31
+ "test": "vitest run",
32
+ "prepublishOnly": "npm run test && npm run lint && tsc --build"
31
33
  },
32
34
  "dependencies": {
33
35
  "@duckdb/duckdb-wasm": "^1.29.0",
34
- "@uwdata/flechette": "^2.0.0",
35
- "@uwdata/mosaic-sql": "^0.16.2"
36
+ "@uwdata/flechette": "^2.0.1",
37
+ "@uwdata/mosaic-sql": "^0.18.0"
36
38
  },
37
39
  "devDependencies": {
38
- "@uwdata/mosaic-duckdb": "^0.16.2"
40
+ "@uwdata/mosaic-duckdb": "^0.18.0"
39
41
  },
40
- "gitHead": "26d2719f4bcab471d2831145e1f03f39f3509869"
42
+ "gitHead": "dfb9ded0b0307754e3185ca34cc49a1384fe8455"
41
43
  }
@@ -0,0 +1,367 @@
1
+ import { SocketConnector } from './connectors/socket.js';
2
+ import { type Connector } from './connectors/Connector.js';
3
+ import { PreAggregator, type PreAggregateOptions } from './preagg/PreAggregator.js';
4
+ import { voidLogger } from './util/void-logger.js';
5
+ import { QueryManager, Priority } from './QueryManager.js';
6
+ import { type Selection } from './Selection.js';
7
+ import { type Logger, type QueryType } from './types.js';
8
+ import { type QueryResult } from './util/query-result.js';
9
+ import { type MosaicClient } from './MosaicClient.js';
10
+ import { type SelectionClause } from './SelectionClause.js';
11
+ import { MaybeArray } from '@uwdata/mosaic-sql';
12
+
13
+ interface FilterGroupEntry {
14
+ selection: Selection;
15
+ clients: Set<MosaicClient>;
16
+ disconnect(): void;
17
+ }
18
+
19
+ /**
20
+ * The singleton Coordinator instance.
21
+ */
22
+ let _instance: Coordinator;
23
+
24
+ /**
25
+ * Set or retrieve the coordinator instance.
26
+ * @param instance The coordinator instance to set
27
+ * @returns The coordinator instance
28
+ */
29
+ export function coordinator(
30
+ instance?: Coordinator
31
+ ): Coordinator {
32
+ if (instance) {
33
+ _instance = instance;
34
+ } else if (_instance == null) {
35
+ _instance = new Coordinator();
36
+ }
37
+ return _instance;
38
+ }
39
+
40
+ /**
41
+ * A Mosaic Coordinator manages all database communication for clients and
42
+ * handles selection updates. The Coordinator also performs optimizations
43
+ * including query caching, consolidation, and pre-aggregation.
44
+ */
45
+ export class Coordinator {
46
+ public manager: QueryManager;
47
+ public preaggregator: PreAggregator;
48
+ public clients = new Set<MosaicClient>;
49
+ public filterGroups = new Map<Selection, FilterGroupEntry>;
50
+ protected _logger: Logger = voidLogger();
51
+
52
+ /**
53
+ * @param db Database connector. Defaults to a web socket connection.
54
+ * @param options Coordinator options.
55
+ * @param options.logger The logger to use, defaults to `console`.
56
+ * @param options.manager The query manager to use.
57
+ * @param options.cache Boolean flag to enable/disable query caching.
58
+ * @param options.consolidate Boolean flag to enable/disable query consolidation.
59
+ * @param options.preagg Options for the Pre-aggregator.
60
+ */
61
+ constructor(
62
+ db: Connector = new SocketConnector(),
63
+ options: {
64
+ logger?: Logger | null;
65
+ manager?: QueryManager;
66
+ cache?: boolean;
67
+ consolidate?: boolean;
68
+ preagg?: PreAggregateOptions;
69
+ } = {}
70
+ ) {
71
+ const {
72
+ logger = console,
73
+ manager = new QueryManager(),
74
+ cache = true,
75
+ consolidate = true,
76
+ preagg = {}
77
+ } = options;
78
+ this.manager = manager;
79
+ this.manager.cache(cache);
80
+ this.manager.consolidate(consolidate);
81
+ this.databaseConnector(db);
82
+ this.logger(logger);
83
+ this.clear();
84
+ this.preaggregator = new PreAggregator(this, preagg);
85
+ }
86
+
87
+ /**
88
+ * Clear the coordinator state.
89
+ * @param options Options object.
90
+ * @param options.clients If true, disconnect all clients.
91
+ * @param options.cache If true, clear the query cache.
92
+ */
93
+ clear(options: { clients?: boolean; cache?: boolean } = {}) {
94
+ const { clients = true, cache = true } = options;
95
+ this.manager.clear();
96
+ if (clients) {
97
+ this.filterGroups?.forEach(group => group.disconnect());
98
+ this.filterGroups = new Map;
99
+ this.clients?.forEach(client => this.disconnect(client));
100
+ this.clients = new Set;
101
+ }
102
+ if (cache) this.manager.cache()!.clear();
103
+ }
104
+
105
+ /**
106
+ * Get or set the database connector.
107
+ * @param db The database connector to use.
108
+ * @returns The current database connector.
109
+ */
110
+ databaseConnector(): Connector | null;
111
+ databaseConnector(db: Connector): Connector;
112
+ databaseConnector(db?: Connector): Connector | null {
113
+ return db
114
+ ? this.manager.connector(db)
115
+ : this.manager.connector();
116
+ }
117
+
118
+ /**
119
+ * Get or set the logger.
120
+ * @param logger The logger to use.
121
+ * @returns The current logger
122
+ */
123
+ logger(logger?: Logger | null): Logger {
124
+ if (arguments.length) {
125
+ this._logger = logger || voidLogger();
126
+ this.manager.logger(this._logger);
127
+ }
128
+ return this._logger!;
129
+ }
130
+
131
+ // -- Query Management ----
132
+
133
+ /**
134
+ * Cancel previously submitted query requests. These queries will be
135
+ * canceled if they are queued but have not yet been submitted.
136
+ * @param requests An array of query result objects, such as those returned by the `query` method.
137
+ */
138
+ cancel(requests: QueryResult[]) {
139
+ this.manager.cancel(requests);
140
+ }
141
+
142
+ /**
143
+ * Issue a query for which no result (return value) is needed.
144
+ * @param query The query or an array of queries. Each query should be either a Query builder object or a SQL string.
145
+ * @param options An options object.
146
+ * @param options.priority The query priority, defaults to `Priority.Normal`.
147
+ * @returns A query result promise.
148
+ */
149
+ exec(
150
+ query: MaybeArray<QueryType>,
151
+ options: { priority?: number } = {}
152
+ ): QueryResult {
153
+ const { priority = Priority.Normal } = options;
154
+ query = Array.isArray(query) ? query.filter(x => x).join(';\n') : query;
155
+ return this.manager.request({ type: 'exec', query }, priority);
156
+ }
157
+
158
+ /**
159
+ * Issue a query to the backing database. The submitted query may be
160
+ * consolidate with other queries and its results may be cached.
161
+ * @param query The query as either a Query builder object or a SQL string.
162
+ * @param options An options object.
163
+ * @param options.type The query result format type.
164
+ * @param options.cache If true, cache the query result client-side within the QueryManager.
165
+ * @param options.persist If true, request the database server to persist a cached query server-side.
166
+ * @param options.priority The query priority, defaults to `Priority.Normal`.
167
+ * @returns A query result promise.
168
+ */
169
+ query(
170
+ query: QueryType,
171
+ options: {
172
+ type?: 'arrow' | 'json';
173
+ cache?: boolean;
174
+ persist?: boolean;
175
+ priority?: number;
176
+ [key: string]: unknown;
177
+ } = {}
178
+ ): QueryResult {
179
+ const {
180
+ type = 'arrow',
181
+ cache = true,
182
+ priority = Priority.Normal,
183
+ ...otherOptions
184
+ } = options;
185
+ return this.manager.request({ type, query, cache, options: otherOptions }, priority);
186
+ }
187
+
188
+ /**
189
+ * Issue a query to prefetch data for later use. The query result is cached
190
+ * for efficient future access.
191
+ * @param query The query as either a Query builder object or a SQL string.
192
+ * @param options An options object.
193
+ * @param options.type The query result format type.
194
+ * @returns A query result promise.
195
+ */
196
+ prefetch(
197
+ query: QueryType,
198
+ options: { type?: 'arrow' | 'json'; [key: string]: unknown } = {}
199
+ ): QueryResult {
200
+ return this.query(query, { ...options, cache: true, priority: Priority.Low });
201
+ }
202
+
203
+ // -- Client Management ----
204
+
205
+ /**
206
+ * Update client data by submitting the given query and returning the()
207
+ * data (or error) to the client.
208
+ * @param client A Mosaic client.
209
+ * @param query The data query.
210
+ * @param priority The query priority.
211
+ * @returns A Promise that resolves upon completion of the update.
212
+ */
213
+ updateClient(
214
+ client: MosaicClient,
215
+ query: QueryType,
216
+ priority: number = Priority.Normal
217
+ ): Promise<unknown> {
218
+ client.queryPending();
219
+ return client._pending = this.query(query, { priority })
220
+ .then(
221
+ data => client.queryResult(data).update(),
222
+ err => { this._logger?.error(err); client.queryError(err); }
223
+ )
224
+ .catch(err => this._logger?.error(err));
225
+ }
226
+
227
+ /**
228
+ * Issue a query request for a client. If the query is null or undefined,
229
+ * the client is simply updated. Otherwise `updateClient` is called. As a
230
+ * side effect, this method clears the current preaggregator state.
231
+ * @param client The client to update.
232
+ * @param query The query to issue.
233
+ */
234
+ requestQuery(client: MosaicClient, query?: QueryType | null): Promise<unknown> {
235
+ this.preaggregator.clear();
236
+ return query
237
+ ? this.updateClient(client, query)
238
+ : Promise.resolve(client.update());
239
+ }
240
+
241
+ /**
242
+ * Connect a client to the coordinator.
243
+ * Throws an error if the client is already connected.
244
+ * @param client The Mosaic client to connect.
245
+ */
246
+ connect(client: MosaicClient): void {
247
+ const { clients } = this;
248
+
249
+ if (clients?.has(client)) {
250
+ throw new Error('Client already connected.');
251
+ }
252
+
253
+ // add client to client set
254
+ clients?.add(client);
255
+
256
+ // register coordinator on client instance
257
+ client.coordinator = this;
258
+
259
+ // initialize client lifecycle
260
+ client.initialize();
261
+
262
+ // connect filter selection
263
+ connectSelection(this, client.filterBy!, client);
264
+ }
265
+
266
+ /**
267
+ * Disconnect a client from the coordinator.
268
+ * This method has no effect if the client is already disconnected.
269
+ * @param client The Mosaic client to disconnect.
270
+ */
271
+ disconnect(client: MosaicClient): void {
272
+ const { clients, filterGroups } = this;
273
+ if (!clients?.has(client)) return;
274
+ clients.delete(client);
275
+ client.coordinator = null;
276
+
277
+ const group = filterGroups?.get(client.filterBy!);
278
+ if (group) {
279
+ group.clients.delete(client);
280
+ }
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Connect a selection-client pair to the coordinator to process updates.
286
+ * @param mc The Mosaic coordinator.
287
+ * @param selection A selection.
288
+ * @param client A Mosaic client that is filtered by the given selection.
289
+ */
290
+ function connectSelection(
291
+ mc: Coordinator,
292
+ selection: Selection,
293
+ client: MosaicClient
294
+ ): void {
295
+ if (!selection) return;
296
+ let entry = mc.filterGroups?.get(selection);
297
+ if (!entry) {
298
+ const activate = (clause: SelectionClause) => activateSelection(mc, selection, clause);
299
+ const value = () => updateSelection(mc, selection);
300
+
301
+ // @ts-expect-error todo: update selection dispatch types
302
+ selection.addEventListener('activate', activate);
303
+ selection.addEventListener('value', value);
304
+
305
+ entry = {
306
+ selection,
307
+ clients: new Set,
308
+ disconnect() {
309
+ // @ts-expect-error todo: update selection dispatch types
310
+ selection.removeEventListener('activate', activate);
311
+ selection.removeEventListener('value', value);
312
+ }
313
+ };
314
+ mc.filterGroups?.set(selection, entry);
315
+ }
316
+ entry.clients.add(client);
317
+ }
318
+
319
+ /**
320
+ * Activate a selection, providing a clause indicative of potential
321
+ * next updates. Activation provides a preview of likely next events,
322
+ * enabling potential precomputation to optimize updates.
323
+ * @param mc The Mosaic coordinator.
324
+ * @param selection A selection.
325
+ * @param clause A selection clause for the activation.
326
+ */
327
+ function activateSelection(
328
+ mc: Coordinator,
329
+ selection: Selection,
330
+ clause: SelectionClause
331
+ ): void {
332
+ const { preaggregator, filterGroups } = mc;
333
+ const { clients } = filterGroups.get(selection)!;
334
+ for (const client of clients) {
335
+ if (client.enabled) {
336
+ preaggregator.request(client, selection, clause);
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Process an updated selection value, querying filtered data for any
343
+ * associated clients.
344
+ * @param mc The Mosaic coordinator.
345
+ * @param selection A selection.
346
+ * @returns A Promise that resolves when the update completes.
347
+ */
348
+ function updateSelection(
349
+ mc: Coordinator,
350
+ selection: Selection
351
+ ): Promise<PromiseSettledResult<unknown>[]> {
352
+ const { preaggregator, filterGroups } = mc;
353
+ const { clients } = filterGroups!.get(selection)!;
354
+ const { active } = selection;
355
+ return Promise.allSettled(Array.from(clients, (client: MosaicClient) => {
356
+ if (!client.enabled) return client.requestQuery();
357
+ const info = preaggregator.request(client, selection, active);
358
+ const filter = info ? null : selection.predicate(client);
359
+
360
+ // skip due to cross-filtering
361
+ if (info?.skip || (!info && !filter)) return;
362
+
363
+ // @ts-expect-error FIXME
364
+ const query = info?.query(active.predicate) ?? client.query(filter);
365
+ return mc.updateClient(client, query);
366
+ }));
367
+ }