@uwdata/mosaic-inputs 0.9.0 → 0.10.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.
@@ -123,7 +123,6 @@ var MosaicClient = class {
123
123
  * @returns {this}
124
124
  */
125
125
  queryError(error) {
126
- console.error(error);
127
126
  return this;
128
127
  }
129
128
  /**
@@ -147,7 +146,7 @@ var MosaicClient = class {
147
146
  /**
148
147
  * Requests a client update.
149
148
  * For example to (re-)render an interface component.
150
- *
149
+ *
151
150
  * @returns {this | Promise<any>}
152
151
  */
153
152
  update() {
@@ -211,16 +210,24 @@ function __await(v) {
211
210
  function __asyncGenerator(thisArg, _arguments, generator) {
212
211
  if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
213
212
  var g = generator.apply(thisArg, _arguments || []), i, q = [];
214
- return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
213
+ return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
215
214
  return this;
216
215
  }, i;
217
- function verb(n) {
218
- if (g[n]) i[n] = function(v) {
219
- return new Promise(function(a, b) {
220
- q.push([n, v, a, b]) > 1 || resume(n, v);
221
- });
216
+ function awaitReturn(f) {
217
+ return function(v) {
218
+ return Promise.resolve(v).then(f, reject);
222
219
  };
223
220
  }
221
+ function verb(n, f) {
222
+ if (g[n]) {
223
+ i[n] = function(v) {
224
+ return new Promise(function(a, b) {
225
+ q.push([n, v, a, b]) > 1 || resume(n, v);
226
+ });
227
+ };
228
+ if (f) i[n] = f(i[n]);
229
+ }
230
+ }
224
231
  function resume(n, v) {
225
232
  try {
226
233
  step(g[n](v));
@@ -840,26 +847,26 @@ var IntervalUnit;
840
847
  IntervalUnit2[IntervalUnit2["MONTH_DAY_NANO"] = 2] = "MONTH_DAY_NANO";
841
848
  })(IntervalUnit || (IntervalUnit = {}));
842
849
 
843
- // ../../node_modules/flatbuffers/mjs/constants.js
850
+ // ../../node_modules/apache-arrow/node_modules/flatbuffers/mjs/constants.js
844
851
  var SIZEOF_SHORT = 2;
845
852
  var SIZEOF_INT = 4;
846
853
  var FILE_IDENTIFIER_LENGTH = 4;
847
854
  var SIZE_PREFIX_LENGTH = 4;
848
855
 
849
- // ../../node_modules/flatbuffers/mjs/utils.js
856
+ // ../../node_modules/apache-arrow/node_modules/flatbuffers/mjs/utils.js
850
857
  var int32 = new Int32Array(2);
851
858
  var float32 = new Float32Array(int32.buffer);
852
859
  var float64 = new Float64Array(int32.buffer);
853
860
  var isLittleEndian = new Uint16Array(new Uint8Array([1, 0]).buffer)[0] === 1;
854
861
 
855
- // ../../node_modules/flatbuffers/mjs/encoding.js
862
+ // ../../node_modules/apache-arrow/node_modules/flatbuffers/mjs/encoding.js
856
863
  var Encoding;
857
864
  (function(Encoding2) {
858
865
  Encoding2[Encoding2["UTF8_BYTES"] = 1] = "UTF8_BYTES";
859
866
  Encoding2[Encoding2["UTF16_STRING"] = 2] = "UTF16_STRING";
860
867
  })(Encoding || (Encoding = {}));
861
868
 
862
- // ../../node_modules/flatbuffers/mjs/byte-buffer.js
869
+ // ../../node_modules/apache-arrow/node_modules/flatbuffers/mjs/byte-buffer.js
863
870
  var ByteBuffer = class _ByteBuffer {
864
871
  /**
865
872
  * Create a new ByteBuffer with a given array of bytes (`Uint8Array`)
@@ -1103,7 +1110,7 @@ var ByteBuffer = class _ByteBuffer {
1103
1110
  }
1104
1111
  };
1105
1112
 
1106
- // ../../node_modules/flatbuffers/mjs/builder.js
1113
+ // ../../node_modules/apache-arrow/node_modules/flatbuffers/mjs/builder.js
1107
1114
  var Builder = class _Builder {
1108
1115
  /**
1109
1116
  * Create a FlatBufferBuilder.
@@ -1543,9 +1550,22 @@ var Builder = class _Builder {
1543
1550
  this.addInt8(0);
1544
1551
  this.startVector(1, utf8.length, 1);
1545
1552
  this.bb.setPosition(this.space -= utf8.length);
1546
- for (let i = 0, offset = this.space, bytes = this.bb.bytes(); i < utf8.length; i++) {
1547
- bytes[offset++] = utf8[i];
1553
+ this.bb.bytes().set(utf8, this.space);
1554
+ return this.endVector();
1555
+ }
1556
+ /**
1557
+ * Create a byte vector.
1558
+ *
1559
+ * @param v The bytes to add
1560
+ * @returns The offset in the buffer where the byte vector starts
1561
+ */
1562
+ createByteVector(v) {
1563
+ if (v === null || v === void 0) {
1564
+ return 0;
1548
1565
  }
1566
+ this.startVector(1, v.length, 1);
1567
+ this.bb.setPosition(this.space -= v.length);
1568
+ this.bb.bytes().set(v, this.space);
1549
1569
  return this.endVector();
1550
1570
  }
1551
1571
  /**
@@ -3148,9 +3168,9 @@ var BufferType;
3148
3168
  // ../../node_modules/apache-arrow/util/vector.mjs
3149
3169
  var vector_exports = {};
3150
3170
  __export(vector_exports, {
3151
- clampIndex: () => clampIndex,
3152
3171
  clampRange: () => clampRange,
3153
- createElementComparator: () => createElementComparator
3172
+ createElementComparator: () => createElementComparator,
3173
+ wrapIndex: () => wrapIndex
3154
3174
  });
3155
3175
 
3156
3176
  // ../../node_modules/apache-arrow/util/pretty.mjs
@@ -3191,9 +3211,23 @@ var bn_exports = {};
3191
3211
  __export(bn_exports, {
3192
3212
  BN: () => BN,
3193
3213
  bigNumToBigInt: () => bigNumToBigInt,
3214
+ bigNumToNumber: () => bigNumToNumber,
3194
3215
  bigNumToString: () => bigNumToString,
3195
3216
  isArrowBigNumSymbol: () => isArrowBigNumSymbol
3196
3217
  });
3218
+
3219
+ // ../../node_modules/apache-arrow/util/bigint.mjs
3220
+ function bigIntToNumber(number) {
3221
+ if (typeof number === "bigint" && (number < Number.MIN_SAFE_INTEGER || number > Number.MAX_SAFE_INTEGER)) {
3222
+ throw new TypeError(`${number} is not safe to convert to a number.`);
3223
+ }
3224
+ return Number(number);
3225
+ }
3226
+ function divideBigInts(number, divisor) {
3227
+ return bigIntToNumber(number / divisor) + bigIntToNumber(number % divisor) / bigIntToNumber(divisor);
3228
+ }
3229
+
3230
+ // ../../node_modules/apache-arrow/util/bn.mjs
3197
3231
  var isArrowBigNumSymbol = Symbol.for("isArrowBigNum");
3198
3232
  function BigNum(x2, ...xs) {
3199
3233
  if (xs.length === 0) {
@@ -3205,8 +3239,8 @@ BigNum.prototype[isArrowBigNumSymbol] = true;
3205
3239
  BigNum.prototype.toJSON = function() {
3206
3240
  return `"${bigNumToString(this)}"`;
3207
3241
  };
3208
- BigNum.prototype.valueOf = function() {
3209
- return bigNumToNumber(this);
3242
+ BigNum.prototype.valueOf = function(scale) {
3243
+ return bigNumToNumber(this, scale);
3210
3244
  };
3211
3245
  BigNum.prototype.toString = function() {
3212
3246
  return bigNumToString(this);
@@ -3237,25 +3271,34 @@ Object.setPrototypeOf(DecimalBigNum.prototype, Object.create(Uint32Array.prototy
3237
3271
  Object.assign(SignedBigNum.prototype, BigNum.prototype, { "constructor": SignedBigNum, "signed": true, "TypedArray": Int32Array, "BigIntArray": BigInt64Array });
3238
3272
  Object.assign(UnsignedBigNum.prototype, BigNum.prototype, { "constructor": UnsignedBigNum, "signed": false, "TypedArray": Uint32Array, "BigIntArray": BigUint64Array });
3239
3273
  Object.assign(DecimalBigNum.prototype, BigNum.prototype, { "constructor": DecimalBigNum, "signed": true, "TypedArray": Uint32Array, "BigIntArray": BigUint64Array });
3240
- function bigNumToNumber(bn) {
3241
- const { buffer, byteOffset, length: length2, "signed": signed } = bn;
3242
- const words = new BigUint64Array(buffer, byteOffset, length2);
3274
+ var TWO_TO_THE_64 = BigInt(4294967296) * BigInt(4294967296);
3275
+ var TWO_TO_THE_64_MINUS_1 = TWO_TO_THE_64 - BigInt(1);
3276
+ function bigNumToNumber(bn, scale) {
3277
+ const { buffer, byteOffset, byteLength, "signed": signed } = bn;
3278
+ const words = new BigUint64Array(buffer, byteOffset, byteLength / 8);
3243
3279
  const negative = signed && words.at(-1) & BigInt(1) << BigInt(63);
3244
- let number = negative ? BigInt(1) : BigInt(0);
3245
- let i = BigInt(0);
3246
- if (!negative) {
3280
+ let number = BigInt(0);
3281
+ let i = 0;
3282
+ if (negative) {
3247
3283
  for (const word of words) {
3248
- number += word * (BigInt(1) << BigInt(32) * i++);
3284
+ number |= (word ^ TWO_TO_THE_64_MINUS_1) * (BigInt(1) << BigInt(64 * i++));
3249
3285
  }
3286
+ number *= BigInt(-1);
3287
+ number -= BigInt(1);
3250
3288
  } else {
3251
3289
  for (const word of words) {
3252
- number += ~word * (BigInt(1) << BigInt(32) * i++);
3290
+ number |= word * (BigInt(1) << BigInt(64 * i++));
3253
3291
  }
3254
- number *= BigInt(-1);
3255
3292
  }
3256
- return number;
3293
+ if (typeof scale === "number") {
3294
+ const denominator = BigInt(Math.pow(10, scale));
3295
+ const quotient = number / denominator;
3296
+ const remainder = number % denominator;
3297
+ return bigIntToNumber(quotient) + bigIntToNumber(remainder) / bigIntToNumber(denominator);
3298
+ }
3299
+ return bigIntToNumber(number);
3257
3300
  }
3258
- var bigNumToString = (a) => {
3301
+ function bigNumToString(a) {
3259
3302
  if (a.byteLength === 8) {
3260
3303
  const bigIntArray = new a["BigIntArray"](a.buffer, a.byteOffset, 1);
3261
3304
  return `${bigIntArray[0]}`;
@@ -3278,15 +3321,15 @@ var bigNumToString = (a) => {
3278
3321
  }
3279
3322
  const negated = unsignedBigNumToString(array);
3280
3323
  return `-${negated}`;
3281
- };
3282
- var bigNumToBigInt = (a) => {
3324
+ }
3325
+ function bigNumToBigInt(a) {
3283
3326
  if (a.byteLength === 8) {
3284
3327
  const bigIntArray = new a["BigIntArray"](a.buffer, a.byteOffset, 1);
3285
3328
  return bigIntArray[0];
3286
3329
  } else {
3287
3330
  return bigNumToString(a);
3288
3331
  }
3289
- };
3332
+ }
3290
3333
  function unsignedBigNumToString(a) {
3291
3334
  let digits = "";
3292
3335
  const base64 = new Uint32Array(2);
@@ -3343,14 +3386,6 @@ var BN = class _BN {
3343
3386
  }
3344
3387
  };
3345
3388
 
3346
- // ../../node_modules/apache-arrow/util/bigint.mjs
3347
- function bigIntToNumber(number) {
3348
- if (typeof number === "bigint" && (number < Number.MIN_SAFE_INTEGER || number > Number.MAX_SAFE_INTEGER)) {
3349
- throw new TypeError(`${number} is not safe to convert to a number.`);
3350
- }
3351
- return Number(number);
3352
- }
3353
-
3354
3389
  // ../../node_modules/apache-arrow/type.mjs
3355
3390
  var _a;
3356
3391
  var _b;
@@ -3725,11 +3760,13 @@ var Date_ = class extends DataType {
3725
3760
  toString() {
3726
3761
  return `Date${(this.unit + 1) * 32}<${DateUnit[this.unit]}>`;
3727
3762
  }
3763
+ get ArrayType() {
3764
+ return this.unit === DateUnit.DAY ? Int32Array : BigInt64Array;
3765
+ }
3728
3766
  };
3729
3767
  _l = Symbol.toStringTag;
3730
3768
  Date_[_l] = ((proto) => {
3731
3769
  proto.unit = null;
3732
- proto.ArrayType = Int32Array;
3733
3770
  return proto[Symbol.toStringTag] = "Date";
3734
3771
  })(Date_.prototype);
3735
3772
  var Time_ = class extends DataType {
@@ -3771,7 +3808,7 @@ _o = Symbol.toStringTag;
3771
3808
  Timestamp_[_o] = ((proto) => {
3772
3809
  proto.unit = null;
3773
3810
  proto.timezone = null;
3774
- proto.ArrayType = Int32Array;
3811
+ proto.ArrayType = BigInt64Array;
3775
3812
  return proto[Symbol.toStringTag] = "Timestamp";
3776
3813
  })(Timestamp_.prototype);
3777
3814
  var Interval_ = class extends DataType {
@@ -3976,10 +4013,6 @@ function strideForType(type) {
3976
4013
  switch (type.typeId) {
3977
4014
  case Type2.Decimal:
3978
4015
  return type.bitWidth / 32;
3979
- case Type2.Timestamp:
3980
- return 2;
3981
- case Type2.Date:
3982
- return 1 + t.unit;
3983
4016
  case Type2.Interval:
3984
4017
  return 1 + t.unit;
3985
4018
  case Type2.FixedSizeList:
@@ -4446,19 +4479,7 @@ function wrapSet(fn) {
4446
4479
  };
4447
4480
  }
4448
4481
  var setEpochMsToDays = (data, index, epochMs) => {
4449
- data[index] = Math.trunc(epochMs / 864e5);
4450
- };
4451
- var setEpochMsToMillisecondsLong = (data, index, epochMs) => {
4452
- data[index] = Math.trunc(epochMs % 4294967296);
4453
- data[index + 1] = Math.trunc(epochMs / 4294967296);
4454
- };
4455
- var setEpochMsToMicrosecondsLong = (data, index, epochMs) => {
4456
- data[index] = Math.trunc(epochMs * 1e3 % 4294967296);
4457
- data[index + 1] = Math.trunc(epochMs * 1e3 / 4294967296);
4458
- };
4459
- var setEpochMsToNanosecondsLong = (data, index, epochMs) => {
4460
- data[index] = Math.trunc(epochMs * 1e6 % 4294967296);
4461
- data[index + 1] = Math.trunc(epochMs * 1e6 / 4294967296);
4482
+ data[index] = Math.floor(epochMs / 864e5);
4462
4483
  };
4463
4484
  var setVariableWidthBytes = (values, valueOffsets, index, value) => {
4464
4485
  if (index + 1 < valueOffsets.length) {
@@ -4493,7 +4514,7 @@ var setDateDay = ({ values }, index, value) => {
4493
4514
  setEpochMsToDays(values, index, value.valueOf());
4494
4515
  };
4495
4516
  var setDateMillisecond = ({ values }, index, value) => {
4496
- setEpochMsToMillisecondsLong(values, index * 2, value.valueOf());
4517
+ values[index] = BigInt(value);
4497
4518
  };
4498
4519
  var setFixedSizeBinary = ({ stride, values }, index, value) => {
4499
4520
  values.set(value.subarray(0, stride), stride * index);
@@ -4503,10 +4524,18 @@ var setUtf8 = ({ values, valueOffsets }, index, value) => setVariableWidthBytes(
4503
4524
  var setDate = (data, index, value) => {
4504
4525
  data.type.unit === DateUnit.DAY ? setDateDay(data, index, value) : setDateMillisecond(data, index, value);
4505
4526
  };
4506
- var setTimestampSecond = ({ values }, index, value) => setEpochMsToMillisecondsLong(values, index * 2, value / 1e3);
4507
- var setTimestampMillisecond = ({ values }, index, value) => setEpochMsToMillisecondsLong(values, index * 2, value);
4508
- var setTimestampMicrosecond = ({ values }, index, value) => setEpochMsToMicrosecondsLong(values, index * 2, value);
4509
- var setTimestampNanosecond = ({ values }, index, value) => setEpochMsToNanosecondsLong(values, index * 2, value);
4527
+ var setTimestampSecond = ({ values }, index, value) => {
4528
+ values[index] = BigInt(value / 1e3);
4529
+ };
4530
+ var setTimestampMillisecond = ({ values }, index, value) => {
4531
+ values[index] = BigInt(value);
4532
+ };
4533
+ var setTimestampMicrosecond = ({ values }, index, value) => {
4534
+ values[index] = BigInt(value * 1e3);
4535
+ };
4536
+ var setTimestampNanosecond = ({ values }, index, value) => {
4537
+ values[index] = BigInt(value * 1e6);
4538
+ };
4510
4539
  var setTimestamp = (data, index, value) => {
4511
4540
  switch (data.type.unit) {
4512
4541
  case TimeUnit.SECOND:
@@ -4811,12 +4840,6 @@ function wrapGet(fn) {
4811
4840
  return (data, _1) => data.getValid(_1) ? fn(data, _1) : null;
4812
4841
  }
4813
4842
  var epochDaysToMs = (data, index) => 864e5 * data[index];
4814
- var epochMillisecondsLongToMs = (data, index) => 4294967296 * data[index + 1] + (data[index] >>> 0);
4815
- var epochMicrosecondsLongToMs = (data, index) => 4294967296 * (data[index + 1] / 1e3) + (data[index] >>> 0) / 1e3;
4816
- var epochNanosecondsLongToMs = (data, index) => 4294967296 * (data[index + 1] / 1e6) + (data[index] >>> 0) / 1e6;
4817
- var epochMillisecondsToDate = (epochMs) => new Date(epochMs);
4818
- var epochDaysToDate = (data, index) => epochMillisecondsToDate(epochDaysToMs(data, index));
4819
- var epochMillisecondsLongToDate = (data, index) => epochMillisecondsToDate(epochMillisecondsLongToMs(data, index));
4820
4843
  var getNull = (_data, _index) => null;
4821
4844
  var getVariableWidthBytes = (values, valueOffsets, index) => {
4822
4845
  if (index + 1 >= valueOffsets.length) {
@@ -4831,8 +4854,8 @@ var getBool = ({ offset, values }, index) => {
4831
4854
  const byte = values[idx >> 3];
4832
4855
  return (byte & 1 << idx % 8) !== 0;
4833
4856
  };
4834
- var getDateDay = ({ values }, index) => epochDaysToDate(values, index);
4835
- var getDateMillisecond = ({ values }, index) => epochMillisecondsLongToDate(values, index * 2);
4857
+ var getDateDay = ({ values }, index) => epochDaysToMs(values, index);
4858
+ var getDateMillisecond = ({ values }, index) => bigIntToNumber(values[index]);
4836
4859
  var getNumeric = ({ stride, values }, index) => values[stride * index];
4837
4860
  var getFloat16 = ({ stride, values }, index) => uint16ToFloat64(values[stride * index]);
4838
4861
  var getBigInts = ({ values }, index) => values[index];
@@ -4845,10 +4868,10 @@ var getUtf8 = ({ values, valueOffsets }, index) => {
4845
4868
  var getInt = ({ values }, index) => values[index];
4846
4869
  var getFloat = ({ type, values }, index) => type.precision !== Precision.HALF ? values[index] : uint16ToFloat64(values[index]);
4847
4870
  var getDate = (data, index) => data.type.unit === DateUnit.DAY ? getDateDay(data, index) : getDateMillisecond(data, index);
4848
- var getTimestampSecond = ({ values }, index) => 1e3 * epochMillisecondsLongToMs(values, index * 2);
4849
- var getTimestampMillisecond = ({ values }, index) => epochMillisecondsLongToMs(values, index * 2);
4850
- var getTimestampMicrosecond = ({ values }, index) => epochMicrosecondsLongToMs(values, index * 2);
4851
- var getTimestampNanosecond = ({ values }, index) => epochNanosecondsLongToMs(values, index * 2);
4871
+ var getTimestampSecond = ({ values }, index) => 1e3 * bigIntToNumber(values[index]);
4872
+ var getTimestampMillisecond = ({ values }, index) => bigIntToNumber(values[index]);
4873
+ var getTimestampMicrosecond = ({ values }, index) => divideBigInts(values[index], BigInt(1e3));
4874
+ var getTimestampNanosecond = ({ values }, index) => divideBigInts(values[index], BigInt(1e6));
4852
4875
  var getTimestamp = (data, index) => {
4853
4876
  switch (data.type.unit) {
4854
4877
  case TimeUnit.SECOND:
@@ -4914,10 +4937,10 @@ var getDictionary = (data, index) => {
4914
4937
  var getInterval = (data, index) => data.type.unit === IntervalUnit.DAY_TIME ? getIntervalDayTime(data, index) : getIntervalYearMonth(data, index);
4915
4938
  var getIntervalDayTime = ({ values }, index) => values.subarray(2 * index, 2 * (index + 1));
4916
4939
  var getIntervalYearMonth = ({ values }, index) => {
4917
- const interval2 = values[index];
4940
+ const interval = values[index];
4918
4941
  const int32s = new Int32Array(2);
4919
- int32s[0] = Math.trunc(interval2 / 12);
4920
- int32s[1] = Math.trunc(interval2 % 12);
4942
+ int32s[0] = Math.trunc(interval / 12);
4943
+ int32s[1] = Math.trunc(interval % 12);
4921
4944
  return int32s;
4922
4945
  };
4923
4946
  var getDurationSecond = ({ values }, index) => values[index];
@@ -4997,12 +5020,18 @@ var instance2 = new GetVisitor();
4997
5020
  // ../../node_modules/apache-arrow/row/map.mjs
4998
5021
  var kKeys = Symbol.for("keys");
4999
5022
  var kVals = Symbol.for("vals");
5023
+ var kKeysAsStrings = Symbol.for("kKeysAsStrings");
5024
+ var _kKeysAsStrings = Symbol.for("_kKeysAsStrings");
5000
5025
  var MapRow = class {
5001
5026
  constructor(slice) {
5002
5027
  this[kKeys] = new Vector([slice.children[0]]).memoize();
5003
5028
  this[kVals] = slice.children[1];
5004
5029
  return new Proxy(this, new MapRowProxyHandler());
5005
5030
  }
5031
+ /** @ignore */
5032
+ get [kKeysAsStrings]() {
5033
+ return this[_kKeysAsStrings] || (this[_kKeysAsStrings] = Array.from(this[kKeys].toArray(), String));
5034
+ }
5006
5035
  [Symbol.iterator]() {
5007
5036
  return new MapRowIterator(this[kKeys], this[kVals]);
5008
5037
  }
@@ -5064,13 +5093,13 @@ var MapRowProxyHandler = class {
5064
5093
  return true;
5065
5094
  }
5066
5095
  ownKeys(row) {
5067
- return row[kKeys].toArray().map(String);
5096
+ return row[kKeysAsStrings];
5068
5097
  }
5069
5098
  has(row, key) {
5070
- return row[kKeys].includes(key);
5099
+ return row[kKeysAsStrings].includes(key);
5071
5100
  }
5072
5101
  getOwnPropertyDescriptor(row, key) {
5073
- const idx = row[kKeys].indexOf(key);
5102
+ const idx = row[kKeysAsStrings].indexOf(key);
5074
5103
  if (idx !== -1) {
5075
5104
  return { writable: true, enumerable: true, configurable: true };
5076
5105
  }
@@ -5080,7 +5109,7 @@ var MapRowProxyHandler = class {
5080
5109
  if (Reflect.has(row, key)) {
5081
5110
  return row[key];
5082
5111
  }
5083
- const idx = row[kKeys].indexOf(key);
5112
+ const idx = row[kKeysAsStrings].indexOf(key);
5084
5113
  if (idx !== -1) {
5085
5114
  const val = instance2.visit(Reflect.get(row, kVals), idx);
5086
5115
  Reflect.set(row, key, val);
@@ -5088,7 +5117,7 @@ var MapRowProxyHandler = class {
5088
5117
  }
5089
5118
  }
5090
5119
  set(row, key, val) {
5091
- const idx = row[kKeys].indexOf(key);
5120
+ const idx = row[kKeysAsStrings].indexOf(key);
5092
5121
  if (idx !== -1) {
5093
5122
  instance.visit(Reflect.get(row, kVals), idx, val);
5094
5123
  return Reflect.set(row, key, val);
@@ -5101,15 +5130,11 @@ var MapRowProxyHandler = class {
5101
5130
  Object.defineProperties(MapRow.prototype, {
5102
5131
  [Symbol.toStringTag]: { enumerable: false, configurable: false, value: "Row" },
5103
5132
  [kKeys]: { writable: true, enumerable: false, configurable: false, value: null },
5104
- [kVals]: { writable: true, enumerable: false, configurable: false, value: null }
5133
+ [kVals]: { writable: true, enumerable: false, configurable: false, value: null },
5134
+ [_kKeysAsStrings]: { writable: true, enumerable: false, configurable: false, value: null }
5105
5135
  });
5106
5136
 
5107
5137
  // ../../node_modules/apache-arrow/util/vector.mjs
5108
- function clampIndex(source, index, then) {
5109
- const length2 = source.length;
5110
- const adjust = index > -1 ? index : length2 + index % length2;
5111
- return then ? then(source, adjust) : adjust;
5112
- }
5113
5138
  var tmp;
5114
5139
  function clampRange(source, begin, end, then) {
5115
5140
  const { length: len = 0 } = source;
@@ -5121,6 +5146,7 @@ function clampRange(source, begin, end, then) {
5121
5146
  rhs > len && (rhs = len);
5122
5147
  return then ? then(source, lhs, rhs) : [lhs, rhs];
5123
5148
  }
5149
+ var wrapIndex = (index, len) => index < 0 ? len + index : index;
5124
5150
  var isNaNFast = (value) => value !== value;
5125
5151
  function createElementComparator(search2) {
5126
5152
  const typeofSearch = typeof search2;
@@ -5409,7 +5435,10 @@ var Data = class _Data {
5409
5435
  let nullCount = this._nullCount;
5410
5436
  let nullBitmap;
5411
5437
  if (nullCount <= kUnknownNullCount && (nullBitmap = this.nullBitmap)) {
5412
- this._nullCount = nullCount = this.length - popcnt_bit_range(nullBitmap, this.offset, this.offset + this.length);
5438
+ this._nullCount = nullCount = nullBitmap.length === 0 ? (
5439
+ // no null bitmap, so all values are valid
5440
+ 0
5441
+ ) : this.length - popcnt_bit_range(nullBitmap, this.offset, this.offset + this.length);
5413
5442
  }
5414
5443
  return nullCount;
5415
5444
  }
@@ -5471,12 +5500,14 @@ var Data = class _Data {
5471
5500
  nullBitmap = new Uint8Array((offset + length2 + 63 & ~63) >> 3).fill(255);
5472
5501
  if (this.nullCount > 0) {
5473
5502
  nullBitmap.set(truncateBitmap(offset, length2, this.nullBitmap), 0);
5503
+ Object.assign(this, { nullBitmap });
5504
+ } else {
5505
+ Object.assign(this, { nullBitmap, _nullCount: 0 });
5474
5506
  }
5475
- Object.assign(this, { nullBitmap, _nullCount: -1 });
5476
5507
  }
5477
5508
  const byte = nullBitmap[byteOffset];
5478
5509
  prev = (byte & mask) !== 0;
5479
- value ? nullBitmap[byteOffset] = byte | mask : nullBitmap[byteOffset] = byte & ~mask;
5510
+ nullBitmap[byteOffset] = value ? byte | mask : byte & ~mask;
5480
5511
  }
5481
5512
  if (prev !== !!value) {
5482
5513
  this._nullCount = this.nullCount + (value ? -1 : 1);
@@ -5917,7 +5948,9 @@ var IteratorVisitor = class extends Visitor {
5917
5948
  };
5918
5949
  function vectorIterator(vector) {
5919
5950
  const { type } = vector;
5920
- if (vector.nullCount === 0 && vector.stride === 1 && (type.typeId === Type2.Timestamp || type instanceof Int_ && type.bitWidth !== 64 || type instanceof Time_ && type.bitWidth !== 64 || type instanceof Float && type.precision !== Precision.HALF)) {
5951
+ if (vector.nullCount === 0 && vector.stride === 1 && // Don't defer to native iterator for timestamps since Numbers are expected
5952
+ // (DataType.isTimestamp(type)) && type.unit === TimeUnit.MILLISECOND ||
5953
+ (DataType.isInt(type) && type.bitWidth !== 64 || DataType.isTime(type) && type.bitWidth !== 64 || DataType.isFloat(type) && type.precision !== Precision.HALF)) {
5921
5954
  return new ChunkedIterator(vector.data.length, (chunkIndex) => {
5922
5955
  const data = vector.data[chunkIndex];
5923
5956
  return data.values.subarray(0, data.length)[Symbol.iterator]();
@@ -6091,6 +6124,13 @@ var Vector = class _Vector {
6091
6124
  get(index) {
6092
6125
  return null;
6093
6126
  }
6127
+ /**
6128
+ * Get an element value by position.
6129
+ * @param index The index of the element to read. A negative index will count back from the last element.
6130
+ */
6131
+ at(index) {
6132
+ return this.get(wrapIndex(index, this.length));
6133
+ }
6094
6134
  /**
6095
6135
  * Set an element value by position.
6096
6136
  * @param index The index of the element to write.
@@ -7368,8 +7408,8 @@ var AsyncByteStreamSource = class {
7368
7408
  return (yield this.next(size, "peek")).value;
7369
7409
  });
7370
7410
  }
7371
- next(size, cmd = "read") {
7372
- return __awaiter(this, void 0, void 0, function* () {
7411
+ next(size_1) {
7412
+ return __awaiter(this, arguments, void 0, function* (size, cmd = "read") {
7373
7413
  return yield this.source.next({ cmd, size });
7374
7414
  });
7375
7415
  }
@@ -8946,6 +8986,14 @@ var Table = class _Table {
8946
8986
  get(index) {
8947
8987
  return null;
8948
8988
  }
8989
+ /**
8990
+ * Get an element value by position.
8991
+ * @param index The index of the element to read. A negative index will count back from the last element.
8992
+ */
8993
+ // @ts-ignore
8994
+ at(index) {
8995
+ return this.get(wrapIndex(index, this.numRows));
8996
+ }
8949
8997
  /**
8950
8998
  * Set an element value by position.
8951
8999
  *
@@ -9183,7 +9231,7 @@ var RecordBatch2 = class _RecordBatch {
9183
9231
  return this.data.nullCount;
9184
9232
  }
9185
9233
  /**
9186
- * Check whether an element is null.
9234
+ * Check whether an row is null.
9187
9235
  * @param index The index at which to read the validity bitmap.
9188
9236
  */
9189
9237
  isValid(index) {
@@ -9191,14 +9239,21 @@ var RecordBatch2 = class _RecordBatch {
9191
9239
  }
9192
9240
  /**
9193
9241
  * Get a row by position.
9194
- * @param index The index of the element to read.
9242
+ * @param index The index of the row to read.
9195
9243
  */
9196
9244
  get(index) {
9197
9245
  return instance2.visit(this.data, index);
9198
9246
  }
9247
+ /**
9248
+ * Get a row value by position.
9249
+ * @param index The index of the row to read. A negative index will count back from the last row.
9250
+ */
9251
+ at(index) {
9252
+ return this.get(wrapIndex(index, this.numRows));
9253
+ }
9199
9254
  /**
9200
9255
  * Set a row by position.
9201
- * @param index The index of the element to write.
9256
+ * @param index The index of the row to write.
9202
9257
  * @param value The value to set.
9203
9258
  */
9204
9259
  set(index, value) {
@@ -9235,7 +9290,7 @@ var RecordBatch2 = class _RecordBatch {
9235
9290
  /**
9236
9291
  * Return a zero-copy sub-section of this RecordBatch.
9237
9292
  * @param start The beginning of the specified portion of the RecordBatch.
9238
- * @param end The end of the specified portion of the RecordBatch. This is exclusive of the element at the index 'end'.
9293
+ * @param end The end of the specified portion of the RecordBatch. This is exclusive of the row at the index 'end'.
9239
9294
  */
9240
9295
  slice(begin, end) {
9241
9296
  const [slice] = new Vector([this.data]).slice(begin, end).data;
@@ -10323,8 +10378,8 @@ var AsyncMessageReader = class {
10323
10378
  );
10324
10379
  });
10325
10380
  }
10326
- readSchema(throwIfNull = false) {
10327
- return __awaiter(this, void 0, void 0, function* () {
10381
+ readSchema() {
10382
+ return __awaiter(this, arguments, void 0, function* (throwIfNull = false) {
10328
10383
  const type = MessageHeader.Schema;
10329
10384
  const message = yield this.readMessage(type);
10330
10385
  const schema = message === null || message === void 0 ? void 0 : message.header();
@@ -10572,8 +10627,8 @@ var AsyncRecordBatchStreamReader = class extends RecordBatchReader {
10572
10627
  this._impl = _impl;
10573
10628
  }
10574
10629
  readAll() {
10575
- var _a5, e_1, _b2, _c2;
10576
10630
  return __awaiter(this, void 0, void 0, function* () {
10631
+ var _a5, e_1, _b2, _c2;
10577
10632
  const batches = new Array();
10578
10633
  try {
10579
10634
  for (var _d2 = true, _e2 = __asyncValues(this), _f2; _f2 = yield _e2.next(), _a5 = _f2.done, !_a5; _d2 = true) {
@@ -10946,8 +11001,8 @@ var AsyncRecordBatchFileReaderImpl = class extends AsyncRecordBatchStreamReaderI
10946
11001
  });
10947
11002
  }
10948
11003
  readRecordBatch(index) {
10949
- var _a5;
10950
11004
  return __awaiter(this, void 0, void 0, function* () {
11005
+ var _a5;
10951
11006
  if (this.closed) {
10952
11007
  return null;
10953
11008
  }
@@ -10968,8 +11023,8 @@ var AsyncRecordBatchFileReaderImpl = class extends AsyncRecordBatchStreamReaderI
10968
11023
  });
10969
11024
  }
10970
11025
  _readDictionaryBatch(index) {
10971
- var _a5;
10972
11026
  return __awaiter(this, void 0, void 0, function* () {
11027
+ var _a5;
10973
11028
  const block = (_a5 = this._footer) === null || _a5 === void 0 ? void 0 : _a5.getDictionaryBatch(index);
10974
11029
  if (block && (yield this._handle.seek(block.offset))) {
10975
11030
  const message = yield this._reader.readMessage(MessageHeader.DictionaryBatch);
@@ -11474,9 +11529,9 @@ function writeAll(writer, input2) {
11474
11529
  return writer.finish();
11475
11530
  }
11476
11531
  function writeAllAsync(writer, batches) {
11477
- var _a5, batches_1, batches_1_1;
11478
- var _b2, e_1, _c2, _d2;
11479
11532
  return __awaiter(this, void 0, void 0, function* () {
11533
+ var _a5, batches_1, batches_1_1;
11534
+ var _b2, e_1, _c2, _d2;
11480
11535
  try {
11481
11536
  for (_a5 = true, batches_1 = __asyncValues(batches); batches_1_1 = yield batches_1.next(), _b2 = batches_1_1.done, !_b2; _a5 = true) {
11482
11537
  _d2 = batches_1_1.value;
@@ -13040,37 +13095,12 @@ function create(name, query, {
13040
13095
  return "CREATE" + (replace ? " OR REPLACE " : " ") + (temp ? "TEMP " : "") + (view ? "VIEW" : "TABLE") + (replace ? " " : " IF NOT EXISTS ") + name + " AS " + query;
13041
13096
  }
13042
13097
 
13043
- // ../core/src/util/hash.js
13044
- function fnv_hash(v) {
13045
- let a = 2166136261;
13046
- for (let i = 0, n = v.length; i < n; ++i) {
13047
- const c = v.charCodeAt(i);
13048
- const d = c & 65280;
13049
- if (d) a = fnv_multiply(a ^ d >> 8);
13050
- a = fnv_multiply(a ^ c & 255);
13051
- }
13052
- return fnv_mix(a);
13053
- }
13054
- function fnv_multiply(a) {
13055
- return a + (a << 1) + (a << 4) + (a << 7) + (a << 8) + (a << 24);
13056
- }
13057
- function fnv_mix(a) {
13058
- a += a << 13;
13059
- a ^= a >>> 7;
13060
- a += a << 3;
13061
- a ^= a >>> 17;
13062
- a += a << 5;
13063
- return a & 4294967295;
13064
- }
13065
-
13066
13098
  // ../core/src/util/index-columns.js
13067
- var NO_INDEX = { from: NaN };
13068
13099
  function indexColumns(client) {
13069
- if (!client.filterIndexable) return NO_INDEX;
13100
+ if (!client.filterIndexable) return null;
13070
13101
  const q = client.query();
13071
13102
  const from = getBaseTable(q);
13072
- if (typeof from !== "string" || !q.groupby) return NO_INDEX;
13073
- const g = new Set(q.groupby().map((c) => c.column));
13103
+ if (typeof from !== "string" || !q.select) return null;
13074
13104
  const aggr = [];
13075
13105
  const dims = [];
13076
13106
  const aux = {};
@@ -13168,10 +13198,11 @@ function indexColumns(client) {
13168
13198
  aggr.push({ [as]: agg`${op}("${as}")` });
13169
13199
  break;
13170
13200
  default:
13171
- if (g.has(as)) dims.push(as);
13201
+ if (!aggregate) dims.push(as);
13172
13202
  else return null;
13173
13203
  }
13174
13204
  }
13205
+ if (!aggr.length) return null;
13175
13206
  return { from, dims, aggr, aux };
13176
13207
  }
13177
13208
  function auxName(type, ...args) {
@@ -13304,125 +13335,153 @@ function regrInterceptExpr(aux, args, from) {
13304
13335
  return agg`${ay} - (${m}) * ${ax}`;
13305
13336
  }
13306
13337
 
13338
+ // ../core/src/util/hash.js
13339
+ function fnv_hash(v) {
13340
+ let a = 2166136261;
13341
+ for (let i = 0, n = v.length; i < n; ++i) {
13342
+ const c = v.charCodeAt(i);
13343
+ const d = c & 65280;
13344
+ if (d) a = fnv_multiply(a ^ d >> 8);
13345
+ a = fnv_multiply(a ^ c & 255);
13346
+ }
13347
+ return fnv_mix(a);
13348
+ }
13349
+ function fnv_multiply(a) {
13350
+ return a + (a << 1) + (a << 4) + (a << 7) + (a << 8) + (a << 24);
13351
+ }
13352
+ function fnv_mix(a) {
13353
+ a += a << 13;
13354
+ a ^= a >>> 7;
13355
+ a += a << 3;
13356
+ a ^= a >>> 17;
13357
+ a += a << 5;
13358
+ return a & 4294967295;
13359
+ }
13360
+
13307
13361
  // ../core/src/DataCubeIndexer.js
13362
+ var Skip = { skip: true, result: null };
13308
13363
  var DataCubeIndexer = class {
13309
13364
  /**
13310
- *
13311
- * @param {import('./Coordinator.js').Coordinator} mc a Mosaic coordinator
13312
- * @param {*} options Options hash to configure the data cube indexes and pass selections to the coordinator.
13365
+ * Create a new data cube index table manager.
13366
+ * @param {import('./Coordinator.js').Coordinator} coordinator A Mosaic coordinator.
13367
+ * @param {object} [options] Indexer options.
13368
+ * @param {boolean} [options.enabled=true] Flag to enable/disable indexer.
13369
+ * @param {boolean} [options.temp=true] Flag to indicate if generated data
13370
+ * cube index tables should be temporary tables.
13313
13371
  */
13314
- constructor(mc, { selection, temp = true }) {
13315
- this.mc = mc;
13316
- this.selection = selection;
13372
+ constructor(coordinator2, {
13373
+ enabled = true,
13374
+ temp = true
13375
+ } = {}) {
13376
+ this.indexes = /* @__PURE__ */ new Map();
13377
+ this.active = null;
13317
13378
  this.temp = temp;
13318
- this.reset();
13379
+ this.mc = coordinator2;
13380
+ this._enabled = enabled;
13319
13381
  }
13320
- reset() {
13321
- this.enabled = false;
13322
- this.clients = null;
13323
- this.indices = null;
13324
- this.active = null;
13382
+ /**
13383
+ * Set the enabled state of this indexer. If false, any cached state is
13384
+ * cleared and subsequent index calls will return null until re-enabled.
13385
+ * @param {boolean} state The enabled state.
13386
+ */
13387
+ enabled(state) {
13388
+ if (state === void 0) {
13389
+ return this._enabled;
13390
+ } else if (this._enabled !== state) {
13391
+ if (!state) this.clear();
13392
+ this._enabled = state;
13393
+ }
13325
13394
  }
13395
+ /**
13396
+ * Clear the cache of data cube index table entries for the current active
13397
+ * selection clause. This method will also cancel any queued data cube table
13398
+ * creation queries that have not yet been submitted to the database. This
13399
+ * method does _not_ drop any existing data cube tables.
13400
+ */
13326
13401
  clear() {
13327
- if (this.indices) {
13328
- this.mc.cancel(Array.from(this.indices.values(), (index) => index?.result));
13329
- this.indices = null;
13330
- }
13331
- }
13332
- index(clients, activeClause) {
13333
- if (this.clients !== clients) {
13334
- const cols = Array.from(clients, indexColumns).filter((x2) => x2);
13335
- const from = cols[0]?.from;
13336
- this.enabled = cols.length && cols.every((c) => c.from === from);
13337
- this.clients = clients;
13338
- this.active = null;
13339
- this.clear();
13340
- }
13341
- if (!this.enabled) return false;
13342
- activeClause = activeClause || this.selection.active;
13402
+ this.mc.cancel(Array.from(this.indexes.values(), (info) => info?.result));
13403
+ this.indexes.clear();
13404
+ this.active = null;
13405
+ }
13406
+ /**
13407
+ * Return data cube index table information for the active state of a
13408
+ * client-selection pair, or null if the client is not indexable. This
13409
+ * method has multiple possible side effects, including data cube table
13410
+ * generation and updating internal caches.
13411
+ * @param {import('./MosaicClient.js').MosaicClient} client A Mosaic client.
13412
+ * @param {import('./Selection.js').Selection} selection A Mosaic selection
13413
+ * to filter the client by.
13414
+ * @param {import('./util/selection-types.js').SelectionClause} activeClause
13415
+ * A representative active selection clause for which to (possibly) generate
13416
+ * data cube index tables.
13417
+ * @returns {DataCubeInfo | Skip | null} Data cube index table
13418
+ * information and query generator, or null if the client is not indexable.
13419
+ */
13420
+ index(client, selection, activeClause) {
13421
+ if (!this._enabled) return null;
13422
+ const { indexes, mc, temp } = this;
13343
13423
  const { source } = activeClause;
13344
- if (source && source === this.active?.source) return true;
13345
- this.clear();
13346
- if (!source) return false;
13347
- const active = this.active = activeColumns(activeClause);
13348
- if (!active) return false;
13349
- const logger = this.mc.logger();
13350
- logger.warn("DATA CUBE INDEX CONSTRUCTION");
13351
- const sel = this.selection.remove(source);
13352
- const indices = this.indices = /* @__PURE__ */ new Map();
13353
- const { mc, temp } = this;
13354
- for (const client of clients) {
13355
- if (sel.skip(client, activeClause)) {
13356
- indices.set(client, null);
13357
- continue;
13358
- }
13359
- const index = indexColumns(client);
13360
- if (!index) {
13361
- continue;
13362
- }
13363
- const query = client.query(sel.predicate(client)).select({ ...active.columns, ...index.aux }).groupby(Object.keys(active.columns));
13364
- const [subq] = query.subqueries;
13365
- if (subq) {
13366
- const cols = Object.values(active.columns).flatMap((c) => c.columns);
13367
- subqueryPushdown(subq, cols);
13368
- }
13369
- const order = query.orderby();
13370
- query.query.orderby = [];
13371
- const sql2 = query.toString();
13372
- const id = (fnv_hash(sql2) >>> 0).toString(16);
13373
- const table2 = `cube_index_${id}`;
13374
- const result = mc.exec(create(table2, sql2, { temp }));
13375
- result.catch((e) => logger.error(e));
13376
- indices.set(client, { table: table2, result, order, ...index });
13424
+ if (!source) return null;
13425
+ if (this.active) {
13426
+ if (this.active.source !== source) this.clear();
13427
+ if (this.active?.source === null) return null;
13428
+ }
13429
+ let { active } = this;
13430
+ if (!active) {
13431
+ this.active = active = activeColumns(activeClause);
13432
+ if (active.source === null) return null;
13433
+ }
13434
+ if (indexes.has(client)) {
13435
+ return indexes.get(client);
13436
+ }
13437
+ const indexCols = indexColumns(client);
13438
+ let info;
13439
+ if (!indexCols) {
13440
+ info = null;
13441
+ } else if (selection.skip(client, activeClause)) {
13442
+ info = Skip;
13443
+ } else {
13444
+ const filter = selection.remove(source).predicate(client);
13445
+ info = dataCubeInfo(client.query(filter), active, indexCols);
13446
+ info.result = mc.exec(create(info.table, info.create, { temp }));
13447
+ info.result.catch((e) => mc.logger().error(e));
13377
13448
  }
13378
- return true;
13379
- }
13380
- async update() {
13381
- const { clients, selection, active } = this;
13382
- const filter = active.predicate(selection.active.predicate);
13383
- return Promise.all(
13384
- Array.from(clients).map((client) => this.updateClient(client, filter))
13385
- );
13386
- }
13387
- async updateClient(client, filter) {
13388
- const { mc, indices, selection } = this;
13389
- if (!indices.has(client)) {
13390
- filter = selection.predicate(client);
13391
- return mc.updateClient(client, client.query(filter));
13392
- }
13393
- ;
13394
- const index = this.indices.get(client);
13395
- if (!index) return;
13396
- const { table: table2, dims, aggr, order = [] } = index;
13397
- const query = Query.select(dims, aggr).from(table2).groupby(dims).where(filter).orderby(order);
13398
- return mc.updateClient(client, query);
13449
+ indexes.set(client, info);
13450
+ return info;
13399
13451
  }
13400
13452
  };
13401
13453
  function activeColumns(clause) {
13402
13454
  const { source, meta } = clause;
13403
- let columns = clause.predicate?.columns;
13404
- if (!meta || !columns) return null;
13405
- const { type, scales: scales2, bin, pixelSize = 1 } = meta;
13455
+ const clausePred = clause.predicate;
13456
+ const clauseCols = clausePred?.columns;
13406
13457
  let predicate;
13407
- if (type === "interval" && scales2) {
13458
+ let columns;
13459
+ if (!meta || !clauseCols) {
13460
+ return { source: null, columns, predicate };
13461
+ }
13462
+ const { type, scales: scales2, bin, pixelSize = 1 } = meta;
13463
+ if (type === "point") {
13464
+ predicate = (x2) => x2;
13465
+ columns = Object.fromEntries(
13466
+ clauseCols.map((col) => [`${col}`, asColumn(col)])
13467
+ );
13468
+ } else if (type === "interval" && scales2) {
13408
13469
  const bins = scales2.map((s) => binInterval(s, pixelSize, bin));
13409
- if (bins.some((b) => b == null)) return null;
13410
- if (bins.length === 1) {
13470
+ if (bins.some((b) => !b)) {
13471
+ } else if (bins.length === 1) {
13411
13472
  predicate = (p) => p ? isBetween("active0", p.range.map(bins[0])) : [];
13412
- columns = { active0: bins[0](clause.predicate.field) };
13473
+ columns = { active0: bins[0](clausePred.field) };
13413
13474
  } else {
13414
- predicate = (p) => p ? and(p.children.map(({ range }, i) => isBetween(`active${i}`, range.map(bins[i])))) : [];
13475
+ predicate = (p) => p ? and(p.children.map(
13476
+ ({ range }, i) => isBetween(`active${i}`, range.map(bins[i]))
13477
+ )) : [];
13415
13478
  columns = Object.fromEntries(
13416
- clause.predicate.children.map((p, i) => [`active${i}`, bins[i](p.field)])
13479
+ // @ts-ignore
13480
+ clausePred.children.map((p, i) => [`active${i}`, bins[i](p.field)])
13417
13481
  );
13418
13482
  }
13419
- } else if (type === "point") {
13420
- predicate = (x2) => x2;
13421
- columns = Object.fromEntries(columns.map((col) => [`${col}`, asColumn(col)]));
13422
- } else {
13423
- return null;
13424
13483
  }
13425
- return { source, columns, predicate };
13484
+ return { source: columns ? source : null, columns, predicate };
13426
13485
  }
13427
13486
  var BIN = { ceil: "CEIL", round: "ROUND" };
13428
13487
  function binInterval(scale, pixelSize, bin) {
@@ -13436,6 +13495,23 @@ function binInterval(scale, pixelSize, bin) {
13436
13495
  const d = lo === 0 ? "" : ` - ${lo}::DOUBLE`;
13437
13496
  return (value) => sql`${fn}(${s}(${sqlApply(value)}${d}))::INTEGER`;
13438
13497
  }
13498
+ function dataCubeInfo(clientQuery, active, indexCols) {
13499
+ const { dims, aggr, aux } = indexCols;
13500
+ const { columns } = active;
13501
+ const query = clientQuery.select({ ...columns, ...aux }).groupby(Object.keys(columns));
13502
+ const [subq] = query.subqueries;
13503
+ if (subq) {
13504
+ const cols = Object.values(columns).flatMap((c) => c.columns);
13505
+ subqueryPushdown(subq, cols);
13506
+ }
13507
+ const order = query.orderby();
13508
+ query.query.orderby = [];
13509
+ const create2 = query.toString();
13510
+ const id = (fnv_hash(create2) >>> 0).toString(16);
13511
+ const table2 = `cube_index_${id}`;
13512
+ const select = Query.select(dims, aggr).from(table2).groupby(dims).orderby(order);
13513
+ return new DataCubeInfo({ table: table2, create: create2, active, select });
13514
+ }
13439
13515
  function subqueryPushdown(query, cols) {
13440
13516
  const memo = /* @__PURE__ */ new Set();
13441
13517
  const pushdown = (q) => {
@@ -13448,1290 +13524,1437 @@ function subqueryPushdown(query, cols) {
13448
13524
  };
13449
13525
  pushdown(query);
13450
13526
  }
13451
-
13452
- // ../core/src/util/AsyncDispatch.js
13453
- var AsyncDispatch = class {
13454
- /**
13455
- * Create a new asynchronous dispatcher instance.
13456
- */
13457
- constructor() {
13458
- this._callbacks = /* @__PURE__ */ new Map();
13459
- }
13460
- /**
13461
- * Add an event listener callback for the provided event type.
13462
- * @param {string} type The event type.
13463
- * @param {(value: *) => void | Promise} callback The event handler
13464
- * callback function to add. If the callback has already been
13465
- * added for the event type, this method has no effect.
13466
- */
13467
- addEventListener(type, callback) {
13468
- if (!this._callbacks.has(type)) {
13469
- this._callbacks.set(type, {
13470
- callbacks: /* @__PURE__ */ new Set(),
13471
- pending: null,
13472
- queue: new DispatchQueue()
13473
- });
13474
- }
13475
- const entry = this._callbacks.get(type);
13476
- entry.callbacks.add(callback);
13477
- }
13527
+ var DataCubeInfo = class {
13478
13528
  /**
13479
- * Remove an event listener callback for the provided event type.
13480
- * @param {string} type The event type.
13481
- * @param {(value: *) => void | Promise} callback The event handler
13482
- * callback function to remove.
13529
+ * Create a new DataCubeInfo instance.
13530
+ * @param {object} options
13483
13531
  */
13484
- removeEventListener(type, callback) {
13485
- const entry = this._callbacks.get(type);
13486
- if (entry) {
13487
- entry.callbacks.delete(callback);
13488
- }
13532
+ constructor({ table: table2, create: create2, active, select } = {}) {
13533
+ this.table = table2;
13534
+ this.create = create2;
13535
+ this.result = null;
13536
+ this.active = active;
13537
+ this.select = select;
13538
+ this.skip = false;
13489
13539
  }
13490
13540
  /**
13491
- * Lifecycle method that returns the event value to emit.
13492
- * This default implementation simply returns the input value as-is.
13493
- * Subclasses may override this method to implement custom transformations
13494
- * prior to emitting an event value to all listeners.
13495
- * @param {string} type The event type.
13496
- * @param {*} value The event value.
13497
- * @returns The (possibly transformed) event value to emit.
13541
+ * Generate a data cube index table query for the given predicate.
13542
+ * @param {import('@uwdata/mosaic-sql').SQLExpression} predicate The current
13543
+ * active clause predicate.
13544
+ * @returns {Query} A data cube index table query.
13498
13545
  */
13499
- willEmit(type, value) {
13500
- return value;
13546
+ query(predicate) {
13547
+ return this.select.clone().where(this.active.predicate(predicate));
13501
13548
  }
13549
+ };
13550
+
13551
+ // ../core/src/util/query-result.js
13552
+ var QueryResult = class extends Promise {
13502
13553
  /**
13503
- * Lifecycle method that returns a filter function for updating the
13504
- * queue of unemitted event values prior to enqueueing a new value.
13505
- * This default implementation simply returns null, indicating that
13506
- * any other unemitted event values should be dropped (that is, all
13507
- * queued events are filtered)
13508
- * @param {string} type The event type.
13509
- * @param {*} value The new event value that will be enqueued.
13510
- * @returns {(value: *) => boolean|null} A dispatch queue filter
13511
- * function, or null if all unemitted event values should be filtered.
13554
+ * Create a new query result Promise.
13512
13555
  */
13513
- emitQueueFilter(type, value) {
13514
- return null;
13556
+ constructor() {
13557
+ let resolve;
13558
+ let reject;
13559
+ super((r, e) => {
13560
+ resolve = r;
13561
+ reject = e;
13562
+ });
13563
+ this._resolve = resolve;
13564
+ this._reject = reject;
13515
13565
  }
13516
13566
  /**
13517
- * Cancel all unemitted event values for the given event type.
13518
- * @param {string} type The event type.
13567
+ * Resolve the result Promise with the provided value.
13568
+ * @param {*} value The result value.
13569
+ * @returns {this}
13519
13570
  */
13520
- cancel(type) {
13521
- const entry = this._callbacks.get(type);
13522
- entry?.queue.clear();
13571
+ fulfill(value) {
13572
+ this._resolve(value);
13573
+ return this;
13523
13574
  }
13524
13575
  /**
13525
- * Emit an event value to listeners for the given event type.
13526
- * If a previous emit has not yet resolved, the event value
13527
- * will be queued to be emitted later.
13528
- * The actual event value given to listeners will be the result
13529
- * of passing the input value through the emitValue() method.
13530
- * @param {string} type The event type.
13531
- * @param {*} value The event value.
13576
+ * Rejects the result Promise with the provided error.
13577
+ * @param {*} error The error value.
13578
+ * @returns {this}
13532
13579
  */
13533
- emit(type, value) {
13534
- const entry = this._callbacks.get(type) || {};
13535
- if (entry.pending) {
13536
- entry.queue.enqueue(value, this.emitQueueFilter(type, value));
13537
- } else {
13538
- const event = this.willEmit(type, value);
13539
- const { callbacks, queue } = entry;
13540
- if (callbacks?.size) {
13541
- const callbackValues = Array.from(callbacks, (cb) => cb(event));
13542
- entry.pending = Promise.allSettled(callbackValues).then(() => {
13543
- entry.pending = null;
13544
- if (!queue.isEmpty()) {
13545
- this.emit(type, queue.dequeue());
13546
- }
13547
- });
13548
- }
13549
- }
13580
+ reject(error) {
13581
+ this._reject(error);
13582
+ return this;
13550
13583
  }
13551
13584
  };
13552
- var DispatchQueue = class {
13553
- /**
13554
- * Create a new dispatch queue instance.
13555
- */
13556
- constructor() {
13557
- this.clear();
13558
- }
13559
- /**
13560
- * Clear the queue state of all event values.
13561
- */
13562
- clear() {
13563
- this.next = null;
13564
- }
13565
- /**
13566
- * Indicate if the queue is empty.
13567
- * @returns {boolean} True if queue is empty, false otherwise.
13568
- */
13569
- isEmpty() {
13570
- return !this.next;
13585
+ QueryResult.prototype.constructor = Promise;
13586
+
13587
+ // ../core/src/QueryConsolidator.js
13588
+ function wait(callback) {
13589
+ const method = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : typeof setImmediate !== "undefined" ? setImmediate : setTimeout;
13590
+ return method(callback);
13591
+ }
13592
+ function consolidator(enqueue, cache, record) {
13593
+ let pending = [];
13594
+ let id = 0;
13595
+ function run() {
13596
+ const groups = entryGroups(pending, cache);
13597
+ pending = [];
13598
+ id = 0;
13599
+ for (const group of groups) {
13600
+ consolidate(group, enqueue, record);
13601
+ processResults(group, cache);
13602
+ }
13571
13603
  }
13572
- /**
13573
- * Add a new value to the queue, and optionally filter the
13574
- * current queue content in response.
13575
- * @param {*} value The value to add.
13576
- * @param {(value: *) => boolean} [filter] An optional filter
13577
- * function to apply to existing queue content. If unspecified
13578
- * or falsy, all previously queued values are removed. Otherwise,
13579
- * the provided function is applied to all queue entries. The
13580
- * entry is retained if the filter function returns a truthy value,
13581
- * otherwise the entry is removed.
13582
- */
13583
- enqueue(value, filter) {
13584
- const tail = { value };
13585
- if (filter && this.next) {
13586
- let curr = this;
13587
- while (curr.next) {
13588
- if (filter(curr.next.value)) {
13589
- curr = curr.next;
13590
- } else {
13591
- curr.next = curr.next.next;
13592
- }
13604
+ return {
13605
+ add(entry, priority) {
13606
+ if (entry.request.type === "arrow") {
13607
+ id = id || wait(() => run());
13608
+ pending.push({ entry, priority, index: pending.length });
13609
+ } else {
13610
+ enqueue(entry, priority);
13593
13611
  }
13594
- curr.next = tail;
13595
- } else {
13596
- this.next = tail;
13597
13612
  }
13598
- }
13599
- /**
13600
- * Remove and return the next queued event value.
13601
- * @returns {*} The next event value in the queue.
13602
- */
13603
- dequeue() {
13604
- const { next } = this;
13605
- this.next = next?.next;
13606
- return next?.value;
13607
- }
13608
- };
13609
-
13610
- // ../core/src/util/distinct.js
13611
- function distinct(a, b) {
13612
- return a === b ? false : a instanceof Date && b instanceof Date ? +a !== +b : Array.isArray(a) && Array.isArray(b) ? distinctArray(a, b) : true;
13613
+ };
13613
13614
  }
13614
- function distinctArray(a, b) {
13615
- if (a.length !== b.length) return true;
13616
- for (let i = 0; i < a.length; ++i) {
13617
- if (a[i] !== b[i]) return true;
13615
+ function entryGroups(entries, cache) {
13616
+ const groups = [];
13617
+ const groupMap = /* @__PURE__ */ new Map();
13618
+ for (const query of entries) {
13619
+ const { entry: { request } } = query;
13620
+ const key = consolidationKey(request.query, cache);
13621
+ if (!groupMap.has(key)) {
13622
+ const list = [];
13623
+ groups.push(list);
13624
+ groupMap.set(key, list);
13625
+ }
13626
+ groupMap.get(key).push(query);
13618
13627
  }
13619
- return false;
13628
+ return groups;
13620
13629
  }
13621
-
13622
- // ../core/src/Param.js
13623
- function isParam(x2) {
13624
- return x2 instanceof Param;
13630
+ function consolidationKey(query, cache) {
13631
+ const sql2 = `${query}`;
13632
+ if (query instanceof Query && !cache.get(sql2)) {
13633
+ if (
13634
+ // @ts-ignore
13635
+ query.orderby().length || query.where().length || // @ts-ignore
13636
+ query.qualify().length || query.having().length
13637
+ ) {
13638
+ return sql2;
13639
+ }
13640
+ const q = query.clone().$select("*");
13641
+ const groupby = query.groupby();
13642
+ if (groupby.length) {
13643
+ const map = {};
13644
+ query.select().forEach(({ as, expr }) => map[as] = expr);
13645
+ q.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
13646
+ }
13647
+ return `${q}`;
13648
+ } else {
13649
+ return sql2;
13650
+ }
13625
13651
  }
13626
- var Param = class _Param extends AsyncDispatch {
13627
- /**
13628
- * Create a new Param instance.
13629
- * @param {*} value The initial value of the Param.
13630
- */
13631
- constructor(value) {
13632
- super();
13633
- this._value = value;
13652
+ function consolidate(group, enqueue, record) {
13653
+ if (shouldConsolidate(group)) {
13654
+ enqueue({
13655
+ request: {
13656
+ type: "arrow",
13657
+ cache: false,
13658
+ record: false,
13659
+ query: group.query = consolidatedQuery(group, record)
13660
+ },
13661
+ result: group.result = new QueryResult()
13662
+ });
13663
+ } else {
13664
+ for (const { entry, priority } of group) {
13665
+ enqueue(entry, priority);
13666
+ }
13634
13667
  }
13635
- /**
13636
- * Create a new Param instance with the given initial value.
13637
- * @param {*} value The initial value of the Param.
13638
- * @returns {Param} The new Param instance.
13639
- */
13640
- static value(value) {
13641
- return new _Param(value);
13668
+ }
13669
+ function shouldConsolidate(group) {
13670
+ if (group.length > 1) {
13671
+ const sql2 = `${group[0].entry.request.query}`;
13672
+ for (let i = 1; i < group.length; ++i) {
13673
+ if (sql2 !== `${group[i].entry.request.query}`) {
13674
+ return true;
13675
+ }
13676
+ }
13642
13677
  }
13643
- /**
13644
- * Create a new Param instance over an array of initial values,
13645
- * which may contain nested Params.
13646
- * @param {*} values The initial values of the Param.
13647
- * @returns {Param} The new Param instance.
13648
- */
13649
- static array(values) {
13650
- if (values.some((v) => isParam(v))) {
13651
- const p = new _Param();
13652
- const update2 = () => {
13653
- p.update(values.map((v) => isParam(v) ? v.value : v));
13654
- };
13655
- update2();
13656
- values.forEach((v) => isParam(v) ? v.addEventListener("value", update2) : 0);
13657
- return p;
13678
+ return false;
13679
+ }
13680
+ function consolidatedQuery(group, record) {
13681
+ const maps = group.maps = [];
13682
+ const fields = /* @__PURE__ */ new Map();
13683
+ for (const item of group) {
13684
+ const { query: query2 } = item.entry.request;
13685
+ const fieldMap = [];
13686
+ maps.push(fieldMap);
13687
+ for (const { as, expr } of query2.select()) {
13688
+ const e = `${expr}`;
13689
+ if (!fields.has(e)) {
13690
+ fields.set(e, [`col${fields.size}`, expr]);
13691
+ }
13692
+ const [name] = fields.get(e);
13693
+ fieldMap.push([name, as]);
13658
13694
  }
13659
- return new _Param(values);
13695
+ record(`${query2}`);
13660
13696
  }
13661
- /**
13662
- * The current value of the Param.
13663
- */
13664
- get value() {
13665
- return this._value;
13697
+ const query = group[0].entry.request.query.clone();
13698
+ const groupby = query.groupby();
13699
+ if (groupby.length) {
13700
+ const map = {};
13701
+ group.maps[0].forEach(([name, as]) => map[as] = name);
13702
+ query.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
13666
13703
  }
13667
- /**
13668
- * Update the Param value
13669
- * @param {*} value The new value of the Param.
13670
- * @param {object} [options] The update options.
13671
- * @param {boolean} [options.force] A boolean flag indicating if the Param
13672
- * should emit a 'value' event even if the internal value is unchanged.
13673
- * @returns {this} This Param instance.
13674
- */
13675
- update(value, { force } = {}) {
13676
- const shouldEmit = distinct(this._value, value) || force;
13677
- if (shouldEmit) {
13678
- this.emit("value", value);
13679
- } else {
13680
- this.cancel("value");
13704
+ return query.$select(Array.from(fields.values()));
13705
+ }
13706
+ async function processResults(group, cache) {
13707
+ const { maps, query, result } = group;
13708
+ if (!maps) return;
13709
+ let data;
13710
+ try {
13711
+ data = await result;
13712
+ } catch (err) {
13713
+ for (const { entry } of group) {
13714
+ entry.result.reject(err);
13681
13715
  }
13682
- return this;
13716
+ return;
13683
13717
  }
13684
- /**
13685
- * Upon value-typed updates, sets the current value to the input value
13686
- * immediately prior to the event value being emitted to listeners.
13687
- * @param {string} type The event type.
13688
- * @param {*} value The input event value.
13689
- * @returns {*} The input event value.
13690
- */
13691
- willEmit(type, value) {
13692
- if (type === "value") {
13693
- this._value = value;
13718
+ const describe = isDescribeQuery(query);
13719
+ group.forEach(({ entry }, index) => {
13720
+ const { request, result: result2 } = entry;
13721
+ const map = maps[index];
13722
+ const extract = describe && map ? filterResult(data, map) : map ? projectResult(data, map) : data;
13723
+ if (request.cache) {
13724
+ cache.set(String(request.query), extract);
13694
13725
  }
13695
- return value;
13696
- }
13697
- };
13698
-
13699
- // ../core/src/Selection.js
13700
- function isSelection(x2) {
13701
- return x2 instanceof Selection;
13726
+ result2.fulfill(extract);
13727
+ });
13702
13728
  }
13703
- var Selection = class _Selection extends Param {
13704
- /**
13705
- * Create a new Selection instance with an
13706
- * intersect (conjunction) resolution strategy.
13707
- * @param {object} [options] The selection options.
13708
- * @param {boolean} [options.cross=false] Boolean flag indicating
13709
- * cross-filtered resolution. If true, selection clauses will not
13710
- * be applied to the clients they are associated with.
13711
- * @returns {Selection} The new Selection instance.
13712
- */
13713
- static intersect({ cross = false } = {}) {
13714
- return new _Selection(new SelectionResolver({ cross }));
13715
- }
13716
- /**
13717
- * Create a new Selection instance with a
13718
- * union (disjunction) resolution strategy.
13719
- * @param {object} [options] The selection options.
13720
- * @param {boolean} [options.cross=false] Boolean flag indicating
13721
- * cross-filtered resolution. If true, selection clauses will not
13722
- * be applied to the clients they are associated with.
13723
- * @returns {Selection} The new Selection instance.
13724
- */
13725
- static union({ cross = false } = {}) {
13726
- return new _Selection(new SelectionResolver({ cross, union: true }));
13727
- }
13728
- /**
13729
- * Create a new Selection instance with a singular resolution strategy
13730
- * that keeps only the most recent selection clause.
13731
- * @param {object} [options] The selection options.
13732
- * @param {boolean} [options.cross=false] Boolean flag indicating
13733
- * cross-filtered resolution. If true, selection clauses will not
13734
- * be applied to the clients they are associated with.
13735
- * @returns {Selection} The new Selection instance.
13736
- */
13737
- static single({ cross = false } = {}) {
13738
- return new _Selection(new SelectionResolver({ cross, single: true }));
13739
- }
13740
- /**
13741
- * Create a new Selection instance with a
13742
- * cross-filtered intersect resolution strategy.
13743
- * @returns {Selection} The new Selection instance.
13744
- */
13745
- static crossfilter() {
13746
- return new _Selection(new SelectionResolver({ cross: true }));
13747
- }
13748
- /**
13749
- * Create a new Selection instance.
13750
- * @param {SelectionResolver} resolver The selection resolution
13751
- * strategy to apply.
13752
- */
13753
- constructor(resolver = new SelectionResolver()) {
13754
- super([]);
13755
- this._resolved = this._value;
13756
- this._resolver = resolver;
13757
- }
13758
- /**
13759
- * Create a cloned copy of this Selection instance.
13760
- * @returns {Selection} A clone of this selection.
13761
- */
13762
- clone() {
13763
- const s = new _Selection(this._resolver);
13764
- s._value = s._resolved = this._value;
13765
- return s;
13766
- }
13767
- /**
13768
- * Create a clone of this Selection with clauses corresponding
13769
- * to the provided source removed.
13770
- * @param {*} source The clause source to remove.
13771
- * @returns {Selection} A cloned and updated Selection.
13772
- */
13773
- remove(source) {
13774
- const s = this.clone();
13775
- s._value = s._resolved = s._resolver.resolve(this._resolved, { source });
13776
- s._value.active = { source };
13777
- return s;
13778
- }
13779
- /**
13780
- * The selection clause resolver.
13781
- */
13782
- get resolver() {
13783
- return this._resolver;
13784
- }
13785
- /**
13786
- * Indicate if this selection has a single resolution strategy.
13787
- */
13788
- get single() {
13789
- return this._resolver.single;
13790
- }
13791
- /**
13792
- * The current array of selection clauses.
13793
- */
13794
- get clauses() {
13795
- return super.value;
13729
+ function projectResult(data, map) {
13730
+ const cols = {};
13731
+ for (const [name, as] of map) {
13732
+ cols[as] = data.getChild(name);
13796
13733
  }
13797
- /**
13798
- * The current active (most recently updated) selection clause.
13799
- */
13800
- get active() {
13801
- return this.clauses.active;
13734
+ return new data.constructor(cols);
13735
+ }
13736
+ function filterResult(data, map) {
13737
+ const lookup = new Map(map);
13738
+ const result = [];
13739
+ for (const d of data) {
13740
+ if (lookup.has(d.column_name)) {
13741
+ result.push({ ...d, column_name: lookup.get(d.column_name) });
13742
+ }
13802
13743
  }
13803
- /**
13804
- * The value corresponding to the current active selection clause.
13805
- * This method ensures compatibility where a normal Param is expected.
13806
- */
13807
- get value() {
13808
- return this.active?.value;
13744
+ return result;
13745
+ }
13746
+
13747
+ // ../core/src/util/cache.js
13748
+ var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
13749
+ var voidCache = () => ({
13750
+ get: () => void 0,
13751
+ set: (key, value) => value,
13752
+ clear: () => {
13809
13753
  }
13810
- /**
13811
- * The value corresponding to a given source. Returns undefined if
13812
- * this selection does not include a clause from this source.
13813
- * @param {*} source The clause source to look up the value for.
13814
- */
13815
- valueFor(source) {
13816
- return this.clauses.find((c) => c.source === source)?.value;
13754
+ });
13755
+ function lruCache({
13756
+ max: max2 = 1e3,
13757
+ // max entries
13758
+ ttl = 3 * 60 * 60 * 1e3
13759
+ // time-to-live, default 3 hours
13760
+ } = {}) {
13761
+ let cache = /* @__PURE__ */ new Map();
13762
+ function evict() {
13763
+ const expire = performance.now() - ttl;
13764
+ let lruKey = null;
13765
+ let lruLast = Infinity;
13766
+ for (const [key, value] of cache) {
13767
+ const { last: last2 } = value;
13768
+ if (last2 < lruLast) {
13769
+ lruKey = key;
13770
+ lruLast = last2;
13771
+ }
13772
+ if (expire > last2) {
13773
+ cache.delete(key);
13774
+ }
13775
+ }
13776
+ if (lruKey) {
13777
+ cache.delete(lruKey);
13778
+ }
13817
13779
  }
13818
- /**
13819
- * Emit an activate event with the given selection clause.
13820
- * @param {*} clause The clause repesenting the potential activation.
13821
- */
13822
- activate(clause) {
13823
- this.emit("activate", clause);
13780
+ return {
13781
+ get(key) {
13782
+ const entry = cache.get(key);
13783
+ if (entry) {
13784
+ entry.last = performance.now();
13785
+ return entry.value;
13786
+ }
13787
+ },
13788
+ set(key, value) {
13789
+ cache.set(key, { last: performance.now(), value });
13790
+ if (cache.size > max2) requestIdle(evict);
13791
+ return value;
13792
+ },
13793
+ clear() {
13794
+ cache = /* @__PURE__ */ new Map();
13795
+ }
13796
+ };
13797
+ }
13798
+
13799
+ // ../core/src/util/priority-queue.js
13800
+ function priorityQueue(ranks) {
13801
+ const queue = Array.from(
13802
+ { length: ranks },
13803
+ () => ({ head: null, tail: null })
13804
+ );
13805
+ return {
13806
+ /**
13807
+ * Indicate if the queue is empty.
13808
+ * @returns [boolean] true if empty, false otherwise.
13809
+ */
13810
+ isEmpty() {
13811
+ return queue.every((list) => !list.head);
13812
+ },
13813
+ /**
13814
+ * Insert an item into the queue with a given priority rank.
13815
+ * @param {*} item The item to add.
13816
+ * @param {number} rank The integer priority rank.
13817
+ * Priority ranks are integers starting at zero.
13818
+ * Lower ranks indicate higher priority.
13819
+ */
13820
+ insert(item, rank2) {
13821
+ const list = queue[rank2];
13822
+ if (!list) {
13823
+ throw new Error(`Invalid queue priority rank: ${rank2}`);
13824
+ }
13825
+ const node = { item, next: null };
13826
+ if (list.head === null) {
13827
+ list.head = list.tail = node;
13828
+ } else {
13829
+ list.tail = list.tail.next = node;
13830
+ }
13831
+ },
13832
+ /**
13833
+ * Remove a set of items from the queue, regardless of priority rank.
13834
+ * If a provided item is not in the queue it will be ignored.
13835
+ * @param {(item: *) => boolean} test A predicate function to test
13836
+ * if an item should be removed (true to drop, false to keep).
13837
+ */
13838
+ remove(test) {
13839
+ for (const list of queue) {
13840
+ let { head, tail } = list;
13841
+ for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
13842
+ if (test(curr.item)) {
13843
+ if (curr === head) {
13844
+ head = curr.next;
13845
+ } else {
13846
+ prev.next = curr.next;
13847
+ }
13848
+ if (curr === tail) tail = prev || head;
13849
+ }
13850
+ }
13851
+ list.head = head;
13852
+ list.tail = tail;
13853
+ }
13854
+ },
13855
+ /**
13856
+ * Remove and return the next highest priority item.
13857
+ * @returns {*} The next item in the queue,
13858
+ * or undefined if this queue is empty.
13859
+ */
13860
+ next() {
13861
+ for (const list of queue) {
13862
+ const { head } = list;
13863
+ if (head !== null) {
13864
+ list.head = head.next;
13865
+ if (list.tail === head) {
13866
+ list.tail = null;
13867
+ }
13868
+ return head.item;
13869
+ }
13870
+ }
13871
+ }
13872
+ };
13873
+ }
13874
+
13875
+ // ../core/src/QueryManager.js
13876
+ var Priority = { High: 0, Normal: 1, Low: 2 };
13877
+ var QueryManager = class {
13878
+ constructor() {
13879
+ this.queue = priorityQueue(3);
13880
+ this.db = null;
13881
+ this.clientCache = null;
13882
+ this._logger = null;
13883
+ this._logQueries = false;
13884
+ this.recorders = [];
13885
+ this.pending = null;
13886
+ this._consolidate = null;
13824
13887
  }
13825
- /**
13826
- * Update the selection with a new selection clause.
13827
- * @param {*} clause The selection clause to add.
13828
- * @returns {this} This Selection instance.
13829
- */
13830
- update(clause) {
13831
- this._resolved = this._resolver.resolve(this._resolved, clause, true);
13832
- this._resolved.active = clause;
13833
- return super.update(this._resolved);
13888
+ next() {
13889
+ if (this.pending || this.queue.isEmpty()) return;
13890
+ const { request, result } = this.queue.next();
13891
+ this.pending = this.submit(request, result);
13892
+ this.pending.finally(() => {
13893
+ this.pending = null;
13894
+ this.next();
13895
+ });
13834
13896
  }
13835
- /**
13836
- * Upon value-typed updates, sets the current clause list to the
13837
- * input value and returns the active clause value.
13838
- * @param {string} type The event type.
13839
- * @param {*} value The input event value.
13840
- * @returns {*} For value-typed events, returns the active clause
13841
- * values. Otherwise returns the input event value as-is.
13842
- */
13843
- willEmit(type, value) {
13844
- if (type === "value") {
13845
- this._value = value;
13846
- return this.value;
13897
+ enqueue(entry, priority = Priority.Normal) {
13898
+ this.queue.insert(entry, priority);
13899
+ this.next();
13900
+ }
13901
+ recordQuery(sql2) {
13902
+ if (this.recorders.length && sql2) {
13903
+ this.recorders.forEach((rec) => rec.add(sql2));
13847
13904
  }
13848
- return value;
13849
13905
  }
13850
- /**
13851
- * Upon value-typed updates, returns a dispatch queue filter function.
13852
- * The return value depends on the selection resolution strategy.
13853
- * @param {string} type The event type.
13854
- * @param {*} value The new event value that will be enqueued.
13855
- * @returns {(value: *) => boolean|null} For value-typed events,
13856
- * returns a dispatch queue filter function. Otherwise returns null.
13857
- */
13858
- emitQueueFilter(type, value) {
13859
- return type === "value" ? this._resolver.queueFilter(value) : null;
13906
+ async submit(request, result) {
13907
+ try {
13908
+ const { query, type, cache = false, record = true, options } = request;
13909
+ const sql2 = query ? `${query}` : null;
13910
+ if (record) {
13911
+ this.recordQuery(sql2);
13912
+ }
13913
+ if (cache) {
13914
+ const cached = this.clientCache.get(sql2);
13915
+ if (cached) {
13916
+ this._logger.debug("Cache");
13917
+ result.fulfill(cached);
13918
+ return;
13919
+ }
13920
+ }
13921
+ const t0 = performance.now();
13922
+ if (this._logQueries) {
13923
+ this._logger.debug("Query", { type, sql: sql2, ...options });
13924
+ }
13925
+ const data = await this.db.query({ type, sql: sql2, ...options });
13926
+ if (cache) this.clientCache.set(sql2, data);
13927
+ this._logger.debug(`Request: ${(performance.now() - t0).toFixed(1)}`);
13928
+ result.fulfill(data);
13929
+ } catch (err) {
13930
+ result.reject(err);
13931
+ }
13860
13932
  }
13861
- /**
13862
- * Indicates if a selection clause should not be applied to a given client.
13863
- * The return value depends on the selection resolution strategy.
13864
- * @param {*} client The selection clause.
13865
- * @param {*} clause The client to test.
13866
- * @returns True if the client should be skipped, false otherwise.
13867
- */
13868
- skip(client, clause) {
13869
- return this._resolver.skip(client, clause);
13933
+ cache(value) {
13934
+ return value !== void 0 ? this.clientCache = value === true ? lruCache() : value || voidCache() : this.clientCache;
13870
13935
  }
13871
- /**
13872
- * Return a selection query predicate for the given client.
13873
- * @param {*} client The client whose data may be filtered.
13874
- * @param {boolean} [noSkip=false] Disable skipping of active
13875
- * cross-filtered sources. If set true, the source of the active
13876
- * clause in a cross-filtered selection will not be skipped.
13877
- * @returns {*} The query predicate for filtering client data,
13878
- * based on the current state of this selection.
13879
- */
13880
- predicate(client, noSkip = false) {
13881
- const { clauses } = this;
13882
- const active = noSkip ? null : clauses.active;
13883
- return this._resolver.predicate(clauses, active, client);
13936
+ logger(value) {
13937
+ return value ? this._logger = value : this._logger;
13884
13938
  }
13885
- };
13886
- var SelectionResolver = class {
13887
- /**
13888
- * Create a new selection resolved instance.
13889
- * @param {object} [options] The resolution strategy options.
13890
- * @param {boolean} [options.union=false] Boolean flag to indicate a union strategy.
13891
- * If false, an intersection strategy is used.
13892
- * @param {boolean} [options.cross=false] Boolean flag to indicate cross-filtering.
13893
- * @param {boolean} [options.single=false] Boolean flag to indicate single clauses only.
13894
- */
13895
- constructor({ union, cross, single } = {}) {
13896
- this.union = !!union;
13897
- this.cross = !!cross;
13898
- this.single = !!single;
13939
+ logQueries(value) {
13940
+ return value !== void 0 ? this._logQueries = !!value : this._logQueries;
13899
13941
  }
13900
- /**
13901
- * Resolve a list of selection clauses according to the resolution strategy.
13902
- * @param {*[]} clauseList An array of selection clauses.
13903
- * @param {*} clause A new selection clause to add.
13904
- * @returns {*[]} An updated array of selection clauses.
13905
- */
13906
- resolve(clauseList, clause, reset = false) {
13907
- const { source, predicate } = clause;
13908
- const filtered = clauseList.filter((c) => source !== c.source);
13909
- const clauses = this.single ? [] : filtered;
13910
- if (this.single && reset) filtered.forEach((c) => c.source?.reset?.());
13911
- if (predicate) clauses.push(clause);
13912
- return clauses;
13942
+ connector(connector) {
13943
+ return connector ? this.db = connector : this.db;
13913
13944
  }
13914
- /**
13915
- * Indicates if a selection clause should not be applied to a given client.
13916
- * The return value depends on the resolution strategy.
13917
- * @param {*} client The selection clause.
13918
- * @param {*} clause The client to test.
13919
- * @returns True if the client should be skipped, false otherwise.
13920
- */
13921
- skip(client, clause) {
13922
- return this.cross && clause?.clients?.has(client);
13945
+ consolidate(flag) {
13946
+ if (flag && !this._consolidate) {
13947
+ this._consolidate = consolidator(this.enqueue.bind(this), this.clientCache, this.recordQuery.bind(this));
13948
+ } else if (!flag && this._consolidate) {
13949
+ this._consolidate = null;
13950
+ }
13923
13951
  }
13924
- /**
13925
- * Return a selection query predicate for the given client.
13926
- * @param {*[]} clauseList An array of selection clauses.
13927
- * @param {*} active The current active selection clause.
13928
- * @param {*} client The client whose data may be filtered.
13929
- * @returns {*} The query predicate for filtering client data,
13930
- * based on the current state of this selection.
13931
- */
13932
- predicate(clauseList, active, client) {
13933
- const { union } = this;
13934
- if (this.skip(client, active)) return void 0;
13935
- const predicates = clauseList.filter((clause) => !this.skip(client, clause)).map((clause) => clause.predicate);
13936
- return union && predicates.length > 1 ? or(predicates) : predicates;
13952
+ request(request, priority = Priority.Normal) {
13953
+ const result = new QueryResult();
13954
+ const entry = { request, result };
13955
+ if (this._consolidate) {
13956
+ this._consolidate.add(entry, priority);
13957
+ } else {
13958
+ this.enqueue(entry, priority);
13959
+ }
13960
+ return result;
13937
13961
  }
13938
- /**
13939
- * Returns a filter function for queued selection updates.
13940
- * @param {*} value The new event value that will be enqueued.
13941
- * @returns {(value: *) => boolean|null} A dispatch queue filter
13942
- * function, or null if all unemitted event values should be filtered.
13943
- */
13944
- queueFilter(value) {
13945
- if (this.cross) {
13946
- const source = value.active?.source;
13947
- return (clauses) => clauses.active?.source !== source;
13962
+ cancel(requests) {
13963
+ const set = new Set(requests);
13964
+ if (set.size) {
13965
+ this.queue.remove(({ result }) => set.has(result));
13948
13966
  }
13949
- return null;
13950
13967
  }
13951
- };
13952
-
13953
- // ../core/src/FilterGroup.js
13954
- var FilterGroup = class {
13955
- /**
13956
- * @param {Coordinator} coordinator The Mosaic coordinator.
13957
- * @param {Selection} selection The shared filter selection.
13958
- * @param {object|boolean} index Boolean flag or options hash for
13959
- * a data cube indexer. Falsy values disable indexing.
13960
- */
13961
- constructor(coordinator2, selection, index = true) {
13962
- this.mc = coordinator2;
13963
- this.selection = selection;
13964
- this.clients = /* @__PURE__ */ new Set();
13965
- this.indexer = null;
13966
- this.index(index);
13967
- const { value, activate } = this.handlers = {
13968
- value: () => this.update(),
13969
- activate: (clause) => {
13970
- this.indexer?.index(this.clients, clause);
13968
+ clear() {
13969
+ this.queue.remove(({ result }) => {
13970
+ result.reject("Cleared");
13971
+ return true;
13972
+ });
13973
+ }
13974
+ record() {
13975
+ let state = [];
13976
+ const recorder = {
13977
+ add(query) {
13978
+ state.push(query);
13979
+ },
13980
+ reset() {
13981
+ state = [];
13982
+ },
13983
+ snapshot() {
13984
+ return state.slice();
13985
+ },
13986
+ stop() {
13987
+ this.recorders = this.recorders.filter((x2) => x2 !== recorder);
13988
+ return state;
13971
13989
  }
13972
13990
  };
13973
- selection.addEventListener("value", value);
13974
- selection.addEventListener("activate", activate);
13975
- }
13976
- finalize() {
13977
- const { value, activate } = this.handlers;
13978
- this.selection.removeEventListener("value", value);
13979
- this.selection.removeEventListener("activate", activate);
13980
- }
13981
- index(state) {
13982
- const { selection } = this;
13983
- const { resolver } = selection;
13984
- this.indexer = state && (resolver.single || !resolver.union) ? new DataCubeIndexer(this.mc, { ...state, selection }) : null;
13985
- }
13986
- reset() {
13987
- this.indexer?.reset();
13988
- }
13989
- add(client) {
13990
- (this.clients = new Set(this.clients)).add(client);
13991
- return this;
13992
- }
13993
- remove(client) {
13994
- if (this.clients.has(client)) {
13995
- (this.clients = new Set(this.clients)).delete(client);
13996
- }
13997
- return this;
13998
- }
13999
- /**
14000
- * Internal method to process a selection update.
14001
- * The return value is passed as a selection callback value.
14002
- * @returns {Promise} A Promise that resolves when the update completes.
14003
- */
14004
- update() {
14005
- const { mc, indexer, clients, selection } = this;
14006
- const hasIndex = indexer?.index(clients);
14007
- return hasIndex ? indexer.update() : defaultUpdate(mc, clients, selection);
13991
+ this.recorders.push(recorder);
13992
+ return recorder;
14008
13993
  }
14009
13994
  };
14010
- function defaultUpdate(mc, clients, selection) {
14011
- return Promise.all(Array.from(clients).map((client) => {
14012
- const filter = selection.predicate(client);
14013
- if (filter != null) {
14014
- return mc.updateClient(client, client.query(filter));
14015
- }
14016
- }));
14017
- }
14018
13995
 
14019
- // ../core/src/util/query-result.js
14020
- function queryResult() {
14021
- let resolve;
14022
- let reject;
14023
- const p = new Promise((r, e) => {
14024
- resolve = r;
14025
- reject = e;
14026
- });
14027
- return Object.assign(p, {
14028
- fulfill: (value) => (resolve(value), p),
14029
- reject: (err) => (reject(err), p)
14030
- });
13996
+ // ../core/src/util/js-type.js
13997
+ function jsType(type) {
13998
+ switch (type) {
13999
+ case "BIGINT":
14000
+ case "HUGEINT":
14001
+ case "INTEGER":
14002
+ case "SMALLINT":
14003
+ case "TINYINT":
14004
+ case "UBIGINT":
14005
+ case "UINTEGER":
14006
+ case "USMALLINT":
14007
+ case "UTINYINT":
14008
+ case "DOUBLE":
14009
+ case "FLOAT":
14010
+ case "REAL":
14011
+ return "number";
14012
+ case "DATE":
14013
+ case "TIMESTAMP":
14014
+ case "TIMESTAMPTZ":
14015
+ case "TIMESTAMP WITH TIME ZONE":
14016
+ case "TIME":
14017
+ case "TIMESTAMP_NS":
14018
+ return "date";
14019
+ case "BOOLEAN":
14020
+ return "boolean";
14021
+ case "VARCHAR":
14022
+ case "UUID":
14023
+ case "JSON":
14024
+ return "string";
14025
+ case "ARRAY":
14026
+ case "LIST":
14027
+ return "array";
14028
+ case "BLOB":
14029
+ case "STRUCT":
14030
+ case "MAP":
14031
+ case "GEOMETRY":
14032
+ return "object";
14033
+ default:
14034
+ if (type.startsWith("DECIMAL")) {
14035
+ return "number";
14036
+ } else if (type.startsWith("STRUCT") || type.startsWith("MAP")) {
14037
+ return "object";
14038
+ } else if (type.endsWith("]")) {
14039
+ return "array";
14040
+ }
14041
+ throw new Error(`Unsupported type: ${type}`);
14042
+ }
14031
14043
  }
14032
14044
 
14033
- // ../core/src/QueryConsolidator.js
14034
- function wait(callback) {
14035
- const method = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : typeof setImmediate !== "undefined" ? setImmediate : setTimeout;
14036
- return method(callback);
14045
+ // ../core/src/util/convert-arrow.js
14046
+ function isArrowTable(values) {
14047
+ return typeof values?.getChild === "function";
14037
14048
  }
14038
- function consolidator(enqueue, cache, record) {
14039
- let pending = [];
14040
- let id = 0;
14041
- function run() {
14042
- const groups = entryGroups(pending, cache);
14043
- pending = [];
14044
- id = 0;
14045
- for (const group of groups) {
14046
- consolidate(group, enqueue, record);
14047
- processResults(group, cache);
14048
- }
14049
+ function convertArrowValue(type) {
14050
+ if (DataType.isTimestamp(type)) {
14051
+ return (v) => v == null ? v : new Date(v);
14049
14052
  }
14050
- return {
14051
- add(entry, priority) {
14052
- if (entry.request.type === "arrow") {
14053
- id = id || wait(() => run());
14054
- pending.push({ entry, priority, index: pending.length });
14055
- } else {
14056
- enqueue(entry, priority);
14057
- }
14058
- }
14059
- };
14060
- }
14061
- function entryGroups(entries, cache) {
14062
- const groups = [];
14063
- const groupMap = /* @__PURE__ */ new Map();
14064
- for (const query of entries) {
14065
- const { entry: { request } } = query;
14066
- const key = consolidationKey(request.query, cache);
14067
- if (!groupMap.has(key)) {
14068
- const list = [];
14069
- groups.push(list);
14070
- groupMap.set(key, list);
14071
- }
14072
- groupMap.get(key).push(query);
14053
+ if (DataType.isInt(type) && type.bitWidth >= 64) {
14054
+ return (v) => v == null ? v : Number(v);
14073
14055
  }
14074
- return groups;
14075
- }
14076
- function consolidationKey(query, cache) {
14077
- const sql2 = `${query}`;
14078
- if (query instanceof Query && !cache.get(sql2)) {
14079
- if (
14080
- // @ts-ignore
14081
- query.orderby().length || query.where().length || // @ts-ignore
14082
- query.qualify().length || query.having().length
14083
- ) {
14084
- return sql2;
14085
- }
14086
- const q = query.clone().$select("*");
14087
- const groupby = query.groupby();
14088
- if (groupby.length) {
14089
- const map = {};
14090
- query.select().forEach(({ as, expr }) => map[as] = expr);
14091
- q.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
14092
- }
14093
- return `${q}`;
14094
- } else {
14095
- return sql2;
14056
+ if (DataType.isDecimal(type)) {
14057
+ const scale = 1 / Math.pow(10, type.scale);
14058
+ return (v) => v == null ? v : decimalToNumber(v, scale);
14096
14059
  }
14060
+ return (v) => v;
14097
14061
  }
14098
- function consolidate(group, enqueue, record) {
14099
- if (shouldConsolidate(group)) {
14100
- enqueue({
14101
- request: {
14102
- type: "arrow",
14103
- cache: false,
14104
- record: false,
14105
- query: group.query = consolidatedQuery(group, record)
14106
- },
14107
- result: group.result = queryResult()
14108
- });
14109
- } else {
14110
- for (const { entry, priority } of group) {
14111
- enqueue(entry, priority);
14062
+ function convertArrowColumn(column2) {
14063
+ const { type } = column2;
14064
+ if (DataType.isTimestamp(type)) {
14065
+ const size = column2.length;
14066
+ const array = new Array(size);
14067
+ for (let row = 0; row < size; ++row) {
14068
+ const v = column2.get(row);
14069
+ array[row] = v == null ? null : new Date(v);
14112
14070
  }
14071
+ return array;
14113
14072
  }
14114
- }
14115
- function shouldConsolidate(group) {
14116
- if (group.length > 1) {
14117
- const sql2 = `${group[0].entry.request.query}`;
14118
- for (let i = 1; i < group.length; ++i) {
14119
- if (sql2 !== `${group[i].entry.request.query}`) {
14120
- return true;
14121
- }
14073
+ if (DataType.isInt(type) && type.bitWidth >= 64) {
14074
+ const size = column2.length;
14075
+ const array = column2.nullCount ? new Array(size) : new Float64Array(size);
14076
+ for (let row = 0; row < size; ++row) {
14077
+ const v = column2.get(row);
14078
+ array[row] = v == null ? null : Number(v);
14122
14079
  }
14080
+ return array;
14123
14081
  }
14124
- return false;
14125
- }
14126
- function consolidatedQuery(group, record) {
14127
- const maps = group.maps = [];
14128
- const fields = /* @__PURE__ */ new Map();
14129
- for (const item of group) {
14130
- const { query: query2 } = item.entry.request;
14131
- const fieldMap = [];
14132
- maps.push(fieldMap);
14133
- for (const { as, expr } of query2.select()) {
14134
- const e = `${expr}`;
14135
- if (!fields.has(e)) {
14136
- fields.set(e, [`col${fields.size}`, expr]);
14137
- }
14138
- const [name] = fields.get(e);
14139
- fieldMap.push([name, as]);
14082
+ if (DataType.isDecimal(type)) {
14083
+ const scale = 1 / Math.pow(10, type.scale);
14084
+ const size = column2.length;
14085
+ const array = column2.nullCount ? new Array(size) : new Float64Array(size);
14086
+ for (let row = 0; row < size; ++row) {
14087
+ const v = column2.get(row);
14088
+ array[row] = v == null ? null : decimalToNumber(v, scale);
14140
14089
  }
14141
- record(`${query2}`);
14090
+ return array;
14142
14091
  }
14143
- const query = group[0].entry.request.query.clone();
14144
- const groupby = query.groupby();
14145
- if (groupby.length) {
14146
- const map = {};
14147
- group.maps[0].forEach(([name, as]) => map[as] = name);
14148
- query.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
14092
+ if (column2.nullCount) {
14093
+ return Array.from(column2);
14149
14094
  }
14150
- return query.$select(Array.from(fields.values()));
14095
+ return column2.toArray();
14151
14096
  }
14152
- async function processResults(group, cache) {
14153
- const { maps, query, result } = group;
14154
- if (!maps) return;
14155
- let data;
14156
- try {
14157
- data = await result;
14158
- } catch (err) {
14159
- for (const { entry } of group) {
14160
- entry.result.reject(err);
14161
- }
14162
- return;
14163
- }
14164
- const describe = isDescribeQuery(query);
14165
- group.forEach(({ entry }, index) => {
14166
- const { request, result: result2 } = entry;
14167
- const map = maps[index];
14168
- const extract = describe && map ? filterResult(data, map) : map ? projectResult(data, map) : data;
14169
- if (request.cache) {
14170
- cache.set(String(request.query), extract);
14097
+ var BASE32 = Array.from(
14098
+ { length: 8 },
14099
+ (_, i) => Math.pow(2, i * 32)
14100
+ );
14101
+ function decimalToNumber(v, scale) {
14102
+ const n = v.length;
14103
+ let x2 = 0;
14104
+ if (v.signed && (v[n - 1] | 0) < 0) {
14105
+ for (let i = 0; i < n; ++i) {
14106
+ x2 += ~v[i] * BASE32[i];
14171
14107
  }
14172
- result2.fulfill(extract);
14173
- });
14174
- }
14175
- function projectResult(data, map) {
14176
- const cols = {};
14177
- for (const [name, as] of map) {
14178
- cols[as] = data.getChild(name);
14179
- }
14180
- return new data.constructor(cols);
14181
- }
14182
- function filterResult(data, map) {
14183
- const lookup = new Map(map);
14184
- const result = [];
14185
- for (const d of data) {
14186
- if (lookup.has(d.column_name)) {
14187
- result.push({ ...d, column_name: lookup.get(d.column_name) });
14108
+ x2 = -(x2 + 1);
14109
+ } else {
14110
+ for (let i = 0; i < n; ++i) {
14111
+ x2 += v[i] * BASE32[i];
14188
14112
  }
14189
14113
  }
14190
- return result;
14114
+ return x2 * scale;
14191
14115
  }
14192
14116
 
14193
- // ../core/src/util/cache.js
14194
- var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
14195
- var voidCache = () => ({
14196
- get: () => void 0,
14197
- set: (key, value) => value,
14198
- clear: () => {
14199
- }
14200
- });
14201
- function lruCache({
14202
- max: max2 = 1e3,
14203
- // max entries
14204
- ttl = 3 * 60 * 60 * 1e3
14205
- // time-to-live, default 3 hours
14206
- } = {}) {
14207
- let cache = /* @__PURE__ */ new Map();
14208
- function evict() {
14209
- const expire = performance.now() - ttl;
14210
- let lruKey = null;
14211
- let lruLast = Infinity;
14212
- for (const [key, value] of cache) {
14213
- const { last: last2 } = value;
14214
- if (last2 < lruLast) {
14215
- lruKey = key;
14216
- lruLast = last2;
14217
- }
14218
- if (expire > last2) {
14219
- cache.delete(key);
14220
- }
14221
- }
14222
- if (lruKey) {
14223
- cache.delete(lruKey);
14224
- }
14117
+ // ../core/src/util/field-info.js
14118
+ var Count = "count";
14119
+ var Nulls = "nulls";
14120
+ var Max = "max";
14121
+ var Min = "min";
14122
+ var Distinct = "distinct";
14123
+ var statMap = {
14124
+ [Count]: count,
14125
+ [Distinct]: (column2) => count(column2).distinct(),
14126
+ [Max]: max,
14127
+ [Min]: min,
14128
+ [Nulls]: (column2) => count().where(isNull(column2))
14129
+ };
14130
+ function summarize(table2, column2, stats) {
14131
+ return Query.from(table2).select(Array.from(stats, (s) => [s, statMap[s](column2)]));
14132
+ }
14133
+ async function queryFieldInfo(mc, fields) {
14134
+ if (fields.length === 1 && `${fields[0].column}` === "*") {
14135
+ return getTableInfo(mc, fields[0].table);
14136
+ } else {
14137
+ return (await Promise.all(fields.map((f) => getFieldInfo(mc, f)))).filter((x2) => x2);
14225
14138
  }
14226
- return {
14227
- get(key) {
14228
- const entry = cache.get(key);
14229
- if (entry) {
14230
- entry.last = performance.now();
14231
- return entry.value;
14232
- }
14233
- },
14234
- set(key, value) {
14235
- cache.set(key, { last: performance.now(), value });
14236
- if (cache.size > max2) requestIdle(evict);
14237
- return value;
14238
- },
14239
- clear() {
14240
- cache = /* @__PURE__ */ new Map();
14241
- }
14139
+ }
14140
+ async function getFieldInfo(mc, { table: table2, column: column2, stats }) {
14141
+ const q = Query.from({ source: table2 }).select({ column: column2 }).groupby(column2.aggregate ? sql`ALL` : []);
14142
+ const [desc2] = Array.from(await mc.query(Query.describe(q)));
14143
+ const info = {
14144
+ table: table2,
14145
+ column: `${column2}`,
14146
+ sqlType: desc2.column_type,
14147
+ type: jsType(desc2.column_type),
14148
+ nullable: desc2.null === "YES"
14242
14149
  };
14150
+ if (!(stats?.length || stats?.size)) return info;
14151
+ const result = await mc.query(
14152
+ summarize(table2, column2, stats),
14153
+ { persist: true }
14154
+ );
14155
+ for (let i = 0; i < result.numCols; ++i) {
14156
+ const { name } = result.schema.fields[i];
14157
+ const child = result.getChildAt(i);
14158
+ const convert = convertArrowValue(child.type);
14159
+ info[name] = convert(child.get(0));
14160
+ }
14161
+ return info;
14162
+ }
14163
+ async function getTableInfo(mc, table2) {
14164
+ const result = await mc.query(`DESCRIBE ${asRelation(table2)}`);
14165
+ return Array.from(result).map((desc2) => ({
14166
+ table: table2,
14167
+ column: desc2.column_name,
14168
+ sqlType: desc2.column_type,
14169
+ type: jsType(desc2.column_type),
14170
+ nullable: desc2.null === "YES"
14171
+ }));
14243
14172
  }
14244
14173
 
14245
- // ../core/src/util/priority-queue.js
14246
- function priorityQueue(ranks) {
14247
- const queue = Array.from(
14248
- { length: ranks },
14249
- () => ({ head: null, tail: null })
14250
- );
14174
+ // ../core/src/util/void-logger.js
14175
+ function voidLogger() {
14251
14176
  return {
14252
- /**
14253
- * Indicate if the queue is empty.
14254
- * @returns [boolean] true if empty, false otherwise.
14255
- */
14256
- isEmpty() {
14257
- return queue.every((list) => !list.head);
14177
+ debug() {
14258
14178
  },
14259
- /**
14260
- * Insert an item into the queue with a given priority rank.
14261
- * @param {*} item The item to add.
14262
- * @param {number} rank The integer priority rank.
14263
- * Priority ranks are integers starting at zero.
14264
- * Lower ranks indicate higher priority.
14265
- */
14266
- insert(item, rank2) {
14267
- const list = queue[rank2];
14268
- if (!list) {
14269
- throw new Error(`Invalid queue priority rank: ${rank2}`);
14270
- }
14271
- const node = { item, next: null };
14272
- if (list.head === null) {
14273
- list.head = list.tail = node;
14274
- } else {
14275
- list.tail = list.tail.next = node;
14276
- }
14179
+ info() {
14277
14180
  },
14278
- /**
14279
- * Remove a set of items from the queue, regardless of priority rank.
14280
- * If a provided item is not in the queue it will be ignored.
14281
- * @param {(item: *) => boolean} test A predicate function to test
14282
- * if an item should be removed (true to drop, false to keep).
14283
- */
14284
- remove(test) {
14285
- for (const list of queue) {
14286
- let { head, tail } = list;
14287
- for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
14288
- if (test(curr.item)) {
14289
- if (curr === head) {
14290
- head = curr.next;
14291
- } else {
14292
- prev.next = curr.next;
14293
- }
14294
- if (curr === tail) tail = prev || head;
14295
- }
14296
- }
14297
- list.head = head;
14298
- list.tail = tail;
14299
- }
14181
+ log() {
14300
14182
  },
14301
- /**
14302
- * Remove and return the next highest priority item.
14303
- * @returns {*} The next item in the queue,
14304
- * or undefined if this queue is empty.
14305
- */
14306
- next() {
14307
- for (const list of queue) {
14308
- const { head } = list;
14309
- if (head !== null) {
14310
- list.head = head.next;
14311
- if (list.tail === head) {
14312
- list.tail = null;
14313
- }
14314
- return head.item;
14315
- }
14316
- }
14183
+ warn() {
14184
+ },
14185
+ error() {
14317
14186
  }
14318
14187
  };
14319
14188
  }
14320
14189
 
14321
- // ../core/src/QueryManager.js
14322
- var Priority = { High: 0, Normal: 1, Low: 2 };
14323
- var QueryManager = class {
14324
- constructor() {
14325
- this.queue = priorityQueue(3);
14326
- this.db = null;
14327
- this.clientCache = null;
14328
- this._logger = null;
14329
- this._logQueries = false;
14330
- this.recorders = [];
14331
- this.pending = null;
14332
- this._consolidate = null;
14190
+ // ../core/src/Coordinator.js
14191
+ var _instance;
14192
+ function coordinator(instance8) {
14193
+ if (instance8) {
14194
+ _instance = instance8;
14195
+ } else if (_instance == null) {
14196
+ _instance = new Coordinator();
14333
14197
  }
14334
- next() {
14335
- if (this.pending || this.queue.isEmpty()) return;
14336
- const { request, result } = this.queue.next();
14337
- this.pending = this.submit(request, result);
14338
- this.pending.finally(() => {
14339
- this.pending = null;
14340
- this.next();
14341
- });
14198
+ return _instance;
14199
+ }
14200
+ var Coordinator = class {
14201
+ constructor(db = socketConnector(), {
14202
+ logger = console,
14203
+ manager = new QueryManager(),
14204
+ cache = true,
14205
+ consolidate: consolidate2 = true,
14206
+ indexes = {}
14207
+ } = {}) {
14208
+ this.manager = manager;
14209
+ this.manager.cache(cache);
14210
+ this.manager.consolidate(consolidate2);
14211
+ this.dataCubeIndexer = new DataCubeIndexer(this, indexes);
14212
+ this.logger(logger);
14213
+ this.databaseConnector(db);
14214
+ this.clear();
14342
14215
  }
14343
- enqueue(entry, priority = Priority.Normal) {
14344
- this.queue.insert(entry, priority);
14345
- this.next();
14216
+ /**
14217
+ * Clear the coordinator state.
14218
+ * @param {object} [options] Options object.
14219
+ * @param {boolean} [options.clients=true] If true, disconnect all clients.
14220
+ * @param {boolean} [options.cache=true] If true, clear the query cache.
14221
+ */
14222
+ clear({ clients = true, cache = true } = {}) {
14223
+ this.manager.clear();
14224
+ if (clients) {
14225
+ this.filterGroups?.forEach((group) => group.disconnect());
14226
+ this.filterGroups = /* @__PURE__ */ new Map();
14227
+ this.clients?.forEach((client) => this.disconnect(client));
14228
+ this.clients = /* @__PURE__ */ new Set();
14229
+ }
14230
+ if (cache) this.manager.cache().clear();
14346
14231
  }
14347
- recordQuery(sql2) {
14348
- if (this.recorders.length && sql2) {
14349
- this.recorders.forEach((rec) => rec.add(sql2));
14232
+ /**
14233
+ * Get or set the database connector.
14234
+ * @param {*} [db] The database connector to use.
14235
+ * @returns The current database connector.
14236
+ */
14237
+ databaseConnector(db) {
14238
+ return this.manager.connector(db);
14239
+ }
14240
+ /**
14241
+ * Get or set the logger.
14242
+ * @param {*} logger The logger to use.
14243
+ * @returns The current logger
14244
+ */
14245
+ logger(logger) {
14246
+ if (arguments.length) {
14247
+ this._logger = logger || voidLogger();
14248
+ this.manager.logger(this._logger);
14350
14249
  }
14250
+ return this._logger;
14351
14251
  }
14352
- async submit(request, result) {
14353
- try {
14354
- const { query, type, cache = false, record = true, options } = request;
14355
- const sql2 = query ? `${query}` : null;
14356
- if (record) {
14357
- this.recordQuery(sql2);
14358
- }
14359
- if (cache) {
14360
- const cached = this.clientCache.get(sql2);
14361
- if (cached) {
14362
- this._logger.debug("Cache");
14363
- result.fulfill(cached);
14364
- return;
14365
- }
14252
+ // -- Query Management ----
14253
+ /**
14254
+ * Cancel previosuly submitted query requests. These queries will be
14255
+ * canceled if they are queued but have not yet been submitted.
14256
+ * @param {import('./util/query-result.js').QueryResult[]} requests An array
14257
+ * of query result objects, such as those returned by the `query` method.
14258
+ */
14259
+ cancel(requests) {
14260
+ this.manager.cancel(requests);
14261
+ }
14262
+ /**
14263
+ * Issue a query for which no result (return value) is needed.
14264
+ * @param {import('@uwdata/mosaic-sql').Query | string} query The query.
14265
+ * @param {object} [options] An options object.
14266
+ * @param {number} [options.priority] The query priority, defaults to
14267
+ * `Priority.Normal`.
14268
+ * @returns {import('./util/query-result.js').QueryResult} A query result
14269
+ * promise.
14270
+ */
14271
+ exec(query, { priority = Priority.Normal } = {}) {
14272
+ query = Array.isArray(query) ? query.join(";\n") : query;
14273
+ return this.manager.request({ type: "exec", query }, priority);
14274
+ }
14275
+ /**
14276
+ * Issue a query to the backing database. The submitted query may be
14277
+ * consolidate with other queries and its results may be cached.
14278
+ * @param {import('@uwdata/mosaic-sql').Query | string} query The query.
14279
+ * @param {object} [options] An options object.
14280
+ * @param {'arrow' | 'json'} [options.type] The query result format type.
14281
+ * @param {boolean} [options.cache=true] If true, cache the query result.
14282
+ * @param {number} [options.priority] The query priority, defaults to
14283
+ * `Priority.Normal`.
14284
+ * @returns {import('./util/query-result.js').QueryResult} A query result
14285
+ * promise.
14286
+ */
14287
+ query(query, {
14288
+ type = "arrow",
14289
+ cache = true,
14290
+ priority = Priority.Normal,
14291
+ ...options
14292
+ } = {}) {
14293
+ return this.manager.request({ type, query, cache, options }, priority);
14294
+ }
14295
+ /**
14296
+ * Issue a query to prefetch data for later use. The query result is cached
14297
+ * for efficient future access.
14298
+ * @param {import('@uwdata/mosaic-sql').Query | string} query The query.
14299
+ * @param {object} [options] An options object.
14300
+ * @param {'arrow' | 'json'} [options.type] The query result format type.
14301
+ * @returns {import('./util/query-result.js').QueryResult} A query result
14302
+ * promise.
14303
+ */
14304
+ prefetch(query, options = {}) {
14305
+ return this.query(query, { ...options, cache: true, priority: Priority.Low });
14306
+ }
14307
+ createBundle(name, queries, priority = Priority.Low) {
14308
+ const options = { name, queries };
14309
+ return this.manager.request({ type: "create-bundle", options }, priority);
14310
+ }
14311
+ loadBundle(name, priority = Priority.High) {
14312
+ const options = { name };
14313
+ return this.manager.request({ type: "load-bundle", options }, priority);
14314
+ }
14315
+ // -- Client Management ----
14316
+ /**
14317
+ * Update client data by submitting the given query and returning the
14318
+ * data (or error) to the client.
14319
+ * @param {import('./MosaicClient.js').MosaicClient} client A Mosaic client.
14320
+ * @param {import('@uwdata/mosaic-sql').Query | string} query The data query.
14321
+ * @param {number} [priority] The query priority.
14322
+ * @returns {Promise} A Promise that resolves upon completion of the update.
14323
+ */
14324
+ updateClient(client, query, priority = Priority.Normal) {
14325
+ client.queryPending();
14326
+ return this.query(query, { priority }).then(
14327
+ (data) => client.queryResult(data).update(),
14328
+ (err) => {
14329
+ this._logger.error(err);
14330
+ client.queryError(err);
14366
14331
  }
14367
- const t0 = performance.now();
14368
- if (this._logQueries) {
14369
- this._logger.debug("Query", { type, sql: sql2, ...options });
14332
+ ).catch((err) => this._logger.error(err));
14333
+ }
14334
+ /**
14335
+ * Issue a query request for a client. If the query is null or undefined,
14336
+ * the client is simply updated. Otherwise `updateClient` is called. As a
14337
+ * side effect, this method clears the current data cube indexer state.
14338
+ * @param {import('./MosaicClient.js').MosaicClient} client The client
14339
+ * to update.
14340
+ * @param {import('@uwdata/mosaic-sql').Query | string | null} [query]
14341
+ * The query to issue.
14342
+ */
14343
+ requestQuery(client, query) {
14344
+ this.dataCubeIndexer.clear();
14345
+ return query ? this.updateClient(client, query) : client.update();
14346
+ }
14347
+ /**
14348
+ * Connect a client to the coordinator.
14349
+ * @param {import('./MosaicClient.js').MosaicClient} client The Mosaic
14350
+ * client to connect.
14351
+ */
14352
+ async connect(client) {
14353
+ const { clients } = this;
14354
+ if (clients.has(client)) {
14355
+ throw new Error("Client already connected.");
14356
+ }
14357
+ clients.add(client);
14358
+ client.coordinator = this;
14359
+ const fields = client.fields();
14360
+ if (fields?.length) {
14361
+ client.fieldInfo(await queryFieldInfo(this, fields));
14362
+ }
14363
+ connectSelection(this, client.filterBy, client);
14364
+ client.requestQuery();
14365
+ }
14366
+ /**
14367
+ * Disconnect a client from the coordinator.
14368
+ * @param {import('./MosaicClient.js').MosaicClient} client The Mosaic
14369
+ * client to disconnect.
14370
+ */
14371
+ disconnect(client) {
14372
+ const { clients, filterGroups } = this;
14373
+ if (!clients.has(client)) return;
14374
+ clients.delete(client);
14375
+ client.coordinator = null;
14376
+ const group = filterGroups.get(client.filterBy);
14377
+ if (group) {
14378
+ group.clients.delete(client);
14379
+ }
14380
+ }
14381
+ };
14382
+ function connectSelection(mc, selection, client) {
14383
+ if (!selection) return;
14384
+ let entry = mc.filterGroups.get(selection);
14385
+ if (!entry) {
14386
+ const activate = (clause) => activateSelection(mc, selection, clause);
14387
+ const value = () => updateSelection(mc, selection);
14388
+ selection.addEventListener("activate", activate);
14389
+ selection.addEventListener("value", value);
14390
+ entry = {
14391
+ selection,
14392
+ clients: /* @__PURE__ */ new Set(),
14393
+ disconnect() {
14394
+ selection.removeEventListener("activate", activate);
14395
+ selection.removeEventListener("value", value);
14370
14396
  }
14371
- const data = await this.db.query({ type, sql: sql2, ...options });
14372
- if (cache) this.clientCache.set(sql2, data);
14373
- this._logger.debug(`Request: ${(performance.now() - t0).toFixed(1)}`);
14374
- result.fulfill(data);
14375
- } catch (err) {
14376
- result.reject(err);
14397
+ };
14398
+ mc.filterGroups.set(selection, entry);
14399
+ }
14400
+ entry.clients.add(client);
14401
+ }
14402
+ function activateSelection(mc, selection, clause) {
14403
+ const { dataCubeIndexer, filterGroups } = mc;
14404
+ const { clients } = filterGroups.get(selection);
14405
+ for (const client of clients) {
14406
+ dataCubeIndexer.index(client, selection, clause);
14407
+ }
14408
+ }
14409
+ function updateSelection(mc, selection) {
14410
+ const { dataCubeIndexer, filterGroups } = mc;
14411
+ const { clients } = filterGroups.get(selection);
14412
+ const { active } = selection;
14413
+ return Promise.allSettled(Array.from(clients, (client) => {
14414
+ const info = dataCubeIndexer.index(client, selection, active);
14415
+ const filter = info ? null : selection.predicate(client);
14416
+ if (info?.skip || !info && !filter) return;
14417
+ const query = info?.query(active.predicate) ?? client.query(filter);
14418
+ return mc.updateClient(client, query);
14419
+ }));
14420
+ }
14421
+
14422
+ // ../core/src/util/AsyncDispatch.js
14423
+ var AsyncDispatch = class {
14424
+ /**
14425
+ * Create a new asynchronous dispatcher instance.
14426
+ */
14427
+ constructor() {
14428
+ this._callbacks = /* @__PURE__ */ new Map();
14429
+ }
14430
+ /**
14431
+ * Add an event listener callback for the provided event type.
14432
+ * @param {string} type The event type.
14433
+ * @param {(value: *) => void | Promise} callback The event handler
14434
+ * callback function to add. If the callback has already been
14435
+ * added for the event type, this method has no effect.
14436
+ */
14437
+ addEventListener(type, callback) {
14438
+ if (!this._callbacks.has(type)) {
14439
+ this._callbacks.set(type, {
14440
+ callbacks: /* @__PURE__ */ new Set(),
14441
+ pending: null,
14442
+ queue: new DispatchQueue()
14443
+ });
14377
14444
  }
14445
+ const entry = this._callbacks.get(type);
14446
+ entry.callbacks.add(callback);
14378
14447
  }
14379
- cache(value) {
14380
- return value !== void 0 ? this.clientCache = value === true ? lruCache() : value || voidCache() : this.clientCache;
14448
+ /**
14449
+ * Remove an event listener callback for the provided event type.
14450
+ * @param {string} type The event type.
14451
+ * @param {(value: *) => void | Promise} callback The event handler
14452
+ * callback function to remove.
14453
+ */
14454
+ removeEventListener(type, callback) {
14455
+ const entry = this._callbacks.get(type);
14456
+ if (entry) {
14457
+ entry.callbacks.delete(callback);
14458
+ }
14381
14459
  }
14382
- logger(value) {
14383
- return value ? this._logger = value : this._logger;
14460
+ /**
14461
+ * Lifecycle method that returns the event value to emit.
14462
+ * This default implementation simply returns the input value as-is.
14463
+ * Subclasses may override this method to implement custom transformations
14464
+ * prior to emitting an event value to all listeners.
14465
+ * @param {string} type The event type.
14466
+ * @param {*} value The event value.
14467
+ * @returns The (possibly transformed) event value to emit.
14468
+ */
14469
+ willEmit(type, value) {
14470
+ return value;
14384
14471
  }
14385
- logQueries(value) {
14386
- return value !== void 0 ? this._logQueries = !!value : this._logQueries;
14472
+ /**
14473
+ * Lifecycle method that returns a filter function for updating the
14474
+ * queue of unemitted event values prior to enqueueing a new value.
14475
+ * This default implementation simply returns null, indicating that
14476
+ * any other unemitted event values should be dropped (that is, all
14477
+ * queued events are filtered).
14478
+ * @param {string} type The event type.
14479
+ * @param {*} value The new event value that will be enqueued.
14480
+ * @returns {(value: *) => boolean|null} A dispatch queue filter
14481
+ * function, or null if all unemitted event values should be filtered.
14482
+ */
14483
+ emitQueueFilter(type, value) {
14484
+ return null;
14387
14485
  }
14388
- connector(connector) {
14389
- return connector ? this.db = connector : this.db;
14486
+ /**
14487
+ * Cancel all unemitted event values for the given event type.
14488
+ * @param {string} type The event type.
14489
+ */
14490
+ cancel(type) {
14491
+ const entry = this._callbacks.get(type);
14492
+ entry?.queue.clear();
14390
14493
  }
14391
- consolidate(flag) {
14392
- if (flag && !this._consolidate) {
14393
- this._consolidate = consolidator(this.enqueue.bind(this), this.clientCache, this.recordQuery.bind(this));
14394
- } else if (!flag && this._consolidate) {
14395
- this._consolidate = null;
14396
- }
14494
+ /**
14495
+ * Returns a promise that resolves when any pending updates complete for
14496
+ * the event of the given type currently being processed. The Promise will
14497
+ * resolve immediately if the queue for the given event type is empty.
14498
+ * @param {string} type The event type to wait for.
14499
+ * @returns {Promise} A pending event promise.
14500
+ */
14501
+ async pending(type) {
14502
+ await this._callbacks.get(type)?.pending;
14397
14503
  }
14398
- request(request, priority = Priority.Normal) {
14399
- const result = queryResult();
14400
- const entry = { request, result };
14401
- if (this._consolidate) {
14402
- this._consolidate.add(entry, priority);
14504
+ /**
14505
+ * Emit an event value to listeners for the given event type.
14506
+ * If a previous emit has not yet resolved, the event value
14507
+ * will be queued to be emitted later.
14508
+ * The actual event value given to listeners will be the result
14509
+ * of passing the input value through the emitValue() method.
14510
+ * @param {string} type The event type.
14511
+ * @param {*} value The event value.
14512
+ */
14513
+ emit(type, value) {
14514
+ const entry = this._callbacks.get(type) || {};
14515
+ if (entry.pending) {
14516
+ entry.queue.enqueue(value, this.emitQueueFilter(type, value));
14403
14517
  } else {
14404
- this.enqueue(entry, priority);
14518
+ const event = this.willEmit(type, value);
14519
+ const { callbacks, queue } = entry;
14520
+ if (callbacks?.size) {
14521
+ const callbackValues = Array.from(callbacks, (cb) => cb(event));
14522
+ entry.pending = Promise.allSettled(callbackValues).then(() => {
14523
+ entry.pending = null;
14524
+ if (!queue.isEmpty()) {
14525
+ this.emit(type, queue.dequeue());
14526
+ }
14527
+ });
14528
+ }
14405
14529
  }
14406
- return result;
14407
14530
  }
14408
- cancel(requests) {
14409
- const set = new Set(requests);
14410
- this.queue.remove(({ result }) => set.has(result));
14531
+ };
14532
+ var DispatchQueue = class {
14533
+ /**
14534
+ * Create a new dispatch queue instance.
14535
+ */
14536
+ constructor() {
14537
+ this.clear();
14411
14538
  }
14539
+ /**
14540
+ * Clear the queue state of all event values.
14541
+ */
14412
14542
  clear() {
14413
- this.queue.remove(({ result }) => {
14414
- result.reject("Cleared");
14415
- return true;
14416
- });
14543
+ this.next = null;
14417
14544
  }
14418
- record() {
14419
- let state = [];
14420
- const recorder = {
14421
- add(query) {
14422
- state.push(query);
14423
- },
14424
- reset() {
14425
- state = [];
14426
- },
14427
- snapshot() {
14428
- return state.slice();
14429
- },
14430
- stop() {
14431
- this.recorders = this.recorders.filter((x2) => x2 !== recorder);
14432
- return state;
14545
+ /**
14546
+ * Indicate if the queue is empty.
14547
+ * @returns {boolean} True if queue is empty, false otherwise.
14548
+ */
14549
+ isEmpty() {
14550
+ return !this.next;
14551
+ }
14552
+ /**
14553
+ * Add a new value to the queue, and optionally filter the
14554
+ * current queue content in response.
14555
+ * @param {*} value The value to add.
14556
+ * @param {(value: *) => boolean} [filter] An optional filter
14557
+ * function to apply to existing queue content. If unspecified
14558
+ * or falsy, all previously queued values are removed. Otherwise,
14559
+ * the provided function is applied to all queue entries. The
14560
+ * entry is retained if the filter function returns a truthy value,
14561
+ * otherwise the entry is removed.
14562
+ */
14563
+ enqueue(value, filter) {
14564
+ const tail = { value };
14565
+ if (filter && this.next) {
14566
+ let curr = this;
14567
+ while (curr.next) {
14568
+ if (filter(curr.next.value)) {
14569
+ curr = curr.next;
14570
+ } else {
14571
+ curr.next = curr.next.next;
14572
+ }
14433
14573
  }
14434
- };
14435
- this.recorders.push(recorder);
14436
- return recorder;
14574
+ curr.next = tail;
14575
+ } else {
14576
+ this.next = tail;
14577
+ }
14578
+ }
14579
+ /**
14580
+ * Remove and return the next queued event value.
14581
+ * @returns {*} The next event value in the queue.
14582
+ */
14583
+ dequeue() {
14584
+ const { next } = this;
14585
+ this.next = next?.next;
14586
+ return next?.value;
14437
14587
  }
14438
14588
  };
14439
14589
 
14440
- // ../core/src/util/js-type.js
14441
- function jsType(type) {
14442
- switch (type) {
14443
- case "BIGINT":
14444
- case "HUGEINT":
14445
- case "INTEGER":
14446
- case "SMALLINT":
14447
- case "TINYINT":
14448
- case "UBIGINT":
14449
- case "UINTEGER":
14450
- case "USMALLINT":
14451
- case "UTINYINT":
14452
- case "DOUBLE":
14453
- case "FLOAT":
14454
- case "REAL":
14455
- return "number";
14456
- case "DATE":
14457
- case "TIMESTAMP":
14458
- case "TIMESTAMPTZ":
14459
- case "TIMESTAMP WITH TIME ZONE":
14460
- case "TIME":
14461
- case "TIMESTAMP_NS":
14462
- return "date";
14463
- case "BOOLEAN":
14464
- return "boolean";
14465
- case "VARCHAR":
14466
- case "UUID":
14467
- case "JSON":
14468
- return "string";
14469
- case "ARRAY":
14470
- case "LIST":
14471
- return "array";
14472
- case "BLOB":
14473
- case "STRUCT":
14474
- case "MAP":
14475
- case "GEOMETRY":
14476
- return "object";
14477
- default:
14478
- if (type.startsWith("DECIMAL")) {
14479
- return "number";
14480
- } else if (type.startsWith("STRUCT") || type.startsWith("MAP")) {
14481
- return "object";
14482
- } else if (type.endsWith("]")) {
14483
- return "array";
14484
- }
14485
- throw new Error(`Unsupported type: ${type}`);
14590
+ // ../core/src/util/distinct.js
14591
+ function distinct(a, b) {
14592
+ return a === b ? false : a instanceof Date && b instanceof Date ? +a !== +b : Array.isArray(a) && Array.isArray(b) ? distinctArray(a, b) : true;
14593
+ }
14594
+ function distinctArray(a, b) {
14595
+ if (a.length !== b.length) return true;
14596
+ for (let i = 0; i < a.length; ++i) {
14597
+ if (a[i] !== b[i]) return true;
14486
14598
  }
14599
+ return false;
14487
14600
  }
14488
14601
 
14489
- // ../core/src/util/convert-arrow.js
14490
- function convertArrowValue(type) {
14491
- if (DataType.isTimestamp(type)) {
14492
- return (v) => v == null ? v : new Date(v);
14602
+ // ../core/src/Param.js
14603
+ function isParam(x2) {
14604
+ return x2 instanceof Param;
14605
+ }
14606
+ var Param = class _Param extends AsyncDispatch {
14607
+ /**
14608
+ * Create a new Param instance.
14609
+ * @param {*} value The initial value of the Param.
14610
+ */
14611
+ constructor(value) {
14612
+ super();
14613
+ this._value = value;
14493
14614
  }
14494
- if (DataType.isInt(type) && type.bitWidth >= 64) {
14495
- return (v) => v == null ? v : Number(v);
14615
+ /**
14616
+ * Create a new Param instance with the given initial value.
14617
+ * @param {*} value The initial value of the Param.
14618
+ * @returns {Param} The new Param instance.
14619
+ */
14620
+ static value(value) {
14621
+ return new _Param(value);
14496
14622
  }
14497
- if (DataType.isDecimal(type)) {
14498
- const scale = 1 / Math.pow(10, type.scale);
14499
- return (v) => v == null ? v : decimalToNumber(v, scale);
14623
+ /**
14624
+ * Create a new Param instance over an array of initial values,
14625
+ * which may contain nested Params.
14626
+ * @param {*} values The initial values of the Param.
14627
+ * @returns {Param} The new Param instance.
14628
+ */
14629
+ static array(values) {
14630
+ if (values.some((v) => isParam(v))) {
14631
+ const p = new _Param();
14632
+ const update2 = () => {
14633
+ p.update(values.map((v) => isParam(v) ? v.value : v));
14634
+ };
14635
+ update2();
14636
+ values.forEach((v) => isParam(v) ? v.addEventListener("value", update2) : 0);
14637
+ return p;
14638
+ }
14639
+ return new _Param(values);
14500
14640
  }
14501
- return (v) => v;
14502
- }
14503
- var BASE32 = Array.from(
14504
- { length: 8 },
14505
- (_, i) => Math.pow(2, i * 32)
14506
- );
14507
- function decimalToNumber(v, scale) {
14508
- const n = v.length;
14509
- let x2 = 0;
14510
- if (v.signed && (v[n - 1] | 0) < 0) {
14511
- for (let i = 0; i < n; ++i) {
14512
- x2 += ~v[i] * BASE32[i];
14641
+ /**
14642
+ * The current value of the Param.
14643
+ */
14644
+ get value() {
14645
+ return this._value;
14646
+ }
14647
+ /**
14648
+ * Update the Param value
14649
+ * @param {*} value The new value of the Param.
14650
+ * @param {object} [options] The update options.
14651
+ * @param {boolean} [options.force] A boolean flag indicating if the Param
14652
+ * should emit a 'value' event even if the internal value is unchanged.
14653
+ * @returns {this} This Param instance.
14654
+ */
14655
+ update(value, { force } = {}) {
14656
+ const shouldEmit = distinct(this._value, value) || force;
14657
+ if (shouldEmit) {
14658
+ this.emit("value", value);
14659
+ } else {
14660
+ this.cancel("value");
14513
14661
  }
14514
- x2 = -(x2 + 1);
14515
- } else {
14516
- for (let i = 0; i < n; ++i) {
14517
- x2 += v[i] * BASE32[i];
14662
+ return this;
14663
+ }
14664
+ /**
14665
+ * Upon value-typed updates, sets the current value to the input value
14666
+ * immediately prior to the event value being emitted to listeners.
14667
+ * @param {string} type The event type.
14668
+ * @param {*} value The input event value.
14669
+ * @returns {*} The input event value.
14670
+ */
14671
+ willEmit(type, value) {
14672
+ if (type === "value") {
14673
+ this._value = value;
14518
14674
  }
14675
+ return value;
14519
14676
  }
14520
- return x2 * scale;
14521
- }
14522
-
14523
- // ../core/src/util/field-info.js
14524
- var Count = "count";
14525
- var Nulls = "nulls";
14526
- var Max = "max";
14527
- var Min = "min";
14528
- var Distinct = "distinct";
14529
- var statMap = {
14530
- [Count]: count,
14531
- [Distinct]: (column2) => count(column2).distinct(),
14532
- [Max]: max,
14533
- [Min]: min,
14534
- [Nulls]: (column2) => count().where(isNull(column2))
14535
14677
  };
14536
- function summarize(table2, column2, stats) {
14537
- return Query.from(table2).select(Array.from(stats, (s) => [s, statMap[s](column2)]));
14678
+
14679
+ // ../core/src/Selection.js
14680
+ function isSelection(x2) {
14681
+ return x2 instanceof Selection;
14538
14682
  }
14539
- async function queryFieldInfo(mc, fields) {
14540
- if (fields.length === 1 && `${fields[0].column}` === "*") {
14541
- return getTableInfo(mc, fields[0].table);
14542
- } else {
14543
- return (await Promise.all(fields.map((f) => getFieldInfo(mc, f)))).filter((x2) => x2);
14683
+ var Selection = class _Selection extends Param {
14684
+ /**
14685
+ * Create a new Selection instance with an
14686
+ * intersect (conjunction) resolution strategy.
14687
+ * @param {object} [options] The selection options.
14688
+ * @param {boolean} [options.cross=false] Boolean flag indicating
14689
+ * cross-filtered resolution. If true, selection clauses will not
14690
+ * be applied to the clients they are associated with.
14691
+ * @param {boolean} [options.empty=false] Boolean flag indicating if a lack
14692
+ * of clauses should correspond to an empty selection with no records. This
14693
+ * setting determines the default selection state.
14694
+ * @returns {Selection} The new Selection instance.
14695
+ */
14696
+ static intersect({ cross = false, empty = false } = {}) {
14697
+ return new _Selection(new SelectionResolver({ cross, empty }));
14544
14698
  }
14545
- }
14546
- async function getFieldInfo(mc, { table: table2, column: column2, stats }) {
14547
- const q = Query.from({ source: table2 }).select({ column: column2 }).groupby(column2.aggregate ? sql`ALL` : []);
14548
- const [desc2] = Array.from(await mc.query(Query.describe(q)));
14549
- const info = {
14550
- table: table2,
14551
- column: `${column2}`,
14552
- sqlType: desc2.column_type,
14553
- type: jsType(desc2.column_type),
14554
- nullable: desc2.null === "YES"
14555
- };
14556
- if (!(stats?.length || stats?.size)) return info;
14557
- const result = await mc.query(
14558
- summarize(table2, column2, stats),
14559
- { persist: true }
14560
- );
14561
- for (let i = 0; i < result.numCols; ++i) {
14562
- const { name } = result.schema.fields[i];
14563
- const child = result.getChildAt(i);
14564
- const convert = convertArrowValue(child.type);
14565
- info[name] = convert(child.get(0));
14699
+ /**
14700
+ * Create a new Selection instance with a
14701
+ * union (disjunction) resolution strategy.
14702
+ * @param {object} [options] The selection options.
14703
+ * @param {boolean} [options.cross=false] Boolean flag indicating
14704
+ * cross-filtered resolution. If true, selection clauses will not
14705
+ * be applied to the clients they are associated with.
14706
+ * @param {boolean} [options.empty=false] Boolean flag indicating if a lack
14707
+ * of clauses should correspond to an empty selection with no records. This
14708
+ * setting determines the default selection state.
14709
+ * @returns {Selection} The new Selection instance.
14710
+ */
14711
+ static union({ cross = false, empty = false } = {}) {
14712
+ return new _Selection(new SelectionResolver({ cross, empty, union: true }));
14566
14713
  }
14567
- return info;
14568
- }
14569
- async function getTableInfo(mc, table2) {
14570
- const result = await mc.query(`DESCRIBE ${asRelation(table2)}`);
14571
- return Array.from(result).map((desc2) => ({
14572
- table: table2,
14573
- column: desc2.column_name,
14574
- sqlType: desc2.column_type,
14575
- type: jsType(desc2.column_type),
14576
- nullable: desc2.null === "YES"
14577
- }));
14578
- }
14579
-
14580
- // ../core/src/util/void-logger.js
14581
- function voidLogger() {
14582
- return {
14583
- debug() {
14584
- },
14585
- info() {
14586
- },
14587
- log() {
14588
- },
14589
- warn() {
14590
- },
14591
- error() {
14592
- }
14593
- };
14594
- }
14595
-
14596
- // ../core/src/Coordinator.js
14597
- var _instance;
14598
- function coordinator(instance8) {
14599
- if (instance8) {
14600
- _instance = instance8;
14601
- } else if (_instance == null) {
14602
- _instance = new Coordinator();
14714
+ /**
14715
+ * Create a new Selection instance with a singular resolution strategy
14716
+ * that keeps only the most recent selection clause.
14717
+ * @param {object} [options] The selection options.
14718
+ * @param {boolean} [options.cross=false] Boolean flag indicating
14719
+ * cross-filtered resolution. If true, selection clauses will not
14720
+ * be applied to the clients they are associated with.
14721
+ * @param {boolean} [options.empty=false] Boolean flag indicating if a lack
14722
+ * of clauses should correspond to an empty selection with no records. This
14723
+ * setting determines the default selection state.
14724
+ * @returns {Selection} The new Selection instance.
14725
+ */
14726
+ static single({ cross = false, empty = false } = {}) {
14727
+ return new _Selection(new SelectionResolver({ cross, empty, single: true }));
14728
+ }
14729
+ /**
14730
+ * Create a new Selection instance with a
14731
+ * cross-filtered intersect resolution strategy.
14732
+ * @param {object} [options] The selection options.
14733
+ * @param {boolean} [options.empty=false] Boolean flag indicating if a lack
14734
+ * of clauses should correspond to an empty selection with no records. This
14735
+ * setting determines the default selection state.
14736
+ * @returns {Selection} The new Selection instance.
14737
+ */
14738
+ static crossfilter({ empty = false } = {}) {
14739
+ return new _Selection(new SelectionResolver({ cross: true, empty }));
14740
+ }
14741
+ /**
14742
+ * Create a new Selection instance.
14743
+ * @param {SelectionResolver} resolver The selection resolution
14744
+ * strategy to apply.
14745
+ */
14746
+ constructor(resolver = new SelectionResolver()) {
14747
+ super([]);
14748
+ this._resolved = this._value;
14749
+ this._resolver = resolver;
14750
+ }
14751
+ /**
14752
+ * Create a cloned copy of this Selection instance.
14753
+ * @returns {Selection} A clone of this selection.
14754
+ */
14755
+ clone() {
14756
+ const s = new _Selection(this._resolver);
14757
+ s._value = s._resolved = this._value;
14758
+ return s;
14759
+ }
14760
+ /**
14761
+ * Create a clone of this Selection with clauses corresponding
14762
+ * to the provided source removed.
14763
+ * @param {*} source The clause source to remove.
14764
+ * @returns {Selection} A cloned and updated Selection.
14765
+ */
14766
+ remove(source) {
14767
+ const s = this.clone();
14768
+ s._value = s._resolved = s._resolver.resolve(this._resolved, { source });
14769
+ s._value.active = { source };
14770
+ return s;
14771
+ }
14772
+ /**
14773
+ * The selection clause resolver.
14774
+ */
14775
+ get resolver() {
14776
+ return this._resolver;
14777
+ }
14778
+ /**
14779
+ * Indicate if this selection has a single resolution strategy.
14780
+ */
14781
+ get single() {
14782
+ return this._resolver.single;
14603
14783
  }
14604
- return _instance;
14605
- }
14606
- var Coordinator = class {
14607
- constructor(db = socketConnector(), options = {}) {
14608
- const {
14609
- logger = console,
14610
- manager = new QueryManager()
14611
- } = options;
14612
- this.manager = manager;
14613
- this.logger(logger);
14614
- this.configure(options);
14615
- this.databaseConnector(db);
14616
- this.clear();
14784
+ /**
14785
+ * The current array of selection clauses.
14786
+ */
14787
+ get clauses() {
14788
+ return super.value;
14617
14789
  }
14618
- logger(logger) {
14619
- if (arguments.length) {
14620
- this._logger = logger || voidLogger();
14621
- this.manager.logger(this._logger);
14622
- }
14623
- return this._logger;
14790
+ /**
14791
+ * The current active (most recently updated) selection clause.
14792
+ */
14793
+ get active() {
14794
+ return this.clauses.active;
14624
14795
  }
14625
14796
  /**
14626
- * Set configuration options for this coordinator.
14627
- * @param {object} [options] Configration options.
14628
- * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
14629
- * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
14630
- * @param {boolean|object} [options.indexes=true] Boolean flag to enable/disable
14631
- * automatic data cube indexes or an index options object.
14797
+ * The value corresponding to the current active selection clause.
14798
+ * This method ensures compatibility where a normal Param is expected.
14632
14799
  */
14633
- configure({ cache = true, consolidate: consolidate2 = true, indexes = true } = {}) {
14634
- this.manager.cache(cache);
14635
- this.manager.consolidate(consolidate2);
14636
- this.indexes = indexes;
14800
+ get value() {
14801
+ return this.active?.value;
14637
14802
  }
14638
- clear({ clients = true, cache = true } = {}) {
14639
- this.manager.clear();
14640
- if (clients) {
14641
- this.clients?.forEach((client) => this.disconnect(client));
14642
- this.filterGroups?.forEach((group) => group.finalize());
14643
- this.clients = /* @__PURE__ */ new Set();
14644
- this.filterGroups = /* @__PURE__ */ new Map();
14645
- }
14646
- if (cache) this.manager.cache().clear();
14803
+ /**
14804
+ * The value corresponding to a given source. Returns undefined if
14805
+ * this selection does not include a clause from this source.
14806
+ * @param {*} source The clause source to look up the value for.
14807
+ */
14808
+ valueFor(source) {
14809
+ return this.clauses.find((c) => c.source === source)?.value;
14647
14810
  }
14648
- databaseConnector(db) {
14649
- return this.manager.connector(db);
14811
+ /**
14812
+ * Emit an activate event with the given selection clause.
14813
+ * @param {*} clause The clause repesenting the potential activation.
14814
+ */
14815
+ activate(clause) {
14816
+ this.emit("activate", clause);
14650
14817
  }
14651
- // -- Query Management ----
14652
- cancel(requests) {
14653
- this.manager.cancel(requests);
14818
+ /**
14819
+ * Update the selection with a new selection clause.
14820
+ * @param {*} clause The selection clause to add.
14821
+ * @returns {this} This Selection instance.
14822
+ */
14823
+ update(clause) {
14824
+ this._resolved = this._resolver.resolve(this._resolved, clause, true);
14825
+ this._resolved.active = clause;
14826
+ return super.update(this._resolved);
14654
14827
  }
14655
- exec(query, { priority = Priority.Normal } = {}) {
14656
- query = Array.isArray(query) ? query.join(";\n") : query;
14657
- return this.manager.request({ type: "exec", query }, priority);
14828
+ /**
14829
+ * Upon value-typed updates, sets the current clause list to the
14830
+ * input value and returns the active clause value.
14831
+ * @param {string} type The event type.
14832
+ * @param {*} value The input event value.
14833
+ * @returns {*} For value-typed events, returns the active clause
14834
+ * values. Otherwise returns the input event value as-is.
14835
+ */
14836
+ willEmit(type, value) {
14837
+ if (type === "value") {
14838
+ this._value = value;
14839
+ return this.value;
14840
+ }
14841
+ return value;
14658
14842
  }
14659
- query(query, {
14660
- type = "arrow",
14661
- cache = true,
14662
- priority = Priority.Normal,
14663
- ...options
14664
- } = {}) {
14665
- return this.manager.request({ type, query, cache, options }, priority);
14843
+ /**
14844
+ * Upon value-typed updates, returns a dispatch queue filter function.
14845
+ * The return value depends on the selection resolution strategy.
14846
+ * @param {string} type The event type.
14847
+ * @param {*} value The new event value that will be enqueued.
14848
+ * @returns {(value: *) => boolean|null} For value-typed events,
14849
+ * returns a dispatch queue filter function. Otherwise returns null.
14850
+ */
14851
+ emitQueueFilter(type, value) {
14852
+ return type === "value" ? this._resolver.queueFilter(value) : null;
14666
14853
  }
14667
- prefetch(query, options = {}) {
14668
- return this.query(query, { ...options, cache: true, priority: Priority.Low });
14854
+ /**
14855
+ * Indicates if a selection clause should not be applied to a given client.
14856
+ * The return value depends on the selection resolution strategy.
14857
+ * @param {*} client The selection clause.
14858
+ * @param {*} clause The client to test.
14859
+ * @returns True if the client should be skipped, false otherwise.
14860
+ */
14861
+ skip(client, clause) {
14862
+ return this._resolver.skip(client, clause);
14669
14863
  }
14670
- createBundle(name, queries, priority = Priority.Low) {
14671
- const options = { name, queries };
14672
- return this.manager.request({ type: "create-bundle", options }, priority);
14864
+ /**
14865
+ * Return a selection query predicate for the given client.
14866
+ * @param {*} client The client whose data may be filtered.
14867
+ * @param {boolean} [noSkip=false] Disable skipping of active
14868
+ * cross-filtered sources. If set true, the source of the active
14869
+ * clause in a cross-filtered selection will not be skipped.
14870
+ * @returns {*} The query predicate for filtering client data,
14871
+ * based on the current state of this selection.
14872
+ */
14873
+ predicate(client, noSkip = false) {
14874
+ const { clauses } = this;
14875
+ const active = noSkip ? null : clauses.active;
14876
+ return this._resolver.predicate(clauses, active, client);
14673
14877
  }
14674
- loadBundle(name, priority = Priority.High) {
14675
- const options = { name };
14676
- return this.manager.request({ type: "load-bundle", options }, priority);
14878
+ };
14879
+ var SelectionResolver = class {
14880
+ /**
14881
+ * Create a new selection resolved instance.
14882
+ * @param {object} [options] The resolution strategy options.
14883
+ * @param {boolean} [options.union=false] Boolean flag to indicate a union strategy.
14884
+ * If false, an intersection strategy is used.
14885
+ * @param {boolean} [options.cross=false] Boolean flag to indicate cross-filtering.
14886
+ * @param {boolean} [options.single=false] Boolean flag to indicate single clauses only.
14887
+ * @param {boolean} [options.empty=false] Boolean flag indicating if a lack
14888
+ * of clauses should correspond to an empty selection with no records. This
14889
+ * setting determines the default selection state.
14890
+ */
14891
+ constructor({ union, cross, single, empty } = {}) {
14892
+ this.union = !!union;
14893
+ this.cross = !!cross;
14894
+ this.single = !!single;
14895
+ this.empty = !!empty;
14677
14896
  }
14678
- // -- Client Management ----
14679
- updateClient(client, query, priority = Priority.Normal) {
14680
- client.queryPending();
14681
- return this.query(query, { priority }).then(
14682
- (data) => client.queryResult(data).update(),
14683
- (err) => {
14684
- client.queryError(err);
14685
- this._logger.error(err);
14686
- }
14687
- );
14897
+ /**
14898
+ * Resolve a list of selection clauses according to the resolution strategy.
14899
+ * @param {*[]} clauseList An array of selection clauses.
14900
+ * @param {*} clause A new selection clause to add.
14901
+ * @returns {*[]} An updated array of selection clauses.
14902
+ */
14903
+ resolve(clauseList, clause, reset = false) {
14904
+ const { source, predicate } = clause;
14905
+ const filtered = clauseList.filter((c) => source !== c.source);
14906
+ const clauses = this.single ? [] : filtered;
14907
+ if (this.single && reset) filtered.forEach((c) => c.source?.reset?.());
14908
+ if (predicate) clauses.push(clause);
14909
+ return clauses;
14688
14910
  }
14689
- requestQuery(client, query) {
14690
- this.filterGroups.get(client.filterBy)?.reset();
14691
- return query ? this.updateClient(client, query) : client.update();
14911
+ /**
14912
+ * Indicates if a selection clause should not be applied to a given client.
14913
+ * The return value depends on the resolution strategy.
14914
+ * @param {*} client The selection clause.
14915
+ * @param {*} clause The client to test.
14916
+ * @returns True if the client should be skipped, false otherwise.
14917
+ */
14918
+ skip(client, clause) {
14919
+ return this.cross && clause?.clients?.has(client);
14692
14920
  }
14693
14921
  /**
14694
- * Connect a client to the coordinator.
14695
- * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
14922
+ * Return a selection query predicate for the given client.
14923
+ * @param {*[]} clauseList An array of selection clauses.
14924
+ * @param {*} active The current active selection clause.
14925
+ * @param {*} client The client whose data may be filtered.
14926
+ * @returns {*} The query predicate for filtering client data,
14927
+ * based on the current state of this selection.
14696
14928
  */
14697
- async connect(client) {
14698
- const { clients, filterGroups, indexes } = this;
14699
- if (clients.has(client)) {
14700
- throw new Error("Client already connected.");
14701
- }
14702
- clients.add(client);
14703
- client.coordinator = this;
14704
- const fields = client.fields();
14705
- if (fields?.length) {
14706
- client.fieldInfo(await queryFieldInfo(this, fields));
14707
- }
14708
- const filter = client.filterBy;
14709
- if (filter) {
14710
- if (filterGroups.has(filter)) {
14711
- filterGroups.get(filter).add(client);
14712
- } else {
14713
- const group = new FilterGroup(this, filter, indexes);
14714
- filterGroups.set(filter, group.add(client));
14715
- }
14929
+ predicate(clauseList, active, client) {
14930
+ const { empty, union } = this;
14931
+ if (empty && !clauseList.length) {
14932
+ return ["FALSE"];
14716
14933
  }
14717
- client.requestQuery();
14934
+ if (this.skip(client, active)) return void 0;
14935
+ const predicates = clauseList.filter((clause) => !this.skip(client, clause)).map((clause) => clause.predicate);
14936
+ return union && predicates.length > 1 ? or(predicates) : predicates;
14718
14937
  }
14719
14938
  /**
14720
- * Disconnect a client from the coordinator.
14721
- *
14722
- * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
14939
+ * Returns a filter function for queued selection updates.
14940
+ * @param {*} value The new event value that will be enqueued.
14941
+ * @returns {(value: *) => boolean|null} A dispatch queue filter
14942
+ * function, or null if all unemitted event values should be filtered.
14723
14943
  */
14724
- disconnect(client) {
14725
- const { clients, filterGroups } = this;
14726
- if (!clients.has(client)) return;
14727
- clients.delete(client);
14728
- filterGroups.get(client.filterBy)?.remove(client);
14729
- client.coordinator = null;
14944
+ queueFilter(value) {
14945
+ if (this.cross) {
14946
+ const source = value.active?.source;
14947
+ return (clauses) => clauses.active?.source !== source;
14948
+ }
14949
+ return null;
14730
14950
  }
14731
14951
  };
14732
14952
 
14733
14953
  // ../core/src/SelectionClause.js
14734
- function point(field, value, { source, clients = void 0 }) {
14954
+ function clausePoint(field, value, {
14955
+ source,
14956
+ clients = source ? /* @__PURE__ */ new Set([source]) : void 0
14957
+ }) {
14735
14958
  const predicate = value !== void 0 ? isNotDistinct(field, literal(value)) : null;
14736
14959
  return {
14737
14960
  meta: { type: "point" },
@@ -14741,19 +14964,39 @@ function point(field, value, { source, clients = void 0 }) {
14741
14964
  predicate
14742
14965
  };
14743
14966
  }
14744
- function interval(field, value, {
14967
+ function clausePoints(fields, value, {
14968
+ source,
14969
+ clients = source ? /* @__PURE__ */ new Set([source]) : void 0
14970
+ }) {
14971
+ let predicate = null;
14972
+ if (value) {
14973
+ const clauses = value.map((vals) => {
14974
+ const list = vals.map((v, i) => isNotDistinct(fields[i], literal(v)));
14975
+ return list.length > 1 ? and(list) : list[0];
14976
+ });
14977
+ predicate = clauses.length > 1 ? or(clauses) : clauses[0];
14978
+ }
14979
+ return {
14980
+ meta: { type: "point" },
14981
+ source,
14982
+ clients,
14983
+ value,
14984
+ predicate
14985
+ };
14986
+ }
14987
+ function clauseInterval(field, value, {
14745
14988
  source,
14746
- clients,
14989
+ clients = source ? /* @__PURE__ */ new Set([source]) : void 0,
14747
14990
  bin,
14748
14991
  scale,
14749
14992
  pixelSize = 1
14750
14993
  }) {
14751
14994
  const predicate = value != null ? isBetween(field, value) : null;
14752
- const meta = { type: "interval", scales: [scale], bin, pixelSize };
14995
+ const meta = { type: "interval", scales: scale && [scale], bin, pixelSize };
14753
14996
  return { meta, source, clients, value, predicate };
14754
14997
  }
14755
14998
  var MATCH_METHODS = { contains, prefix, suffix, regexp: regexp_matches };
14756
- function match(field, value, {
14999
+ function clauseMatch(field, value, {
14757
15000
  source,
14758
15001
  clients = void 0,
14759
15002
  method = "contains"
@@ -14764,6 +15007,39 @@ function match(field, value, {
14764
15007
  return { meta, source, clients, value, predicate };
14765
15008
  }
14766
15009
 
15010
+ // ../core/src/util/to-data-columns.js
15011
+ function toDataColumns(data) {
15012
+ return isArrowTable(data) ? arrowToColumns(data) : arrayToColumns(data);
15013
+ }
15014
+ function arrowToColumns(data) {
15015
+ const { numRows, numCols, schema: { fields } } = data;
15016
+ const columns = {};
15017
+ for (let col = 0; col < numCols; ++col) {
15018
+ const name = fields[col].name;
15019
+ if (columns[name]) {
15020
+ console.warn(`Redundant column name "${name}". Skipping...`);
15021
+ } else {
15022
+ columns[name] = convertArrowColumn(data.getChildAt(col));
15023
+ }
15024
+ }
15025
+ return { numRows, columns };
15026
+ }
15027
+ function arrayToColumns(data) {
15028
+ const numRows = data.length;
15029
+ if (typeof data[0] === "object") {
15030
+ const names = numRows ? Object.keys(data[0]) : [];
15031
+ const columns = {};
15032
+ if (names.length > 0) {
15033
+ names.forEach((name) => {
15034
+ columns[name] = data.map((d) => d[name]);
15035
+ });
15036
+ }
15037
+ return { numRows, columns };
15038
+ } else {
15039
+ return { numRows, values: data };
15040
+ }
15041
+ }
15042
+
14767
15043
  // src/input.js
14768
15044
  function input(InputClass, options) {
14769
15045
  const input2 = new InputClass(options);
@@ -14873,7 +15149,7 @@ var Menu = class extends MosaicClient {
14873
15149
  const { selection, field } = this;
14874
15150
  if (isSelection(selection)) {
14875
15151
  if (value === "") value = void 0;
14876
- const clause = point(field, value, { source: this });
15152
+ const clause = clausePoint(field, value, { source: this });
14877
15153
  selection.update(clause);
14878
15154
  } else if (isParam(selection)) {
14879
15155
  selection.update(value);
@@ -14984,7 +15260,7 @@ var Search = class extends MosaicClient {
14984
15260
  publish(value) {
14985
15261
  const { selection, field, type } = this;
14986
15262
  if (isSelection(selection)) {
14987
- const clause = match(field, value, { source: this, method: type });
15263
+ const clause = clauseMatch(field, value, { source: this, method: type });
14988
15264
  selection.update(clause);
14989
15265
  } else if (isParam(selection)) {
14990
15266
  selection.update(value);
@@ -15140,14 +15416,14 @@ var Slider = class extends MosaicClient {
15140
15416
  if (isSelection(selection)) {
15141
15417
  if (selectionType === "interval") {
15142
15418
  const domain = [this.min ?? 0, value];
15143
- selection.update(interval(field, domain, {
15419
+ selection.update(clauseInterval(field, domain, {
15144
15420
  source: this,
15145
15421
  bin: "ceil",
15146
15422
  scale: { type: "identity", domain },
15147
15423
  pixelSize: this.step
15148
15424
  }));
15149
15425
  } else {
15150
- selection.update(point(field, value, { source: this }));
15426
+ selection.update(clausePoint(field, value, { source: this }));
15151
15427
  }
15152
15428
  } else if (isParam(this.selection)) {
15153
15429
  selection.update(value);
@@ -15209,7 +15485,8 @@ var Table2 = class extends MosaicClient {
15209
15485
  width,
15210
15486
  maxWidth,
15211
15487
  height = 500,
15212
- rowBatch = 100
15488
+ rowBatch = 100,
15489
+ as
15213
15490
  } = {}) {
15214
15491
  super(filterBy);
15215
15492
  this.id = `table-${++_id3}`;
@@ -15221,6 +15498,8 @@ var Table2 = class extends MosaicClient {
15221
15498
  this.offset = 0;
15222
15499
  this.limit = +rowBatch;
15223
15500
  this.pending = false;
15501
+ this.selection = as;
15502
+ this.currentRow = -1;
15224
15503
  this.sortHeader = null;
15225
15504
  this.sortColumn = null;
15226
15505
  this.sortDesc = false;
@@ -15249,9 +15528,31 @@ var Table2 = class extends MosaicClient {
15249
15528
  this.tbl.appendChild(this.head);
15250
15529
  this.body = document.createElement("tbody");
15251
15530
  this.tbl.appendChild(this.body);
15531
+ if (this.selection) {
15532
+ this.body.addEventListener("pointerover", (evt) => {
15533
+ const row = resolveRow(evt.target);
15534
+ if (row > -1 && row !== this.currentRow) {
15535
+ this.currentRow = row;
15536
+ this.selection.update(this.clause([row]));
15537
+ }
15538
+ });
15539
+ this.body.addEventListener("pointerleave", () => {
15540
+ this.currentRow = -1;
15541
+ this.selection.update(this.clause());
15542
+ });
15543
+ }
15252
15544
  this.style = document.createElement("style");
15253
15545
  this.element.appendChild(this.style);
15254
15546
  }
15547
+ clause(rows = []) {
15548
+ const { data, limit, schema } = this;
15549
+ const fields = schema.map((s) => s.column);
15550
+ const values = rows.map((row) => {
15551
+ const { columns } = data[~~(row / limit)];
15552
+ return fields.map((f) => columns[f][row % limit]);
15553
+ });
15554
+ return clausePoints(fields, values, { source: this });
15555
+ }
15255
15556
  requestData(offset = 0) {
15256
15557
  this.offset = offset;
15257
15558
  const query = this.query(this.filterBy?.predicate(this));
@@ -15289,27 +15590,32 @@ var Table2 = class extends MosaicClient {
15289
15590
  queryResult(data) {
15290
15591
  if (!this.pending) {
15291
15592
  this.loaded = false;
15593
+ this.data = [];
15292
15594
  this.body.replaceChildren();
15595
+ this.offset = 0;
15293
15596
  }
15294
- this.data = data;
15597
+ this.data.push(toDataColumns(data));
15295
15598
  return this;
15296
15599
  }
15297
15600
  update() {
15298
15601
  const { body, formats, data, schema, limit } = this;
15299
15602
  const nf = schema.length;
15300
- let count2 = 0;
15301
- for (const row of data) {
15302
- ++count2;
15603
+ const n = data.length - 1;
15604
+ const rowCount = limit * n;
15605
+ const { numRows, columns } = data[n];
15606
+ const cols = schema.map((s) => columns[s.column]);
15607
+ for (let i = 0; i < numRows; ++i) {
15303
15608
  const tr = document.createElement("tr");
15304
- for (let i = 0; i < nf; ++i) {
15305
- const value = row[schema[i].column];
15609
+ Object.assign(tr, { __row__: rowCount + i });
15610
+ for (let j = 0; j < nf; ++j) {
15611
+ const value = cols[j][i];
15306
15612
  const td = document.createElement("td");
15307
- td.innerText = value == null ? "" : formats[i](value);
15613
+ td.innerText = value == null ? "" : formats[j](value);
15308
15614
  tr.appendChild(td);
15309
15615
  }
15310
15616
  body.appendChild(tr);
15311
15617
  }
15312
- if (count2 < limit) {
15618
+ if (numRows < limit) {
15313
15619
  this.loaded = true;
15314
15620
  }
15315
15621
  this.pending = false;
@@ -15336,6 +15642,10 @@ var Table2 = class extends MosaicClient {
15336
15642
  this.requestData();
15337
15643
  }
15338
15644
  };
15645
+ function resolveRow(element) {
15646
+ const p = element.parentElement;
15647
+ return Object.hasOwn(p, "__row__") ? +p.__row__ : -1;
15648
+ }
15339
15649
  function formatof(base = {}, schema, locale) {
15340
15650
  return schema.map(({ column: column2, type }) => {
15341
15651
  if (column2 in base) {