@genome-spy/core 0.67.0 → 0.68.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 (172) hide show
  1. package/dist/bundle/index.es.js +7641 -6313
  2. package/dist/bundle/index.js +115 -134
  3. package/dist/schema.json +534 -132
  4. package/dist/src/data/collector.d.ts +20 -0
  5. package/dist/src/data/collector.d.ts.map +1 -1
  6. package/dist/src/data/collector.js +148 -0
  7. package/dist/src/data/dataFlow.d.ts +6 -0
  8. package/dist/src/data/dataFlow.d.ts.map +1 -1
  9. package/dist/src/data/dataFlow.js +10 -0
  10. package/dist/src/data/flowInit.d.ts.map +1 -1
  11. package/dist/src/data/flowInit.js +2 -3
  12. package/dist/src/data/flowNode.d.ts +8 -0
  13. package/dist/src/data/flowNode.d.ts.map +1 -1
  14. package/dist/src/data/flowNode.js +18 -0
  15. package/dist/src/data/keyIndex.d.ts +18 -0
  16. package/dist/src/data/keyIndex.d.ts.map +1 -0
  17. package/dist/src/data/keyIndex.js +241 -0
  18. package/dist/src/data/keyIndex.test.d.ts +2 -0
  19. package/dist/src/data/keyIndex.test.d.ts.map +1 -0
  20. package/dist/src/data/sources/dataSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/dataSource.js +5 -1
  22. package/dist/src/data/sources/dataSourceFactory.d.ts +14 -12
  23. package/dist/src/data/sources/dataSourceFactory.d.ts.map +1 -1
  24. package/dist/src/data/sources/dataSourceFactory.js +52 -16
  25. package/dist/src/data/sources/lazy/mockLazySource.d.ts +29 -0
  26. package/dist/src/data/sources/lazy/mockLazySource.d.ts.map +1 -0
  27. package/dist/src/data/sources/lazy/mockLazySource.js +44 -0
  28. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +22 -1
  29. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  30. package/dist/src/data/sources/lazy/singleAxisLazySource.js +34 -2
  31. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  32. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +15 -0
  33. package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
  34. package/dist/src/data/sources/lazy/tabixSource.js +15 -5
  35. package/dist/src/data/transforms/stack.d.ts.map +1 -1
  36. package/dist/src/data/transforms/stack.js +1 -0
  37. package/dist/src/encoder/accessor.d.ts +43 -0
  38. package/dist/src/encoder/accessor.d.ts.map +1 -1
  39. package/dist/src/encoder/accessor.js +164 -0
  40. package/dist/src/encoder/encoder.d.ts +11 -2
  41. package/dist/src/encoder/encoder.d.ts.map +1 -1
  42. package/dist/src/encoder/encoder.js +24 -4
  43. package/dist/src/encoder/metadataChannels.d.ts +15 -0
  44. package/dist/src/encoder/metadataChannels.d.ts.map +1 -0
  45. package/dist/src/encoder/metadataChannels.js +65 -0
  46. package/dist/src/encoder/metadataChannels.test.d.ts +2 -0
  47. package/dist/src/encoder/metadataChannels.test.d.ts.map +1 -0
  48. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  49. package/dist/src/genome/scaleLocus.js +14 -1
  50. package/dist/src/genomeSpy/containerUi.d.ts +0 -1
  51. package/dist/src/genomeSpy/containerUi.d.ts.map +1 -1
  52. package/dist/src/genomeSpy/containerUi.js +0 -14
  53. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts +3 -7
  54. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts.map +1 -1
  55. package/dist/src/genomeSpy/loadingIndicatorManager.js +68 -20
  56. package/dist/src/genomeSpy/loadingStatusRegistry.d.ts +52 -0
  57. package/dist/src/genomeSpy/loadingStatusRegistry.d.ts.map +1 -0
  58. package/dist/src/genomeSpy/loadingStatusRegistry.js +86 -0
  59. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -1
  60. package/dist/src/genomeSpy/viewContextFactory.js +0 -1
  61. package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -1
  62. package/dist/src/genomeSpy/viewDataInit.js +56 -11
  63. package/dist/src/genomeSpy.d.ts +0 -2
  64. package/dist/src/genomeSpy.d.ts.map +1 -1
  65. package/dist/src/genomeSpy.js +46 -26
  66. package/dist/src/marks/mark.d.ts.map +1 -1
  67. package/dist/src/marks/mark.js +18 -11
  68. package/dist/src/marks/markUtils.js +1 -1
  69. package/dist/src/scale/scale.d.ts +6 -1
  70. package/dist/src/scale/scale.d.ts.map +1 -1
  71. package/dist/src/scale/scale.js +83 -23
  72. package/dist/src/scales/axisResolution.d.ts.map +1 -1
  73. package/dist/src/scales/axisResolution.js +10 -0
  74. package/dist/src/scales/{scaleDomainAggregator.d.ts → domainPlanner.d.ts} +6 -3
  75. package/dist/src/scales/domainPlanner.d.ts.map +1 -0
  76. package/dist/src/scales/{scaleDomainAggregator.js → domainPlanner.js} +128 -10
  77. package/dist/src/scales/domainPlanner.test.d.ts +2 -0
  78. package/dist/src/scales/domainPlanner.test.d.ts.map +1 -0
  79. package/dist/src/scales/scaleInteractionController.d.ts +6 -0
  80. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  81. package/dist/src/scales/scaleInteractionController.js +41 -3
  82. package/dist/src/scales/scaleResolution.d.ts +19 -17
  83. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  84. package/dist/src/scales/scaleResolution.js +181 -70
  85. package/dist/src/scales/scaleResolution.test.d.ts.map +1 -1
  86. package/dist/src/selection/selection.d.ts +21 -0
  87. package/dist/src/selection/selection.d.ts.map +1 -1
  88. package/dist/src/selection/selection.js +82 -0
  89. package/dist/src/spec/channel.d.ts +52 -15
  90. package/dist/src/spec/data.d.ts +4 -0
  91. package/dist/src/spec/parameter.d.ts +16 -11
  92. package/dist/src/spec/testing.d.ts +12 -0
  93. package/dist/src/spec/testing.d.ts.map +1 -0
  94. package/dist/src/spec/testing.js +20 -0
  95. package/dist/src/spec/view.d.ts +45 -10
  96. package/dist/src/styles/genome-spy.css +3 -31
  97. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  98. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  99. package/dist/src/styles/genome-spy.css.js +0 -29
  100. package/dist/src/types/encoder.d.ts +37 -2
  101. package/dist/src/types/rendering.d.ts +4 -3
  102. package/dist/src/types/viewContext.d.ts +0 -14
  103. package/dist/src/utils/throttle.d.ts +4 -1
  104. package/dist/src/utils/throttle.d.ts.map +1 -1
  105. package/dist/src/utils/throttle.js +54 -23
  106. package/dist/src/utils/throttle.test.d.ts +2 -0
  107. package/dist/src/utils/throttle.test.d.ts.map +1 -0
  108. package/dist/src/utils/transition.d.ts +21 -0
  109. package/dist/src/utils/transition.d.ts.map +1 -1
  110. package/dist/src/utils/transition.js +28 -0
  111. package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
  112. package/dist/src/utils/ui/tooltip.js +7 -1
  113. package/dist/src/utils/ui/tooltip.test.d.ts +2 -0
  114. package/dist/src/utils/ui/tooltip.test.d.ts.map +1 -0
  115. package/dist/src/view/axisGridView.d.ts.map +1 -1
  116. package/dist/src/view/axisGridView.js +22 -5
  117. package/dist/src/view/axisView.d.ts.map +1 -1
  118. package/dist/src/view/axisView.js +20 -5
  119. package/dist/src/view/concatView.js +3 -3
  120. package/dist/src/view/containerMutationHelper.js +1 -1
  121. package/dist/src/view/containerView.d.ts +9 -5
  122. package/dist/src/view/containerView.d.ts.map +1 -1
  123. package/dist/src/view/containerView.js +34 -9
  124. package/dist/src/view/dataReadiness.d.ts +46 -0
  125. package/dist/src/view/dataReadiness.d.ts.map +1 -0
  126. package/dist/src/view/dataReadiness.js +267 -0
  127. package/dist/src/view/dataReadiness.test.d.ts +2 -0
  128. package/dist/src/view/dataReadiness.test.d.ts.map +1 -0
  129. package/dist/src/view/facetView.d.ts.map +1 -1
  130. package/dist/src/view/facetView.js +7 -5
  131. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  132. package/dist/src/view/flowBuilder.js +5 -1
  133. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  134. package/dist/src/view/gridView/gridChild.js +8 -0
  135. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  136. package/dist/src/view/gridView/gridView.js +119 -2
  137. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  138. package/dist/src/view/gridView/scrollbar.js +3 -0
  139. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  140. package/dist/src/view/gridView/selectionRect.js +20 -5
  141. package/dist/src/view/gridView/separatorView.d.ts +51 -0
  142. package/dist/src/view/gridView/separatorView.d.ts.map +1 -0
  143. package/dist/src/view/gridView/separatorView.js +275 -0
  144. package/dist/src/view/layerView.js +3 -3
  145. package/dist/src/view/layout/flexLayout.d.ts +0 -30
  146. package/dist/src/view/layout/flexLayout.d.ts.map +1 -1
  147. package/dist/src/view/layout/flexLayout.js +0 -86
  148. package/dist/src/view/paramMediator.d.ts +19 -0
  149. package/dist/src/view/paramMediator.d.ts.map +1 -1
  150. package/dist/src/view/paramMediator.js +86 -19
  151. package/dist/src/view/testUtils.d.ts.map +1 -1
  152. package/dist/src/view/testUtils.js +6 -1
  153. package/dist/src/view/unitView.d.ts +8 -13
  154. package/dist/src/view/unitView.d.ts.map +1 -1
  155. package/dist/src/view/unitView.js +110 -41
  156. package/dist/src/view/view.d.ts +22 -14
  157. package/dist/src/view/view.d.ts.map +1 -1
  158. package/dist/src/view/view.js +93 -9
  159. package/dist/src/view/viewFactory.d.ts.map +1 -1
  160. package/dist/src/view/viewFactory.js +20 -1
  161. package/dist/src/view/viewSelectors.d.ts +148 -0
  162. package/dist/src/view/viewSelectors.d.ts.map +1 -0
  163. package/dist/src/view/viewSelectors.js +773 -0
  164. package/dist/src/view/viewSelectors.test.d.ts +2 -0
  165. package/dist/src/view/viewSelectors.test.d.ts.map +1 -0
  166. package/dist/src/view/viewUtils.d.ts +0 -8
  167. package/dist/src/view/viewUtils.d.ts.map +1 -1
  168. package/dist/src/view/viewUtils.js +1 -21
  169. package/package.json +3 -3
  170. package/dist/src/scales/scaleDomainAggregator.d.ts.map +0 -1
  171. package/dist/src/scales/scaleDomainAggregator.test.d.ts +0 -2
  172. package/dist/src/scales/scaleDomainAggregator.test.d.ts.map +0 -1
@@ -19,6 +19,19 @@ export default class Collector extends FlowNode {
19
19
  * @returns {() => void}
20
20
  */
21
21
  observe(listener: (arg0: Collector) => void): () => void;
22
+ /**
23
+ * @param {string} domainKey
24
+ * @param {import("../spec/channel.js").Type} type
25
+ * @param {import("../types/encoder.js").Accessor} accessor
26
+ * @returns {import("../utils/domainArray.js").DomainArray}
27
+ */
28
+ getDomain(domainKey: string, type: import("../spec/channel.js").Type, accessor: import("../types/encoder.js").Accessor): import("../utils/domainArray.js").DomainArray;
29
+ /**
30
+ * @param {string} domainKey
31
+ * @param {() => void} listener
32
+ * @returns {() => void}
33
+ */
34
+ subscribeDomainChanges(domainKey: string, listener: () => void): () => void;
22
35
  /**
23
36
  * @returns {Iterable<Datum>}
24
37
  */
@@ -38,6 +51,13 @@ export default class Collector extends FlowNode {
38
51
  * @param {number} uniqueId
39
52
  */
40
53
  findDatumByUniqueId(uniqueId: number): import("./flowNode.js").Datum;
54
+ /**
55
+ * Uses a lazy index to find a datum by its key fields.
56
+ *
57
+ * @param {string[]} keyFields
58
+ * @param {import("../spec/channel.js").Scalar[]} keyTuple
59
+ */
60
+ findDatumByKey(keyFields: string[], keyTuple: import("../spec/channel.js").Scalar[]): import("./flowNode.js").Datum;
41
61
  #private;
42
62
  }
43
63
  import FlowNode from "./flowNode.js";
@@ -1 +1 @@
1
- {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../../src/data/collector.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH;IAuCI;;OAEG;IACH,qBAFW,OAAO,sBAAsB,EAAE,aAAa,EAiBtD;IAZG,qDAA2C;IAE3C,4CAA4C;IAC5C,WADW,GAAG,CAAC,CAAS,IAAS,EAAT,SAAS,KAAE,IAAI,CAAC,CACd;IAG1B,yFAAyF;IACzF,cADW,GAAG,CAAC,OAAO,oBAAoB,EAAE,MAAM,EAAE,+BAAO,CACN;IAmFzD;;;OAGG;IACH,kBAHW,CAAS,IAAS,EAAT,SAAS,KAAE,IAAI,GACtB,MAAM,IAAI,CAOtB;IA+BD;;OAEG;IACH,WAFa,QAAQ,+BAAO,CAqB3B;IAED;;;OAGG;IACH,mBAFW,CAAC,KAAK,+BAAO,KAAK,IAAI,QAUhC;IAED;;OAEG;IACH,uBAMC;IA8CD;;;;OAIG;IACH,8BAFW,MAAM,iCA4BhB;;CACJ;qBAxTyD,eAAe"}
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../../src/data/collector.js"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH;IA6CI;;OAEG;IACH,qBAFW,OAAO,sBAAsB,EAAE,aAAa,EAiBtD;IAZG,qDAA2C;IAE3C,4CAA4C;IAC5C,WADW,GAAG,CAAC,CAAS,IAAS,EAAT,SAAS,KAAE,IAAI,CAAC,CACd;IAG1B,yFAAyF;IACzF,cADW,GAAG,CAAC,OAAO,oBAAoB,EAAE,MAAM,EAAE,+BAAO,CACN;IAwFzD;;;OAGG;IACH,kBAHW,CAAS,IAAS,EAAT,SAAS,KAAE,IAAI,GACtB,MAAM,IAAI,CAOtB;IAiCD;;;;;OAKG;IACH,qBALW,MAAM,QACN,OAAO,oBAAoB,EAAE,IAAI,YACjC,OAAO,qBAAqB,EAAE,QAAQ,GACpC,OAAO,yBAAyB,EAAE,WAAW,CAkBzD;IAED;;;;OAIG;IACH,kCAJW,MAAM,YACN,MAAM,IAAI,GACR,MAAM,IAAI,CAItB;IAED;;OAEG;IACH,WAFa,QAAQ,+BAAO,CAqB3B;IAED;;;OAGG;IACH,mBAFW,CAAC,KAAK,+BAAO,KAAK,IAAI,QAUhC;IAED;;OAEG;IACH,uBAMC;IAqDD;;;;OAIG;IACH,8BAFW,MAAM,iCA4BhB;IAED;;;;;OAKG;IACH,0BAHW,MAAM,EAAE,YACR,OAAO,oBAAoB,EAAE,MAAM,EAAE,iCAS/C;;CACJ;qBA9XyD,eAAe"}
@@ -7,6 +7,8 @@ import { field } from "../utils/field.js";
7
7
  import { asArray } from "../utils/arrayUtils.js";
8
8
  import { radixSortIntoLookupArray } from "../utils/radixSort.js";
9
9
  import { UNIQUE_ID_KEY } from "./transforms/identifier.js";
10
+ import createDomain from "../utils/domainArray.js";
11
+ import KeyIndex from "./keyIndex.js";
10
12
 
11
13
  /**
12
14
  * Collects (materializes) the data that flows through this node.
@@ -33,6 +35,9 @@ export default class Collector extends FlowNode {
33
35
  */
34
36
  #uniqueIdIndex = [];
35
37
 
38
+ /** @type {KeyIndex} */
39
+ #keyIndex = new KeyIndex();
40
+
36
41
  /**
37
42
  * Start and end indices of all facets if they are concatenated into a single array.
38
43
  * Used together with the uniqueIdIndex for looking up data items by their unique id.
@@ -45,6 +50,9 @@ export default class Collector extends FlowNode {
45
50
  */
46
51
  #comparator;
47
52
 
53
+ /** @type {DomainCache} */
54
+ #domainCache = new DomainCache();
55
+
48
56
  get behavior() {
49
57
  return BEHAVIOR_COLLECTS;
50
58
  }
@@ -76,6 +84,7 @@ export default class Collector extends FlowNode {
76
84
  #init() {
77
85
  this.#buffer = [];
78
86
  this.#uniqueIdIndex = [];
87
+ this.#keyIndex.invalidate();
79
88
 
80
89
  this.facetBatches.clear();
81
90
  this.facetBatches.set(undefined, this.#buffer);
@@ -97,6 +106,8 @@ export default class Collector extends FlowNode {
97
106
  * @param {import("../types/flowBatch.js").FlowBatch} flowBatch
98
107
  */
99
108
  beginBatch(flowBatch) {
109
+ this.#keyIndex.invalidate();
110
+
100
111
  if (isFacetBatch(flowBatch)) {
101
112
  this.#buffer = [];
102
113
  this.facetBatches.set(asArray(flowBatch.facetId), this.#buffer);
@@ -144,6 +155,8 @@ export default class Collector extends FlowNode {
144
155
 
145
156
  super.complete();
146
157
 
158
+ this.#invalidateDomains();
159
+
147
160
  for (const observer of this.observers) {
148
161
  observer(this);
149
162
  }
@@ -187,6 +200,41 @@ export default class Collector extends FlowNode {
187
200
  for (const child of this.children) {
188
201
  child.complete();
189
202
  }
203
+
204
+ this.#invalidateDomains();
205
+ }
206
+
207
+ /**
208
+ * @param {string} domainKey
209
+ * @param {import("../spec/channel.js").Type} type
210
+ * @param {import("../types/encoder.js").Accessor} accessor
211
+ * @returns {import("../utils/domainArray.js").DomainArray}
212
+ */
213
+ getDomain(domainKey, type, accessor) {
214
+ return this.#domainCache.getDomain(domainKey, () => {
215
+ const domain = createDomain(type);
216
+
217
+ if (accessor.constant) {
218
+ domain.extend(accessor({}));
219
+ } else if (this.completed) {
220
+ for (const data of this.facetBatches.values()) {
221
+ for (let i = 0, n = data.length; i < n; i++) {
222
+ domain.extend(accessor(data[i]));
223
+ }
224
+ }
225
+ }
226
+
227
+ return domain;
228
+ });
229
+ }
230
+
231
+ /**
232
+ * @param {string} domainKey
233
+ * @param {() => void} listener
234
+ * @returns {() => void}
235
+ */
236
+ subscribeDomainChanges(domainKey, listener) {
237
+ return this.#domainCache.subscribe(domainKey, listener);
190
238
  }
191
239
 
192
240
  /**
@@ -246,6 +294,13 @@ export default class Collector extends FlowNode {
246
294
  }
247
295
  }
248
296
 
297
+ #invalidateDomains() {
298
+ if (this.#domainCache.hasCachedDomains()) {
299
+ this.#domainCache.clear();
300
+ }
301
+ this.#domainCache.notify();
302
+ }
303
+
249
304
  /**
250
305
  * Builds an index for looking up data items by their unique id.
251
306
  * Using a sorted index and binary search for O(log n) complexity.
@@ -314,6 +369,99 @@ export default class Collector extends FlowNode {
314
369
  }
315
370
  }
316
371
  }
372
+
373
+ /**
374
+ * Uses a lazy index to find a datum by its key fields.
375
+ *
376
+ * @param {string[]} keyFields
377
+ * @param {import("../spec/channel.js").Scalar[]} keyTuple
378
+ */
379
+ findDatumByKey(keyFields, keyTuple) {
380
+ this.#checkStatus();
381
+ return this.#keyIndex.findDatum(
382
+ keyFields,
383
+ keyTuple,
384
+ this.facetBatches.values()
385
+ );
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Manages cached domains and subscriptions for invalidation.
391
+ */
392
+ class DomainCache {
393
+ /** @type {Map<string, import("../utils/domainArray.js").DomainArray>} */
394
+ #cache = new Map();
395
+
396
+ /** @type {Map<string, Set<() => void>>} */
397
+ #observers = new Map();
398
+
399
+ hasCachedDomains() {
400
+ return this.#cache.size > 0;
401
+ }
402
+
403
+ clear() {
404
+ this.#cache.clear();
405
+ }
406
+
407
+ /**
408
+ * @param {string} domainKey
409
+ * @param {() => import("../utils/domainArray.js").DomainArray} build
410
+ * @returns {import("../utils/domainArray.js").DomainArray}
411
+ */
412
+ getDomain(domainKey, build) {
413
+ const cached = this.#cache.get(domainKey);
414
+ if (cached) {
415
+ return cached;
416
+ } else {
417
+ const domain = build();
418
+ this.#cache.set(domainKey, domain);
419
+ return domain;
420
+ }
421
+ }
422
+
423
+ /**
424
+ * @param {string} domainKey
425
+ * @param {() => void} listener
426
+ * @returns {() => void}
427
+ */
428
+ subscribe(domainKey, listener) {
429
+ let listeners = this.#observers.get(domainKey);
430
+ if (!listeners) {
431
+ listeners = new Set();
432
+ this.#observers.set(domainKey, listeners);
433
+ }
434
+ listeners.add(listener);
435
+
436
+ return () => {
437
+ const entry = this.#observers.get(domainKey);
438
+ if (!entry) {
439
+ return;
440
+ }
441
+ entry.delete(listener);
442
+ if (entry.size === 0) {
443
+ this.#observers.delete(domainKey);
444
+ }
445
+ };
446
+ }
447
+
448
+ notify() {
449
+ if (this.#observers.size === 0) {
450
+ return;
451
+ }
452
+
453
+ /** @type {Set<() => void>} */
454
+ const listeners = new Set();
455
+ for (const observers of this.#observers.values()) {
456
+ for (const observer of observers) {
457
+ listeners.add(observer);
458
+ }
459
+ }
460
+
461
+ for (const listener of listeners) {
462
+ listener();
463
+ }
464
+ }
317
465
  }
318
466
 
319
467
  /**
@@ -2,6 +2,12 @@
2
2
  * DataFlow holds data sources and collectors for optimization and initialization.
3
3
  */
4
4
  export default class DataFlow {
5
+ /**
6
+ * Registry for per-view loading status. The host may replace this.
7
+ *
8
+ * @type {import("../genomeSpy/loadingStatusRegistry.js").default}
9
+ */
10
+ loadingStatusRegistry: import("../genomeSpy/loadingStatusRegistry.js").default;
5
11
  get dataSources(): DataSource[];
6
12
  get collectors(): import("./collector.js").default[];
7
13
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dataFlow.d.ts","sourceRoot":"","sources":["../../../src/data/dataFlow.js"],"names":[],"mappings":"AAGA;;GAEG;AACH;IAeI,gCAEC;IAED,qDAEC;IAED;;OAEG;IACH,gCAFW,QAAQ,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,QAI7D;IAED;;OAEG;IACH,0BAFW,OAAO,yBAAyB,EAAE,OAAO,QAInD;IAED;;OAEG;IACH,6BAFW,OAAO,yBAAyB,EAAE,OAAO,QAInD;IAED;;OAEG;IACH,wBAFW,OAAO,gBAAgB,EAAE,OAAO,QAI1C;IAED;;OAEG;IACH,2BAFW,OAAO,gBAAgB,EAAE,OAAO,QAK1C;IAED;;;;OAIG;IACH,gCAFW,OAAO,gBAAgB,EAAE,OAAO,QAiB1C;IAED;;OAEG;IACH,0BAFW,MAAM;;MA2BhB;;CAGJ;uBAtHsB,yBAAyB;wBACxB,0BAA0B"}
1
+ {"version":3,"file":"dataFlow.d.ts","sourceRoot":"","sources":["../../../src/data/dataFlow.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;IAOI;;;;OAIG;IACH,uBAFU,OAAO,uCAAuC,EAAE,OAAO,CAE3C;IAYtB,gCAEC;IAED,qDAEC;IAED;;OAEG;IACH,gCAFW,QAAQ,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,QAI7D;IAED;;OAEG;IACH,0BAFW,OAAO,yBAAyB,EAAE,OAAO,QAInD;IAED;;OAEG;IACH,6BAFW,OAAO,yBAAyB,EAAE,OAAO,QAInD;IAED;;OAEG;IACH,wBAFW,OAAO,gBAAgB,EAAE,OAAO,QAI1C;IAED;;OAEG;IACH,2BAFW,OAAO,gBAAgB,EAAE,OAAO,QAK1C;IAED;;;;OAIG;IACH,gCAFW,OAAO,gBAAgB,EAAE,OAAO,QAiB1C;IAED;;OAEG;IACH,0BAFW,MAAM;;MA2BhB;;CAGJ;uBA/HsB,yBAAyB;wBACxB,0BAA0B"}
@@ -1,3 +1,4 @@
1
+ import LoadingStatusRegistry from "../genomeSpy/loadingStatusRegistry.js";
1
2
  import DataSource from "./sources/dataSource.js";
2
3
  import NamedSource from "./sources/namedSource.js";
3
4
 
@@ -11,12 +12,21 @@ export default class DataFlow {
11
12
  /** @type {Set<import("./collector.js").default>} */
12
13
  #collectors;
13
14
 
15
+ /**
16
+ * Registry for per-view loading status. The host may replace this.
17
+ *
18
+ * @type {import("../genomeSpy/loadingStatusRegistry.js").default}
19
+ */
20
+ loadingStatusRegistry;
21
+
14
22
  constructor() {
15
23
  /** @type {Set<import("./sources/dataSource.js").default>} */
16
24
  this.#dataSources = new Set();
17
25
 
18
26
  /** @type {Set<import("./collector.js").default>} */
19
27
  this.#collectors = new Set();
28
+
29
+ this.loadingStatusRegistry = new LoadingStatusRegistry();
20
30
  }
21
31
 
22
32
  get dataSources() {
@@ -1 +1 @@
1
- {"version":3,"file":"flowInit.d.ts","sourceRoot":"","sources":["../../../src/data/flowInit.js"],"names":[],"mappings":"AAyDA;;;;;GAKG;AACH,sCAHW,OAAO,iBAAiB,EAAE,OAAO,qBACjC,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,EAAE,OAAO,yBAAyB,EAAE,OAAO,CAAC,QAcnG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,mDAXW,OAAO,iBAAiB,EAAE,OAAO,QACjC,OAAO,eAAe,EAAE,OAAO,eAC/B,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,gCACpD,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,GAClD;IACN,QAAQ,EAAE,OAAO,eAAe,EAAE,OAAO,CAAC;IAC1C,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,WAAW,EAAE,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,CAAC;IAC5D,gBAAgB,EAAE,OAAO,CAAC,OAAO,kBAAkB,EAAE,OAAO,CAAC,EAAE,CAAA;CAClE,CA4FH;AAED;;;;;;;GAOG;AACH,2DAJW,OAAO,iBAAiB,EAAE,OAAO,GAAG,OAAO,iBAAiB,EAAE,OAAO,EAAE,eACvE,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,GAClD,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,CAsB1D;AAED;;;;;;;GAOG;AACH,kEAJW,OAAO,iBAAiB,EAAE,OAAO,eACjC,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,GAClD,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,CAe1D;AAED;;;;;;;;;GASG;AACH,iDANW,OAAO,iBAAiB,EAAE,OAAO,gBACjC,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,eAC9C,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,gBACpD;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACvB,OAAO,CAAC,IAAI,EAAE,CAAC,CAuB3B;qBA5SoB,qBAAqB"}
1
+ {"version":3,"file":"flowInit.d.ts","sourceRoot":"","sources":["../../../src/data/flowInit.js"],"names":[],"mappings":"AAwDA;;;;;GAKG;AACH,sCAHW,OAAO,iBAAiB,EAAE,OAAO,qBACjC,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,EAAE,OAAO,yBAAyB,EAAE,OAAO,CAAC,QAcnG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,mDAXW,OAAO,iBAAiB,EAAE,OAAO,QACjC,OAAO,eAAe,EAAE,OAAO,eAC/B,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,gCACpD,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,GAClD;IACN,QAAQ,EAAE,OAAO,eAAe,EAAE,OAAO,CAAC;IAC1C,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,WAAW,EAAE,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,CAAC;IAC5D,gBAAgB,EAAE,OAAO,CAAC,OAAO,kBAAkB,EAAE,OAAO,CAAC,EAAE,CAAA;CAClE,CA6FH;AAED;;;;;;;GAOG;AACH,2DAJW,OAAO,iBAAiB,EAAE,OAAO,GAAG,OAAO,iBAAiB,EAAE,OAAO,EAAE,eACvE,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,GAClD,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,CAsB1D;AAED;;;;;;;GAOG;AACH,kEAJW,OAAO,iBAAiB,EAAE,OAAO,eACjC,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,GAClD,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,CAe1D;AAED;;;;;;;;;GASG;AACH,iDANW,OAAO,iBAAiB,EAAE,OAAO,gBACjC,GAAG,CAAC,OAAO,yBAAyB,EAAE,OAAO,CAAC,eAC9C,CAAC,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,KAAK,OAAO,gBACpD;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACvB,OAAO,CAAC,IAAI,EAAE,CAAC,CAsB3B;qBA3SoB,qBAAqB"}
@@ -2,7 +2,6 @@ import UnitView from "../view/unitView.js";
2
2
  import { buildDataFlow } from "../view/flowBuilder.js";
3
3
  import { optimizeDataFlow } from "./flowOptimizer.js";
4
4
  import { VISIT_SKIP } from "../view/view.js";
5
- import { reconfigureScaleDomains } from "../scales/scaleResolution.js";
6
5
 
7
6
  /** @type {WeakMap<import("./sources/dataSource.js").default, Promise<void>>} */
8
7
  const inFlightLoads = new WeakMap();
@@ -163,7 +162,7 @@ export function initializeViewSubtree(
163
162
 
164
163
  // Initialize flow nodes for the sources that belong to this subtree.
165
164
  for (const dataSource of dataSources) {
166
- dataSource.visit((node) => node.initialize());
165
+ dataSource.visit((node) => node.initializeOnce());
167
166
  }
168
167
 
169
168
  /** @type {UnitView[]} */
@@ -180,6 +179,7 @@ export function initializeViewSubtree(
180
179
  const mark = view.mark;
181
180
  // Encoders can be initialized immediately; graphics need a GL context.
182
181
  mark.initializeEncoders();
182
+ view.registerDomainSubscriptions();
183
183
  if (canInitializeGraphics) {
184
184
  graphicsPromises.push(mark.initializeGraphics().then(() => mark));
185
185
  }
@@ -294,7 +294,6 @@ export function loadViewSubtreeData(
294
294
  loadDataSourceOnce(dataSource, loadOptions)
295
295
  )
296
296
  ).then((results) => {
297
- reconfigureScaleDomains(subtreeRoot, viewFilter);
298
297
  broadcastSubtreeDataReady(subtreeRoot);
299
298
  return results;
300
299
  });
@@ -69,8 +69,16 @@ export default class FlowNode {
69
69
  /**
70
70
  * Allows for doing final initialization after the flow structure has been
71
71
  * built and optimized. Must be called before any data are to be propagated.
72
+ * Note: Some transforms call initialize() from reset() to rebuild internal
73
+ * fast paths per batch. Use initializeOnce() for graph-level init to avoid
74
+ * double-initialization issues.
72
75
  */
73
76
  initialize(): void;
77
+ /**
78
+ * Calls initialize() once per node instance. Intended for graph-level init
79
+ * passes that should not rerun when reusing existing branches.
80
+ */
81
+ initializeOnce(): void;
74
82
  /**
75
83
  * @param {Datum} datum
76
84
  * @protected
@@ -1 +1 @@
1
- {"version":3,"file":"flowNode.d.ts","sourceRoot":"","sources":["../../../src/data/flowNode.js"],"names":[],"mappings":"AAuUA;;;GAGG;AACH,uCAHW,OAAO,uBAAuB,EAAE,SAAS,GACvC,SAAS,IAAI,OAAO,uBAAuB,EAAE,SAAS,CAIlE;AAED;;;GAGG;AACH,wCAHW,OAAO,uBAAuB,EAAE,SAAS,GACvC,SAAS,IAAI,OAAO,uBAAuB,EAAE,UAAU,CAInE;AAnVD;;;GAGG;AACH,qCAAsC;AAEtC;;;GAGG;AACH,uCAAwC;AAExC;;;GAGG;AACH,uCAAwC;AAExC;;GAEG;AAEH;;;;;GAKG;AACH;IA2BI;;OAEG;IACH,oCAFW,qBAAqB,EAa/B;IAxCD;;QAEI,oBAAoB;eAAT,KAAK;MAElB;IAEF;;;;OAIG;IACH,uBAFU,qBAAqB,CAEF;IAE7B,uBAEC;IAED;;;;OAIG;IACH,aAFa,MAAM,CAIlB;IAQG,yBAAyB;IACzB,UADW,QAAQ,EAAE,CACH;IAElB,uBAAuB;IACvB,QADW,QAAQ,CACI;IAEvB,2CAA2C;IAC3C,mBAAsB;IAG1B;;;OAGG;IACH,cASC;IAED;;;OAGG;IACH,mBAEC;IAgOD;;;OAGG;IACH,4BAHW,KAAK,QAKf;IA7MD;;;OAGG;IACH,kBAFW,QAAQ,QAIlB;IAED;;;OAGG;IACH,gBAFW,QAAQ,QAUlB;IAED;;OAEG;IACH,YAFW,QAAQ,QAQlB;IAED;;OAEG;IACH,6BAFW,QAAQ,QAMlB;IAED;;OAEG;IACH,0BAFW,QAAQ,QAalB;IAED;;;OAGG;IACH,mBAFW,QAAQ,QAWlB;IAED;;OAEG;IACH,eAiBC;IAED,kBAEC;IAED,uBAEC;IAED,sBAEC;IAED;;;;OAIG;IACH,eAFW,CAAC,CAAS,IAAQ,EAAR,QAAQ,KAAE,IAAI,CAAC,GAAG;QAAE,aAAa,CAAC,EAAE,CAAS,IAAQ,EAAR,QAAQ,KAAE,IAAI,CAAA;KAAC,QAchF;IAED;;;OAGG;IACH,wBAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;OAGG;IACH,cAFW,KAAK,QAKf;IAED,iBAMC;IAED;;;;OAIG;IACH,sBAFW,OAAO,uBAAuB,EAAE,SAAS,QAMnD;IAED;;;OAGG;IACH,+BAHa,OAAO,0BAA0B,EAAE,OAAO,CAYtD;IAED;;;;OAIG;IACH,8BAQC;;CASJ;oCAhTY;IAAC,aAAa,EAAE,OAAO,0BAA0B,EAAE,OAAO,CAAA;CAAC;;;;oBAM3D,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;mBACnB,KAAK,EAAE"}
1
+ {"version":3,"file":"flowNode.d.ts","sourceRoot":"","sources":["../../../src/data/flowNode.js"],"names":[],"mappings":"AAyVA;;;GAGG;AACH,uCAHW,OAAO,uBAAuB,EAAE,SAAS,GACvC,SAAS,IAAI,OAAO,uBAAuB,EAAE,SAAS,CAIlE;AAED;;;GAGG;AACH,wCAHW,OAAO,uBAAuB,EAAE,SAAS,GACvC,SAAS,IAAI,OAAO,uBAAuB,EAAE,UAAU,CAInE;AArWD;;;GAGG;AACH,qCAAsC;AAEtC;;;GAGG;AACH,uCAAwC;AAExC;;;GAGG;AACH,uCAAwC;AAExC;;GAEG;AAEH;;;;;GAKG;AACH;IA8BI;;OAEG;IACH,oCAFW,qBAAqB,EAa/B;IA3CD;;QAEI,oBAAoB;eAAT,KAAK;MAElB;IAKF;;;;OAIG;IACH,uBAFU,qBAAqB,CAEF;IAE7B,uBAEC;IAED;;;;OAIG;IACH,aAFa,MAAM,CAIlB;IAQG,yBAAyB;IACzB,UADW,QAAQ,EAAE,CACH;IAElB,uBAAuB;IACvB,QADW,QAAQ,CACI;IAEvB,2CAA2C;IAC3C,mBAAsB;IAG1B;;;OAGG;IACH,cASC;IAED;;;;;;OAMG;IACH,mBAEC;IAED;;;OAGG;IACH,uBAMC;IAgOD;;;OAGG;IACH,4BAHW,KAAK,QAKf;IA7MD;;;OAGG;IACH,kBAFW,QAAQ,QAIlB;IAED;;;OAGG;IACH,gBAFW,QAAQ,QAUlB;IAED;;OAEG;IACH,YAFW,QAAQ,QAQlB;IAED;;OAEG;IACH,6BAFW,QAAQ,QAMlB;IAED;;OAEG;IACH,0BAFW,QAAQ,QAalB;IAED;;;OAGG;IACH,mBAFW,QAAQ,QAWlB;IAED;;OAEG;IACH,eAiBC;IAED,kBAEC;IAED,uBAEC;IAED,sBAEC;IAED;;;;OAIG;IACH,eAFW,CAAC,CAAS,IAAQ,EAAR,QAAQ,KAAE,IAAI,CAAC,GAAG;QAAE,aAAa,CAAC,EAAE,CAAS,IAAQ,EAAR,QAAQ,KAAE,IAAI,CAAA;KAAC,QAchF;IAED;;;OAGG;IACH,wBAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;OAGG;IACH,cAFW,KAAK,QAKf;IAED,iBAMC;IAED;;;;OAIG;IACH,sBAFW,OAAO,uBAAuB,EAAE,SAAS,QAMnD;IAED;;;OAGG;IACH,+BAHa,OAAO,0BAA0B,EAAE,OAAO,CAYtD;IAED;;;;OAIG;IACH,8BAQC;;CASJ;oCAlUY;IAAC,aAAa,EAAE,OAAO,0BAA0B,EAAE,OAAO,CAAA;CAAC;;;;oBAM3D,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;mBACnB,KAAK,EAAE"}
@@ -35,6 +35,9 @@ export default class FlowNode {
35
35
  first: null,
36
36
  };
37
37
 
38
+ /** @type {boolean} */
39
+ #initialized = false;
40
+
38
41
  /**
39
42
  * An object that provides a paramMediator. (Most likely a View)
40
43
  *
@@ -89,11 +92,26 @@ export default class FlowNode {
89
92
  /**
90
93
  * Allows for doing final initialization after the flow structure has been
91
94
  * built and optimized. Must be called before any data are to be propagated.
95
+ * Note: Some transforms call initialize() from reset() to rebuild internal
96
+ * fast paths per batch. Use initializeOnce() for graph-level init to avoid
97
+ * double-initialization issues.
92
98
  */
93
99
  initialize() {
94
100
  // override
95
101
  }
96
102
 
103
+ /**
104
+ * Calls initialize() once per node instance. Intended for graph-level init
105
+ * passes that should not rerun when reusing existing branches.
106
+ */
107
+ initializeOnce() {
108
+ if (this.#initialized) {
109
+ return;
110
+ }
111
+ this.initialize();
112
+ this.#initialized = true;
113
+ }
114
+
97
115
  /**
98
116
  * Dynamically updates the propagator method to allow the JavaScript engine
99
117
  * to employ optimizations such as inlining.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Lazily builds and caches lookup indexes for `encoding.key` fields so that
3
+ * bookmarked point selections can be resolved back to datums efficiently.
4
+ *
5
+ * TODO: Use requestIdleCallback to warm up the index to prevent jank on first access.
6
+ */
7
+ export default class KeyIndex {
8
+ invalidate(): void;
9
+ /**
10
+ * @param {string[]} keyFields
11
+ * @param {import("../spec/channel.js").Scalar[]} keyTuple
12
+ * @param {Iterable<Data>} facetBatches
13
+ * @returns {Datum | undefined}
14
+ */
15
+ findDatum(keyFields: string[], keyTuple: import("../spec/channel.js").Scalar[], facetBatches: Iterable<import("./flowNode.js").Data>): import("./flowNode.js").Datum | undefined;
16
+ #private;
17
+ }
18
+ //# sourceMappingURL=keyIndex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyIndex.d.ts","sourceRoot":"","sources":["../../../src/data/keyIndex.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH;IAeI,mBAIC;IAED;;;;;OAKG;IACH,qBALW,MAAM,EAAE,YACR,OAAO,oBAAoB,EAAE,MAAM,EAAE,gBACrC,QAAQ,8BAAM,GACZ,gCAAQ,SAAS,CAwC7B;;CAgGJ"}
@@ -0,0 +1,241 @@
1
+ import { field } from "../utils/field.js";
2
+
3
+ const MULTI_KEY_SEPARATOR = "|";
4
+ const MULTI_KEY_ESCAPE = "\\";
5
+
6
+ /**
7
+ * Lazily builds and caches lookup indexes for `encoding.key` fields so that
8
+ * bookmarked point selections can be resolved back to datums efficiently.
9
+ *
10
+ * TODO: Use requestIdleCallback to warm up the index to prevent jank on first access.
11
+ */
12
+ export default class KeyIndex {
13
+ /**
14
+ * @typedef {import("./flowNode.js").Datum} Datum
15
+ * @typedef {import("./flowNode.js").Data} Data
16
+ */
17
+
18
+ /** @type {Map<import("../spec/channel.js").Scalar | string, Datum> | null} */
19
+ #index = null;
20
+
21
+ /** @type {string[] | null} */
22
+ #keyFields = null;
23
+
24
+ /** @type {boolean} */
25
+ #usesCompositeKey = false;
26
+
27
+ invalidate() {
28
+ this.#index = null;
29
+ this.#keyFields = null;
30
+ this.#usesCompositeKey = false;
31
+ }
32
+
33
+ /**
34
+ * @param {string[]} keyFields
35
+ * @param {import("../spec/channel.js").Scalar[]} keyTuple
36
+ * @param {Iterable<Data>} facetBatches
37
+ * @returns {Datum | undefined}
38
+ */
39
+ findDatum(keyFields, keyTuple, facetBatches) {
40
+ if (!keyFields || keyFields.length === 0) {
41
+ return;
42
+ }
43
+
44
+ const fieldList = keyFields.join(", ");
45
+ if (keyFields.length !== keyTuple.length) {
46
+ throw new Error(
47
+ `Key tuple length ${keyTuple.length} does not match fields [${fieldList}]`
48
+ );
49
+ }
50
+
51
+ if (!this.#index || !this.#matchesKeyFields(keyFields)) {
52
+ this.#buildIndex(keyFields, facetBatches);
53
+ }
54
+
55
+ const canonicalFields = /** @type {string[]} */ (this.#keyFields);
56
+ /** @type {import("../spec/channel.js").Scalar | string} */
57
+ let key;
58
+ if (!this.#usesCompositeKey) {
59
+ const fieldName = canonicalFields[0];
60
+ key = validateKeyComponent(keyTuple[0], fieldName);
61
+ } else {
62
+ let compositeKey = "";
63
+ for (let i = 0; i < keyTuple.length; i++) {
64
+ if (i > 0) {
65
+ compositeKey += MULTI_KEY_SEPARATOR;
66
+ }
67
+
68
+ const fieldName = canonicalFields[i];
69
+ const value = validateKeyComponent(keyTuple[i], fieldName);
70
+
71
+ compositeKey += encodeKeyPart(value);
72
+ }
73
+ key = compositeKey;
74
+ }
75
+
76
+ return this.#index.get(key);
77
+ }
78
+
79
+ /**
80
+ * @param {string[]} keyFields
81
+ * @param {Iterable<Data>} facetBatches
82
+ */
83
+ #buildIndex(keyFields, facetBatches) {
84
+ /** @type {Array<(datum: Datum) => import("../spec/channel.js").Scalar>} */
85
+ const accessors = keyFields.map((fieldName) => field(fieldName));
86
+ /** @type {Map<import("../spec/channel.js").Scalar | string, Datum>} */
87
+ const index = new Map();
88
+ const fieldList = keyFields.join(", ");
89
+
90
+ const usesCompositeKey = keyFields.length !== 1;
91
+
92
+ if (!usesCompositeKey) {
93
+ const accessor = accessors[0];
94
+ const fieldName = keyFields[0];
95
+ for (const data of facetBatches) {
96
+ for (let i = 0, n = data.length; i < n; i++) {
97
+ const datum = data[i];
98
+ const key = validateKeyComponent(
99
+ accessor(datum),
100
+ fieldName
101
+ );
102
+
103
+ const previous = index.get(key);
104
+ if (previous !== undefined) {
105
+ throw new Error(
106
+ `Duplicate key detected for fields [${fieldList}]: ${JSON.stringify(key)}`
107
+ );
108
+ }
109
+
110
+ index.set(key, datum);
111
+ }
112
+ }
113
+ } else {
114
+ for (const data of facetBatches) {
115
+ for (let i = 0, n = data.length; i < n; i++) {
116
+ const datum = data[i];
117
+ let compositeKey = "";
118
+ for (let j = 0; j < accessors.length; j++) {
119
+ if (j > 0) {
120
+ compositeKey += MULTI_KEY_SEPARATOR;
121
+ }
122
+
123
+ const fieldName = keyFields[j];
124
+ const value = validateKeyComponent(
125
+ accessors[j](datum),
126
+ fieldName
127
+ );
128
+
129
+ compositeKey += encodeKeyPart(value);
130
+ }
131
+
132
+ const previous = index.get(compositeKey);
133
+ if (previous !== undefined) {
134
+ const duplicateTuple = accessors.map((accessor) =>
135
+ accessor(datum)
136
+ );
137
+ throw new Error(
138
+ `Duplicate key detected for fields [${fieldList}]: ${JSON.stringify(duplicateTuple)}`
139
+ );
140
+ }
141
+
142
+ index.set(compositeKey, datum);
143
+ }
144
+ }
145
+ }
146
+
147
+ this.#index = index;
148
+ this.#keyFields = [...keyFields];
149
+ this.#usesCompositeKey = usesCompositeKey;
150
+ }
151
+
152
+ /**
153
+ * @param {string[]} keyFields
154
+ * @returns {boolean}
155
+ */
156
+ #matchesKeyFields(keyFields) {
157
+ if (!this.#keyFields) {
158
+ return false;
159
+ }
160
+
161
+ if (this.#keyFields.length !== keyFields.length) {
162
+ return false;
163
+ }
164
+
165
+ for (let i = 0; i < keyFields.length; i++) {
166
+ if (this.#keyFields[i] !== keyFields[i]) {
167
+ return false;
168
+ }
169
+ }
170
+
171
+ return true;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * @param {unknown} value
177
+ * @param {string} fieldName
178
+ * @returns {import("../spec/channel.js").Scalar}
179
+ */
180
+ function validateKeyComponent(value, fieldName) {
181
+ if (value === undefined) {
182
+ throw new Error(
183
+ `Key field "${fieldName}" is undefined. Ensure all key fields are present in the data.`
184
+ );
185
+ }
186
+
187
+ if (value === null) {
188
+ throw new Error(
189
+ `Key field "${fieldName}" is null. Ensure all key fields are present in the data.`
190
+ );
191
+ }
192
+
193
+ if (
194
+ typeof value !== "string" &&
195
+ typeof value !== "number" &&
196
+ typeof value !== "boolean"
197
+ ) {
198
+ throw new Error(
199
+ `Key field "${fieldName}" must be a scalar value (string, number, or boolean).`
200
+ );
201
+ }
202
+
203
+ return value;
204
+ }
205
+
206
+ /**
207
+ * @param {string} value
208
+ * @returns {string}
209
+ */
210
+ function escapeKeyString(value) {
211
+ const needsEscaping =
212
+ value.indexOf(MULTI_KEY_ESCAPE) !== -1 ||
213
+ value.indexOf(MULTI_KEY_SEPARATOR) !== -1;
214
+
215
+ if (!needsEscaping) {
216
+ return value;
217
+ }
218
+
219
+ let escaped = "";
220
+ for (let i = 0; i < value.length; i++) {
221
+ const char = value[i];
222
+ if (char === MULTI_KEY_ESCAPE || char === MULTI_KEY_SEPARATOR) {
223
+ escaped += MULTI_KEY_ESCAPE;
224
+ }
225
+ escaped += char;
226
+ }
227
+
228
+ return escaped;
229
+ }
230
+
231
+ /**
232
+ * @param {import("../spec/channel.js").Scalar} scalar
233
+ * @returns {string}
234
+ */
235
+ function encodeKeyPart(scalar) {
236
+ if (typeof scalar === "string") {
237
+ return escapeKeyString(scalar);
238
+ }
239
+
240
+ return String(scalar);
241
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=keyIndex.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyIndex.test.d.ts","sourceRoot":"","sources":["../../../src/data/keyIndex.test.js"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"dataSource.d.ts","sourceRoot":"","sources":["../../../../src/data/sources/dataSource.js"],"names":[],"mappings":"AAEA;IAMI;;OAEG;IACH,kBAFW,OAAO,oBAAoB,EAAE,OAAO,EAM9C;IAZD;;OAEG;IACH,MAFU,OAAO,oBAAoB,EAAE,OAAO,CAEzC;IAWL;;;;;OAKG;IACH,kBAFY,MAAM,CAIjB;IAED;;;;;;OAMG;IACH,mCAJW,OAAO,4BAA4B,EAAE,iBAAiB,WACtD,MAAM,QAKhB;IAcD,sBAEC;CAKJ;qBAzDoB,gBAAgB"}
1
+ {"version":3,"file":"dataSource.d.ts","sourceRoot":"","sources":["../../../../src/data/sources/dataSource.js"],"names":[],"mappings":"AAEA;IAMI;;OAEG;IACH,kBAFW,OAAO,oBAAoB,EAAE,OAAO,EAM9C;IAZD;;OAEG;IACH,MAFU,OAAO,oBAAoB,EAAE,OAAO,CAEzC;IAWL;;;;;OAKG;IACH,kBAFY,MAAM,CAIjB;IAED;;;;;;OAMG;IACH,mCAJW,OAAO,4BAA4B,EAAE,iBAAiB,WACtD,MAAM,QAShB;IAcD,sBAEC;CAKJ;qBA7DoB,gBAAgB"}