@uwdata/mosaic-inputs 0.1.0 → 0.3.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.
@@ -328,8 +328,8 @@ function joinUint8Arrays(chunks, size) {
328
328
  }
329
329
  return [buffer || new Uint8Array(0), result.slice(index), byteLength - (buffer ? buffer.byteLength : 0)];
330
330
  }
331
- function toArrayBufferView(ArrayBufferViewCtor, input) {
332
- let value = isIteratorResult(input) ? input.value : input;
331
+ function toArrayBufferView(ArrayBufferViewCtor, input2) {
332
+ let value = isIteratorResult(input2) ? input2.value : input2;
333
333
  if (value instanceof ArrayBufferViewCtor) {
334
334
  if (ArrayBufferViewCtor === Uint8Array) {
335
335
  return new ArrayBufferViewCtor(value.buffer, value.byteOffset, value.byteLength);
@@ -353,17 +353,17 @@ function toArrayBufferView(ArrayBufferViewCtor, input) {
353
353
  }
354
354
  return !ArrayBuffer.isView(value) ? ArrayBufferViewCtor.from(value) : value.byteLength <= 0 ? new ArrayBufferViewCtor(0) : new ArrayBufferViewCtor(value.buffer, value.byteOffset, value.byteLength / ArrayBufferViewCtor.BYTES_PER_ELEMENT);
355
355
  }
356
- var toInt8Array = (input) => toArrayBufferView(Int8Array, input);
357
- var toInt16Array = (input) => toArrayBufferView(Int16Array, input);
358
- var toInt32Array = (input) => toArrayBufferView(Int32Array, input);
359
- var toBigInt64Array = (input) => toArrayBufferView(BigInt64ArrayCtor, input);
360
- var toUint8Array = (input) => toArrayBufferView(Uint8Array, input);
361
- var toUint16Array = (input) => toArrayBufferView(Uint16Array, input);
362
- var toUint32Array = (input) => toArrayBufferView(Uint32Array, input);
363
- var toBigUint64Array = (input) => toArrayBufferView(BigUint64ArrayCtor, input);
364
- var toFloat32Array = (input) => toArrayBufferView(Float32Array, input);
365
- var toFloat64Array = (input) => toArrayBufferView(Float64Array, input);
366
- var toUint8ClampedArray = (input) => toArrayBufferView(Uint8ClampedArray, input);
356
+ var toInt8Array = (input2) => toArrayBufferView(Int8Array, input2);
357
+ var toInt16Array = (input2) => toArrayBufferView(Int16Array, input2);
358
+ var toInt32Array = (input2) => toArrayBufferView(Int32Array, input2);
359
+ var toBigInt64Array = (input2) => toArrayBufferView(BigInt64ArrayCtor, input2);
360
+ var toUint8Array = (input2) => toArrayBufferView(Uint8Array, input2);
361
+ var toUint16Array = (input2) => toArrayBufferView(Uint16Array, input2);
362
+ var toUint32Array = (input2) => toArrayBufferView(Uint32Array, input2);
363
+ var toBigUint64Array = (input2) => toArrayBufferView(BigUint64ArrayCtor, input2);
364
+ var toFloat32Array = (input2) => toArrayBufferView(Float32Array, input2);
365
+ var toFloat64Array = (input2) => toArrayBufferView(Float64Array, input2);
366
+ var toUint8ClampedArray = (input2) => toArrayBufferView(Uint8ClampedArray, input2);
367
367
  var pump = (iterator) => {
368
368
  iterator.next();
369
369
  return iterator;
@@ -381,15 +381,15 @@ function* toArrayBufferViewIterator(ArrayCtor, source) {
381
381
  }(buffers[Symbol.iterator]()));
382
382
  return new ArrayCtor();
383
383
  }
384
- var toInt8ArrayIterator = (input) => toArrayBufferViewIterator(Int8Array, input);
385
- var toInt16ArrayIterator = (input) => toArrayBufferViewIterator(Int16Array, input);
386
- var toInt32ArrayIterator = (input) => toArrayBufferViewIterator(Int32Array, input);
387
- var toUint8ArrayIterator = (input) => toArrayBufferViewIterator(Uint8Array, input);
388
- var toUint16ArrayIterator = (input) => toArrayBufferViewIterator(Uint16Array, input);
389
- var toUint32ArrayIterator = (input) => toArrayBufferViewIterator(Uint32Array, input);
390
- var toFloat32ArrayIterator = (input) => toArrayBufferViewIterator(Float32Array, input);
391
- var toFloat64ArrayIterator = (input) => toArrayBufferViewIterator(Float64Array, input);
392
- var toUint8ClampedArrayIterator = (input) => toArrayBufferViewIterator(Uint8ClampedArray, input);
384
+ var toInt8ArrayIterator = (input2) => toArrayBufferViewIterator(Int8Array, input2);
385
+ var toInt16ArrayIterator = (input2) => toArrayBufferViewIterator(Int16Array, input2);
386
+ var toInt32ArrayIterator = (input2) => toArrayBufferViewIterator(Int32Array, input2);
387
+ var toUint8ArrayIterator = (input2) => toArrayBufferViewIterator(Uint8Array, input2);
388
+ var toUint16ArrayIterator = (input2) => toArrayBufferViewIterator(Uint16Array, input2);
389
+ var toUint32ArrayIterator = (input2) => toArrayBufferViewIterator(Uint32Array, input2);
390
+ var toFloat32ArrayIterator = (input2) => toArrayBufferViewIterator(Float32Array, input2);
391
+ var toFloat64ArrayIterator = (input2) => toArrayBufferViewIterator(Float64Array, input2);
392
+ var toUint8ClampedArrayIterator = (input2) => toArrayBufferViewIterator(Uint8ClampedArray, input2);
393
393
  function toArrayBufferViewAsyncIterator(ArrayCtor, source) {
394
394
  return __asyncGenerator(this, arguments, function* toArrayBufferViewAsyncIterator_1() {
395
395
  if (isPromise(source)) {
@@ -425,15 +425,15 @@ function toArrayBufferViewAsyncIterator(ArrayCtor, source) {
425
425
  return yield __await(new ArrayCtor());
426
426
  });
427
427
  }
428
- var toInt8ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Int8Array, input);
429
- var toInt16ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Int16Array, input);
430
- var toInt32ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Int32Array, input);
431
- var toUint8ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Uint8Array, input);
432
- var toUint16ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Uint16Array, input);
433
- var toUint32ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Uint32Array, input);
434
- var toFloat32ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Float32Array, input);
435
- var toFloat64ArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Float64Array, input);
436
- var toUint8ClampedArrayAsyncIterator = (input) => toArrayBufferViewAsyncIterator(Uint8ClampedArray, input);
428
+ var toInt8ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Int8Array, input2);
429
+ var toInt16ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Int16Array, input2);
430
+ var toInt32ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Int32Array, input2);
431
+ var toUint8ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Uint8Array, input2);
432
+ var toUint16ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Uint16Array, input2);
433
+ var toUint32ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Uint32Array, input2);
434
+ var toFloat32ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Float32Array, input2);
435
+ var toFloat64ArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Float64Array, input2);
436
+ var toUint8ClampedArrayAsyncIterator = (input2) => toArrayBufferViewAsyncIterator(Uint8ClampedArray, input2);
437
437
  function rebaseValueOffsets(offset, length2, valueOffsets) {
438
438
  if (offset !== 0) {
439
439
  valueOffsets = valueOffsets.slice(0, length2 + 1);
@@ -2621,31 +2621,31 @@ function clampRange(source, begin, end, then) {
2621
2621
  return then ? then(source, lhs, rhs) : [lhs, rhs];
2622
2622
  }
2623
2623
  var isNaNFast = (value) => value !== value;
2624
- function createElementComparator(search) {
2625
- const typeofSearch = typeof search;
2626
- if (typeofSearch !== "object" || search === null) {
2627
- if (isNaNFast(search)) {
2624
+ function createElementComparator(search2) {
2625
+ const typeofSearch = typeof search2;
2626
+ if (typeofSearch !== "object" || search2 === null) {
2627
+ if (isNaNFast(search2)) {
2628
2628
  return isNaNFast;
2629
2629
  }
2630
- return (value) => value === search;
2630
+ return (value) => value === search2;
2631
2631
  }
2632
- if (search instanceof Date) {
2633
- const valueOfSearch = search.valueOf();
2632
+ if (search2 instanceof Date) {
2633
+ const valueOfSearch = search2.valueOf();
2634
2634
  return (value) => value instanceof Date ? value.valueOf() === valueOfSearch : false;
2635
2635
  }
2636
- if (ArrayBuffer.isView(search)) {
2637
- return (value) => value ? compareArrayLike(search, value) : false;
2636
+ if (ArrayBuffer.isView(search2)) {
2637
+ return (value) => value ? compareArrayLike(search2, value) : false;
2638
2638
  }
2639
- if (search instanceof Map) {
2640
- return createMapComparator(search);
2639
+ if (search2 instanceof Map) {
2640
+ return createMapComparator(search2);
2641
2641
  }
2642
- if (Array.isArray(search)) {
2643
- return createArrayLikeComparator(search);
2642
+ if (Array.isArray(search2)) {
2643
+ return createArrayLikeComparator(search2);
2644
2644
  }
2645
- if (search instanceof Vector) {
2646
- return createVectorComparator(search);
2645
+ if (search2 instanceof Vector) {
2646
+ return createVectorComparator(search2);
2647
2647
  }
2648
- return createObjectComparator(search, true);
2648
+ return createObjectComparator(search2, true);
2649
2649
  }
2650
2650
  function createArrayLikeComparator(lhs) {
2651
2651
  const comparators = [];
@@ -3470,21 +3470,21 @@ var getListByteLength = ({ valueOffsets, stride, children }, index) => {
3470
3470
  const child = children[0];
3471
3471
  const { [index * stride]: start } = valueOffsets;
3472
3472
  const { [index * stride + 1]: end } = valueOffsets;
3473
- const visit = instance5.getVisitFn(child.type);
3473
+ const visit2 = instance5.getVisitFn(child.type);
3474
3474
  const slice = child.slice(start, end - start);
3475
3475
  let size = 8;
3476
3476
  for (let idx = -1, len = end - start; ++idx < len; ) {
3477
- size += visit(slice, idx);
3477
+ size += visit2(slice, idx);
3478
3478
  }
3479
3479
  return size;
3480
3480
  };
3481
3481
  var getFixedSizeListByteLength = ({ stride, children }, index) => {
3482
3482
  const child = children[0];
3483
3483
  const slice = child.slice(index * stride, stride);
3484
- const visit = instance5.getVisitFn(child.type);
3484
+ const visit2 = instance5.getVisitFn(child.type);
3485
3485
  let size = 0;
3486
3486
  for (let idx = -1, len = slice.length; ++idx < len; ) {
3487
- size += visit(slice, idx);
3487
+ size += visit2(slice, idx);
3488
3488
  }
3489
3489
  return size;
3490
3490
  };
@@ -3512,9 +3512,9 @@ var _a2;
3512
3512
  var visitorsByTypeId = {};
3513
3513
  var vectorPrototypesByTypeId = {};
3514
3514
  var Vector = class {
3515
- constructor(input) {
3515
+ constructor(input2) {
3516
3516
  var _b2, _c2, _d2;
3517
- const data = input[0] instanceof Vector ? input.flatMap((x) => x.data) : input;
3517
+ const data = input2[0] instanceof Vector ? input2.flatMap((x) => x.data) : input2;
3518
3518
  if (data.length === 0 || data.some((x) => !(x instanceof Data))) {
3519
3519
  throw new TypeError("Vector constructor expects an Array of Data instances.");
3520
3520
  }
@@ -4958,8 +4958,8 @@ var Builder2 = class {
4958
4958
  * This checks a required field has been set in a given table that has
4959
4959
  * just been constructed.
4960
4960
  */
4961
- requiredField(table, field) {
4962
- const table_start = this.bb.capacity() - table;
4961
+ requiredField(table2, field) {
4962
+ const table_start = this.bb.capacity() - table2;
4963
4963
  const vtable_start = table_start - this.bb.readInt32(table_start);
4964
4964
  const ok = this.bb.readInt16(vtable_start + field) != 0;
4965
4965
  if (!ok) {
@@ -10903,13 +10903,13 @@ var RecordBatchWriter = class extends ReadableInterop {
10903
10903
  toUint8Array(sync = false) {
10904
10904
  return this._sink.toUint8Array(sync);
10905
10905
  }
10906
- writeAll(input) {
10907
- if (isPromise(input)) {
10908
- return input.then((x) => this.writeAll(x));
10909
- } else if (isAsyncIterable(input)) {
10910
- return writeAllAsync(this, input);
10906
+ writeAll(input2) {
10907
+ if (isPromise(input2)) {
10908
+ return input2.then((x) => this.writeAll(x));
10909
+ } else if (isAsyncIterable(input2)) {
10910
+ return writeAllAsync(this, input2);
10911
10911
  }
10912
- return writeAll(this, input);
10912
+ return writeAll(this, input2);
10913
10913
  }
10914
10914
  get closed() {
10915
10915
  return this._sink.closed;
@@ -11076,26 +11076,26 @@ var RecordBatchWriter = class extends ReadableInterop {
11076
11076
  };
11077
11077
  var RecordBatchStreamWriter = class extends RecordBatchWriter {
11078
11078
  /** @nocollapse */
11079
- static writeAll(input, options) {
11079
+ static writeAll(input2, options) {
11080
11080
  const writer = new RecordBatchStreamWriter(options);
11081
- if (isPromise(input)) {
11082
- return input.then((x) => writer.writeAll(x));
11083
- } else if (isAsyncIterable(input)) {
11084
- return writeAllAsync(writer, input);
11081
+ if (isPromise(input2)) {
11082
+ return input2.then((x) => writer.writeAll(x));
11083
+ } else if (isAsyncIterable(input2)) {
11084
+ return writeAllAsync(writer, input2);
11085
11085
  }
11086
- return writeAll(writer, input);
11086
+ return writeAll(writer, input2);
11087
11087
  }
11088
11088
  };
11089
11089
  var RecordBatchFileWriter = class extends RecordBatchWriter {
11090
11090
  /** @nocollapse */
11091
- static writeAll(input) {
11091
+ static writeAll(input2) {
11092
11092
  const writer = new RecordBatchFileWriter();
11093
- if (isPromise(input)) {
11094
- return input.then((x) => writer.writeAll(x));
11095
- } else if (isAsyncIterable(input)) {
11096
- return writeAllAsync(writer, input);
11093
+ if (isPromise(input2)) {
11094
+ return input2.then((x) => writer.writeAll(x));
11095
+ } else if (isAsyncIterable(input2)) {
11096
+ return writeAllAsync(writer, input2);
11097
11097
  }
11098
- return writeAll(writer, input);
11098
+ return writeAll(writer, input2);
11099
11099
  }
11100
11100
  constructor() {
11101
11101
  super();
@@ -11110,11 +11110,11 @@ var RecordBatchFileWriter = class extends RecordBatchWriter {
11110
11110
  return super._writeFooter(schema)._write(buffer)._write(Int32Array.of(buffer.byteLength))._writeMagic();
11111
11111
  }
11112
11112
  };
11113
- function writeAll(writer, input) {
11114
- let chunks = input;
11115
- if (input instanceof Table) {
11116
- chunks = input.batches;
11117
- writer.reset(void 0, input.schema);
11113
+ function writeAll(writer, input2) {
11114
+ let chunks = input2;
11115
+ if (input2 instanceof Table) {
11116
+ chunks = input2.batches;
11117
+ writer.reset(void 0, input2.schema);
11118
11118
  }
11119
11119
  for (const batch of chunks) {
11120
11120
  writer.write(batch);
@@ -11389,8 +11389,8 @@ function recordBatchWriterThroughDOMStream(writableStrategy, readableStrategy) {
11389
11389
  }
11390
11390
 
11391
11391
  // ../../node_modules/apache-arrow/ipc/serialization.mjs
11392
- function tableFromIPC(input) {
11393
- const reader = RecordBatchReader.from(input);
11392
+ function tableFromIPC(input2) {
11393
+ const reader = RecordBatchReader.from(input2);
11394
11394
  if (isPromise(reader)) {
11395
11395
  return reader.then((reader2) => tableFromIPC(reader2));
11396
11396
  }
@@ -11417,8 +11417,8 @@ RecordBatchWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
11417
11417
  RecordBatchFileWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
11418
11418
  RecordBatchStreamWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
11419
11419
 
11420
- // ../core/src/clients/socket.js
11421
- function socketClient(uri = "ws://localhost:3000/") {
11420
+ // ../core/src/connectors/socket.js
11421
+ function socketConnector(uri = "ws://localhost:3000/") {
11422
11422
  const queue = [];
11423
11423
  let connected = false;
11424
11424
  let request = null;
@@ -11537,22 +11537,35 @@ function jsType(type) {
11537
11537
 
11538
11538
  // ../sql/src/ref.js
11539
11539
  var Ref = class {
11540
- constructor(table, column2) {
11541
- if (table)
11542
- this.table = String(table);
11540
+ /**
11541
+ * Create a new Ref instance.
11542
+ * @param {string|Ref|null} table The table name.
11543
+ * @param {string|null} column The column name.
11544
+ */
11545
+ constructor(table2, column2) {
11546
+ if (table2)
11547
+ this.table = String(table2);
11543
11548
  if (column2)
11544
11549
  this.column = column2;
11545
11550
  }
11551
+ /**
11552
+ * Get the list of referenced columns. Either a single element array
11553
+ * if column is non-null, otherwise an empty array.
11554
+ */
11546
11555
  get columns() {
11547
11556
  return this.column ? [this.column] : [];
11548
11557
  }
11558
+ /**
11559
+ * Generate a SQL string for this reference.
11560
+ * @returns {string} The SQL string.
11561
+ */
11549
11562
  toString() {
11550
- const { table, column: column2 } = this;
11563
+ const { table: table2, column: column2 } = this;
11551
11564
  if (column2) {
11552
- const col = column2 === "*" ? column2 : `"${column2}"`;
11553
- return (table ? `"${table}".` : "") + col;
11565
+ const col = column2.startsWith("*") ? column2 : `"${column2}"`;
11566
+ return `${table2 ? `"${table2}".` : ""}${col}`;
11554
11567
  } else {
11555
- return `"${table}"`;
11568
+ return table2 ? `"${table2}"` : "NULL";
11556
11569
  }
11557
11570
  }
11558
11571
  };
@@ -11568,25 +11581,34 @@ function asRelation(value) {
11568
11581
  function relation(name) {
11569
11582
  return new Ref(name);
11570
11583
  }
11571
- function column(table, column2) {
11572
- return arguments.length === 1 ? new Ref(null, table) : new Ref(table, column2);
11584
+ function column(table2, column2) {
11585
+ if (arguments.length === 1) {
11586
+ column2 = table2;
11587
+ table2 = null;
11588
+ }
11589
+ return new Ref(table2, column2);
11573
11590
  }
11574
11591
 
11575
11592
  // ../sql/src/to-sql.js
11576
- function toSQL(value) {
11577
- return typeof value === "string" ? `"${value}"` : literalToSQL(value);
11578
- }
11579
11593
  function literalToSQL(value) {
11580
11594
  switch (typeof value) {
11581
11595
  case "boolean":
11582
11596
  return value ? "TRUE" : "FALSE";
11583
11597
  case "string":
11584
11598
  return `'${value}'`;
11599
+ case "number":
11600
+ return Number.isFinite(value) ? String(value) : "NULL";
11585
11601
  default:
11586
11602
  if (value == null) {
11587
11603
  return "NULL";
11588
11604
  } else if (value instanceof Date) {
11589
- return `MAKE_DATE(${value.getUTCFullYear()}, ${value.getUTCMonth() + 1}, ${value.getUTCDate()})`;
11605
+ const ts = +value;
11606
+ if (Number.isNaN(ts))
11607
+ return "NULL";
11608
+ const y = value.getUTCFullYear();
11609
+ const m = value.getUTCMonth();
11610
+ const d = value.getUTCDate();
11611
+ return ts === Date.UTC(y, m, d) ? `MAKE_DATE(${y}, ${m + 1}, ${d})` : `EPOCH_MS(${ts})`;
11590
11612
  } else if (value instanceof RegExp) {
11591
11613
  return `'${value.source}'`;
11592
11614
  } else {
@@ -11596,263 +11618,406 @@ function literalToSQL(value) {
11596
11618
  }
11597
11619
 
11598
11620
  // ../sql/src/expression.js
11599
- function isExpression(e) {
11600
- return e instanceof SQLExpression;
11621
+ var isParamLike = (value) => typeof value?.addEventListener === "function";
11622
+ function isSQLExpression(value) {
11623
+ return value instanceof SQLExpression;
11601
11624
  }
11602
11625
  var SQLExpression = class {
11603
- constructor(sql2, columns, label) {
11604
- this.expr = sql2;
11605
- this.label = label;
11606
- this.columns = columns || [];
11607
- }
11608
- toString() {
11609
- return `${this.expr}`;
11610
- }
11611
- };
11612
- function expr(sql2, columns, label) {
11613
- return new SQLExpression(sql2, columns, label);
11614
- }
11615
- function desc(e) {
11616
- return Object.assign(
11617
- expr(`${asColumn(e)} DESC NULLS LAST`, e?.columns, e?.label),
11618
- { desc: true }
11619
- );
11620
- }
11621
- function transform(func2, label) {
11622
- return (value) => expr(func2(value), asColumn(value).columns, label);
11623
- }
11624
-
11625
- // ../sql/src/literal.js
11626
- var Literal = class {
11627
- constructor(value) {
11628
- this.value = value;
11629
- }
11630
- toString() {
11631
- return literalToSQL(this.value);
11626
+ /**
11627
+ * Create a new SQL expression instance.
11628
+ * @param {(string|SQLExpression|Ref)[]} parts The parts of the expression.
11629
+ * @param {string[]} [columns=[]] The column dependencies
11630
+ * @param {object} [props] Additional properties for this expression.
11631
+ */
11632
+ constructor(parts, columns, props) {
11633
+ this._expr = Array.isArray(parts) ? parts : [parts];
11634
+ this._deps = columns || [];
11635
+ this.annotate(props);
11636
+ const params = this._expr.filter((part) => isParamLike(part));
11637
+ if (params.length > 0) {
11638
+ this._params = Array.from(new Set(params));
11639
+ this._params.forEach((param) => {
11640
+ param.addEventListener("value", () => update(this, this.map?.get("value")));
11641
+ });
11642
+ } else {
11643
+ this.addEventListener = void 0;
11644
+ }
11632
11645
  }
11633
- };
11634
- var literal = (value) => new Literal(value);
11635
-
11636
- // ../sql/src/compare.js
11637
- function extractColumns(...args) {
11638
- return args.flat().flatMap((arg) => arg?.columns || []);
11639
- }
11640
- var Compare1 = class {
11641
- constructor(op, a) {
11642
- this.op = op;
11643
- this.a = asColumn(a);
11646
+ /**
11647
+ * A reference to this expression.
11648
+ * Provides compatibility with param-like objects.
11649
+ */
11650
+ get value() {
11651
+ return this;
11644
11652
  }
11653
+ /**
11654
+ * The column dependencies of this expression.
11655
+ * @returns {string[]} The columns dependencies.
11656
+ */
11645
11657
  get columns() {
11646
- return extractColumns(this.a);
11647
- }
11648
- visit(callback) {
11649
- callback(this.op, this);
11650
- }
11651
- toString() {
11652
- const { op, a } = this;
11653
- return `(${toSQL(a)} ${op})`;
11654
- }
11655
- };
11656
- function compare1(op) {
11657
- return (a) => new Compare1(op, a);
11658
- }
11659
- var not = compare1("NOT");
11660
- var isNull = compare1("IS NULL");
11661
- var isNotNull = compare1("IS NOT NULL");
11662
- var Compare2 = class {
11663
- constructor(op, a, b) {
11664
- this.op = op;
11665
- this.a = asColumn(a);
11666
- this.b = asColumn(b);
11658
+ const { _params, _deps } = this;
11659
+ if (_params) {
11660
+ const pset = new Set(_params.flatMap((p) => {
11661
+ const cols = p.value?.columns;
11662
+ return Array.isArray(cols) ? cols : [];
11663
+ }));
11664
+ if (pset.size) {
11665
+ const set = new Set(_deps);
11666
+ pset.forEach((col) => set.add(col));
11667
+ return Array.from(set);
11668
+ }
11669
+ }
11670
+ return _deps;
11667
11671
  }
11668
- get columns() {
11669
- return extractColumns(this.a, this.b);
11672
+ /**
11673
+ * The first column dependency in this expression, or undefined if none.
11674
+ * @returns {string} The first column dependency.
11675
+ */
11676
+ get column() {
11677
+ return this._deps.length ? this._deps[0] : this.columns[0];
11670
11678
  }
11671
- visit(callback) {
11672
- callback(this.op, this);
11679
+ /**
11680
+ * Annotate this expression instance with additional properties.
11681
+ * @param {object[]} [props] One or more objects with properties to add.
11682
+ * @returns {this} This SQL expression.
11683
+ */
11684
+ annotate(...props) {
11685
+ return Object.assign(this, ...props);
11673
11686
  }
11687
+ /**
11688
+ * Generate a SQL code string corresponding to this expression.
11689
+ * @returns {string} A SQL code string.
11690
+ */
11674
11691
  toString() {
11675
- const { op, a, b } = this;
11676
- return `(${toSQL(a)} ${op} ${toSQL(b)})`;
11677
- }
11678
- };
11679
- function compare2(op) {
11680
- return (a, b) => new Compare2(op, a, b);
11681
- }
11682
- var eq = compare2("=");
11683
- var neq = compare2("<>");
11684
- var lt = compare2("<");
11685
- var gt = compare2(">");
11686
- var lte = compare2("<=");
11687
- var gte = compare2(">=");
11688
- var isDistinct = compare2("IS DISTINCT FROM");
11689
- var isNotDistinct = compare2("IS NOT DISTINCT FROM");
11690
- var Range = class {
11691
- constructor(op, expr2, value) {
11692
- this.op = op;
11693
- this.expr = asColumn(expr2);
11694
- this.value = value?.map(asColumn);
11695
- }
11696
- get columns() {
11697
- return extractColumns(this.expr, this.value);
11698
- }
11699
- visit(callback) {
11700
- callback(this.op, this);
11692
+ return this._expr.map((p) => isParamLike(p) && !isSQLExpression(p) ? literalToSQL(p.value) : p).join("");
11701
11693
  }
11702
- toString() {
11703
- const { op, expr: expr2, value } = this;
11704
- if (!value)
11705
- return "";
11706
- const [a, b] = value;
11707
- return `(${toSQL(expr2)} ${op} ${toSQL(a)} AND ${toSQL(b)})`;
11694
+ /**
11695
+ * Add an event listener callback for the provided event type.
11696
+ * @param {string} type The event type to listen for (for example, "value").
11697
+ * @param {(a: SQLExpression) => Promise?} callback The callback function to
11698
+ * invoke upon updates. A callback may optionally return a Promise that
11699
+ * upstream listeners may await before proceeding.
11700
+ */
11701
+ addEventListener(type, callback) {
11702
+ const map = this.map || (this.map = /* @__PURE__ */ new Map());
11703
+ const set = map.get(type) || (map.set(type, /* @__PURE__ */ new Set()), map.get(type));
11704
+ set.add(callback);
11708
11705
  }
11709
11706
  };
11710
- function range(op) {
11711
- return (a, range2) => new Range(op, a, range2);
11712
- }
11713
- var isBetween = range("BETWEEN");
11714
- var isNotBetween = range("NOT BETWEEN");
11715
- var CompareN = class {
11716
- constructor(op, value) {
11717
- this.op = op;
11718
- this.value = value.map(asColumn);
11707
+ function update(expr, callbacks) {
11708
+ if (callbacks?.size) {
11709
+ return Promise.allSettled(Array.from(callbacks, (fn) => fn(expr)));
11719
11710
  }
11720
- get columns() {
11721
- return extractColumns(this.value);
11722
- }
11723
- visit(callback) {
11724
- callback(this.op, this);
11725
- this.value?.forEach((v) => v.visit(callback));
11726
- }
11727
- toString() {
11728
- const { op, value } = this;
11729
- return !value || value.length === 0 ? "" : value.length === 1 ? toSQL(value[0]) : `(${value.map(toSQL).filter((x) => x).join(` ${op} `)})`;
11711
+ }
11712
+ function parseSQL(strings, exprs) {
11713
+ const spans = [strings[0]];
11714
+ const cols = /* @__PURE__ */ new Set();
11715
+ const n = exprs.length;
11716
+ for (let i = 0, k = 0; i < n; ) {
11717
+ const e = exprs[i];
11718
+ if (isParamLike(e)) {
11719
+ spans[++k] = e;
11720
+ } else {
11721
+ if (Array.isArray(e?.columns)) {
11722
+ e.columns.forEach((col) => cols.add(col));
11723
+ }
11724
+ spans[k] += typeof e === "string" ? e : literalToSQL(e);
11725
+ }
11726
+ const s = strings[++i];
11727
+ if (isParamLike(spans[k])) {
11728
+ spans[++k] = s;
11729
+ } else {
11730
+ spans[k] += s;
11731
+ }
11730
11732
  }
11731
- };
11732
- function and(...clauses) {
11733
- return new CompareN("AND", clauses.flat());
11733
+ return { spans, cols: Array.from(cols) };
11734
11734
  }
11735
- function or(...clauses) {
11736
- return new CompareN("OR", clauses.flat());
11735
+ function sql(strings, ...exprs) {
11736
+ const { spans, cols } = parseSQL(strings, exprs);
11737
+ return new SQLExpression(spans, cols);
11737
11738
  }
11738
11739
 
11739
- // ../sql/src/function-call.js
11740
- var FunctionCall = class {
11741
- constructor(func2, args) {
11742
- this.func = func2;
11743
- this.args = (args || []).map(asColumn);
11744
- }
11745
- get column() {
11746
- return this.columns[0];
11740
+ // ../sql/src/desc.js
11741
+ function desc(expr) {
11742
+ const e = asColumn(expr);
11743
+ return sql`${e} DESC NULLS LAST`.annotate({ label: e?.label, desc: true });
11744
+ }
11745
+
11746
+ // ../sql/src/literal.js
11747
+ var literal = (value) => ({
11748
+ value,
11749
+ toString: () => literalToSQL(value)
11750
+ });
11751
+
11752
+ // ../sql/src/operators.js
11753
+ function visit(callback) {
11754
+ callback(this.op, this);
11755
+ this.children?.forEach((v) => v.visit(callback));
11756
+ }
11757
+ function logical(op, clauses) {
11758
+ const children = clauses.filter((x) => x != null).map(asColumn);
11759
+ const strings = children.map((c, i) => i ? ` ${op} ` : "");
11760
+ if (clauses.length)
11761
+ strings.push("");
11762
+ return sql(strings, ...children).annotate({ op, children, visit });
11763
+ }
11764
+ var and = (...clauses) => logical("AND", clauses.flat());
11765
+ var or = (...clauses) => logical("OR", clauses.flat());
11766
+ var unaryOp = (op) => (a) => sql`(${op} ${asColumn(a)})`.annotate({ op, a, visit });
11767
+ var not = unaryOp("NOT");
11768
+ var unaryPostOp = (op) => (a) => sql`(${asColumn(a)} ${op})`.annotate({ op, a, visit });
11769
+ var isNull = unaryPostOp("IS NULL");
11770
+ var isNotNull = unaryPostOp("IS NOT NULL");
11771
+ var binaryOp = (op) => (a, b) => sql`(${asColumn(a)} ${op} ${asColumn(b)})`.annotate({ op, a, b, visit });
11772
+ var eq = binaryOp("=");
11773
+ var neq = binaryOp("<>");
11774
+ var lt = binaryOp("<");
11775
+ var gt = binaryOp(">");
11776
+ var lte = binaryOp("<=");
11777
+ var gte = binaryOp(">=");
11778
+ var isDistinct = binaryOp("IS DISTINCT FROM");
11779
+ var isNotDistinct = binaryOp("IS NOT DISTINCT FROM");
11780
+ function rangeOp(op, a, range, exclusive) {
11781
+ a = asColumn(a);
11782
+ const prefix2 = op.startsWith("NOT ") ? "NOT " : "";
11783
+ const expr = !range ? sql`` : exclusive ? sql`${prefix2}(${range[0]} <= ${a} AND ${a} < ${range[1]})` : sql`(${a} ${op} ${range[0]} AND ${range[1]})`;
11784
+ return expr.annotate({ op, visit, field: a, range });
11785
+ }
11786
+ var isBetween = (a, range, exclusive) => rangeOp("BETWEEN", a, range, exclusive);
11787
+
11788
+ // ../sql/src/repeat.js
11789
+ function repeat(length2, str) {
11790
+ return Array.from({ length: length2 }, () => str);
11791
+ }
11792
+
11793
+ // ../sql/src/functions.js
11794
+ function functionCall(op, type) {
11795
+ return (...values) => {
11796
+ const args = values.map(asColumn);
11797
+ const cast2 = type ? `::${type}` : "";
11798
+ const expr = args.length ? sql([`${op}(`, ...repeat(args.length - 1, ", "), `)${cast2}`], ...args) : sql`${op}()${cast2}`;
11799
+ return expr.annotate({ func: op, args });
11800
+ };
11801
+ }
11802
+ var regexp_matches = functionCall("REGEXP_MATCHES");
11803
+ var contains = functionCall("CONTAINS");
11804
+ var prefix = functionCall("PREFIX");
11805
+ var suffix = functionCall("SUFFIX");
11806
+ var lower = functionCall("LOWER");
11807
+ var upper = functionCall("UPPER");
11808
+ var length = functionCall("LENGTH");
11809
+ var isNaN2 = functionCall("ISNAN");
11810
+ var isFinite = functionCall("ISFINITE");
11811
+ var isInfinite = functionCall("ISINF");
11812
+
11813
+ // ../sql/src/windows.js
11814
+ var WindowFunction = class extends SQLExpression {
11815
+ constructor(op, func, type, name, group = "", order = "", frame = "") {
11816
+ let expr;
11817
+ const noWindowParams = !(group || order || frame);
11818
+ if (name && noWindowParams) {
11819
+ expr = name ? sql`${func} OVER "${name}"` : sql`${func} OVER ()`;
11820
+ } else {
11821
+ const s1 = group && order ? " " : "";
11822
+ const s2 = (group || order) && frame ? " " : "";
11823
+ expr = sql`${func} OVER (${name ? `"${name}" ` : ""}${group}${s1}${order}${s2}${frame})`;
11824
+ }
11825
+ if (type) {
11826
+ expr = sql`(${expr})::${type}`;
11827
+ }
11828
+ const { _expr, _deps } = expr;
11829
+ super(_expr, _deps, { window: op, func, type, name, group, order, frame });
11747
11830
  }
11748
- get columns() {
11749
- return this.args.flatMap((a) => a.columns || []);
11831
+ get basis() {
11832
+ return this.column;
11750
11833
  }
11751
- toString() {
11752
- const { func: func2, args } = this;
11753
- return `${func2}(${args.map(toSQL).join(", ")})`;
11754
- }
11755
- };
11756
- function func(op) {
11757
- return (...args) => new FunctionCall(op, args);
11758
- }
11759
- var regexp_matches = func("regexp_matches");
11760
- var contains = func("contains");
11761
- var prefix = func("prefix");
11762
- var suffix = func("suffix");
11763
- var lower = func("lower");
11764
- var upper = func("upper");
11765
- var length = func("length");
11766
- var isNaN2 = func("isnan");
11767
- var isFinite = func("isfinite");
11768
- var isInfinite = func("isinf");
11769
-
11770
- // ../sql/src/aggregate.js
11771
- var Aggregate = class {
11772
- constructor(op, args) {
11773
- this.aggregate = op;
11774
- this.args = (args || []).map(asColumn);
11834
+ get label() {
11835
+ const { func } = this;
11836
+ return func.label ?? func.toString();
11837
+ }
11838
+ over(name) {
11839
+ const { window: op, func, type, group, order, frame } = this;
11840
+ return new WindowFunction(op, func, type, name, group, order, frame);
11841
+ }
11842
+ partitionby(...expr) {
11843
+ const exprs = expr.flat().filter((x) => x).map(asColumn);
11844
+ const group = sql(
11845
+ ["PARTITION BY ", repeat(exprs.length - 1, ", "), ""],
11846
+ ...exprs
11847
+ );
11848
+ const { window: op, func, type, name, order, frame } = this;
11849
+ return new WindowFunction(op, func, type, name, group, order, frame);
11850
+ }
11851
+ orderby(...expr) {
11852
+ const exprs = expr.flat().filter((x) => x).map(asColumn);
11853
+ const order = sql(
11854
+ ["ORDER BY ", repeat(exprs.length - 1, ", "), ""],
11855
+ ...exprs
11856
+ );
11857
+ const { window: op, func, type, name, group, frame } = this;
11858
+ return new WindowFunction(op, func, type, name, group, order, frame);
11859
+ }
11860
+ rows(expr) {
11861
+ const frame = windowFrame("ROWS", expr);
11862
+ const { window: op, func, type, name, group, order } = this;
11863
+ return new WindowFunction(op, func, type, name, group, order, frame);
11864
+ }
11865
+ range(expr) {
11866
+ const frame = windowFrame("RANGE", expr);
11867
+ const { window: op, func, type, name, group, order } = this;
11868
+ return new WindowFunction(op, func, type, name, group, order, frame);
11869
+ }
11870
+ };
11871
+ function windowFrame(type, frame) {
11872
+ if (isParamLike(frame)) {
11873
+ const expr = sql`${frame}`;
11874
+ expr.toString = () => `${type} ${frameToSQL(frame.value)}`;
11875
+ return expr;
11876
+ }
11877
+ return `${type} ${frameToSQL(frame)}`;
11878
+ }
11879
+ function frameToSQL(frame) {
11880
+ const [prev, next] = frame;
11881
+ const a = prev === 0 ? "CURRENT ROW" : Number.isFinite(prev) ? `${Math.abs(prev)} PRECEDING` : "UNBOUNDED PRECEDING";
11882
+ const b = next === 0 ? "CURRENT ROW" : Number.isFinite(next) ? `${Math.abs(next)} FOLLOWING` : "UNBOUNDED FOLLOWING";
11883
+ return `BETWEEN ${a} AND ${b}`;
11884
+ }
11885
+ function winf(op, type) {
11886
+ return (...values) => {
11887
+ const func = functionCall(op)(...values);
11888
+ return new WindowFunction(op, func, type);
11889
+ };
11890
+ }
11891
+ var row_number = winf("ROW_NUMBER", "INTEGER");
11892
+ var rank = winf("RANK", "INTEGER");
11893
+ var dense_rank = winf("DENSE_RANK", "INTEGER");
11894
+ var percent_rank = winf("PERCENT_RANK");
11895
+ var cume_dist = winf("CUME_DIST");
11896
+ var ntile = winf("NTILE");
11897
+ var lag = winf("LAG");
11898
+ var lead = winf("LEAD");
11899
+ var first_value = winf("FIRST_VALUE");
11900
+ var last_value = winf("LAST_VALUE");
11901
+ var nth_value = winf("NTH_VALUE");
11902
+
11903
+ // ../sql/src/aggregates.js
11904
+ var AggregateFunction = class extends SQLExpression {
11905
+ constructor(op, args, type, isDistinct2, filter) {
11906
+ args = (args || []).map(asColumn);
11907
+ const { strings, exprs } = aggExpr(op, args, type, isDistinct2, filter);
11908
+ const { spans, cols } = parseSQL(strings, exprs);
11909
+ super(spans, cols, { aggregate: op, args, type, isDistinct: isDistinct2, filter });
11910
+ }
11911
+ get basis() {
11912
+ return this.column;
11775
11913
  }
11776
11914
  get label() {
11777
- return this.aggregate.toLowerCase() + (this.args.length ? ` ${this.columns.join(", ")}` : "");
11915
+ const { aggregate: op, args, isDistinct: isDistinct2 } = this;
11916
+ const dist = isDistinct2 ? "DISTINCT" + (args.length ? " " : "") : "";
11917
+ const tail = args.length ? `(${dist}${args.map(unquoted).join(", ")})` : "";
11918
+ return `${op.toLowerCase()}${tail}`;
11778
11919
  }
11779
- get column() {
11780
- return this.columns[0];
11920
+ distinct() {
11921
+ const { aggregate: op, args, type, filter } = this;
11922
+ return new AggregateFunction(op, args, type, true, filter);
11781
11923
  }
11782
- get columns() {
11783
- return this.args.flatMap((a) => a.columns || []);
11924
+ where(filter) {
11925
+ const { aggregate: op, args, type, isDistinct: isDistinct2 } = this;
11926
+ return new AggregateFunction(op, args, type, isDistinct2, filter);
11784
11927
  }
11785
- distinct() {
11786
- this.isDistinct = true;
11787
- return this;
11928
+ window() {
11929
+ const { aggregate: op, args, type, isDistinct: isDistinct2 } = this;
11930
+ const func = new AggregateFunction(op, args, null, isDistinct2);
11931
+ return new WindowFunction(op, func, type);
11788
11932
  }
11789
- where(expr2) {
11790
- this.filter = expr2;
11791
- return this;
11933
+ partitionby(...expr) {
11934
+ return this.window().partitionby(...expr);
11792
11935
  }
11793
- toString() {
11794
- const { aggregate, args, isDistinct: isDistinct2, filter } = this;
11795
- const arg = args.length === 0 ? "*" : args.map(toSQL).join(", ");
11796
- const distinct2 = isDistinct2 ? "DISTINCT " : "";
11797
- const where = filter ? ` FILTER (WHERE ${toSQL(filter)})` : "";
11798
- const cast = aggregate === "COUNT" ? "::INTEGER" : "";
11799
- return where && cast ? `(${aggregate}(${distinct2}${arg})${where})${cast}` : `${aggregate}(${distinct2}${arg})${where}${cast}`;
11800
- }
11801
- };
11802
- function agg(op) {
11803
- return (...args) => new Aggregate(op, args);
11804
- }
11805
- var count = agg("COUNT");
11806
- var avg = agg("AVG");
11807
- var mean = agg("AVG");
11808
- var mad = agg("MAD");
11809
- var max = agg("MAX");
11810
- var min = agg("MIN");
11811
- var sum2 = agg("SUM");
11812
- var product = agg("PRODUCT");
11813
- var median = agg("MEDIAN");
11814
- var quantile = agg("QUANTILE");
11815
- var mode = agg("MODE");
11816
- var variance = agg("VARIANCE");
11817
- var stddev = agg("STDDEV");
11818
- var skewness = agg("SKEWNESS");
11819
- var kurtosis = agg("KURTOSIS");
11820
- var entropy = agg("ENTROPY");
11821
- var varPop = agg("VAR_POP");
11822
- var stddevPop = agg("STDDEV_POP");
11823
- var corr = agg("CORR");
11824
- var covarPop = agg("COVAR_POP");
11825
- var regrIntercept = agg("REGR_INTERCEPT");
11826
- var regrSlope = agg("REGR_SLOPE");
11827
- var regrCount = agg("REGR_COUNT");
11828
- var regrR2 = agg("REGR_R2");
11829
- var regrSYY = agg("REGR_SYY");
11830
- var regrSXX = agg("REGR_SXX");
11831
- var regrSXY = agg("REGR_SXY");
11832
- var regrAvgX = agg("REGR_AVGX");
11833
- var regrAvgY = agg("REGR_AVGY");
11834
- var first = agg("FIRST");
11835
- var last = agg("LAST");
11836
- var argmin = agg("ARG_MIN");
11837
- var argmax = agg("ARG_MAX");
11838
- var stringAgg = agg("STRING_AGG");
11839
- var arrayAgg = agg("ARRAY_AGG");
11936
+ orderby(...expr) {
11937
+ return this.window().orderby(...expr);
11938
+ }
11939
+ rows(prev, next) {
11940
+ return this.window().rows(prev, next);
11941
+ }
11942
+ range(prev, next) {
11943
+ return this.window().range(prev, next);
11944
+ }
11945
+ };
11946
+ function aggExpr(op, args, type, isDistinct2, filter) {
11947
+ const close = `)${type ? `::${type}` : ""}`;
11948
+ let strings = [`${op}(${isDistinct2 ? "DISTINCT " : ""}`];
11949
+ let exprs = [];
11950
+ if (args.length) {
11951
+ strings = strings.concat([
11952
+ ...repeat(args.length - 1, ", "),
11953
+ `${close}${filter ? " FILTER (WHERE " : ""}`,
11954
+ ...filter ? [")"] : []
11955
+ ]);
11956
+ exprs = [...args, ...filter ? [filter] : []];
11957
+ } else {
11958
+ strings[0] += "*" + close;
11959
+ }
11960
+ return { exprs, strings };
11961
+ }
11962
+ function unquoted(value) {
11963
+ const s = literalToSQL(value);
11964
+ return s && s.startsWith('"') && s.endsWith('"') ? s.slice(1, -1) : s;
11965
+ }
11966
+ function aggf(op, type) {
11967
+ return (...args) => new AggregateFunction(op, args, type);
11968
+ }
11969
+ var count = aggf("COUNT", "INTEGER");
11970
+ var avg = aggf("AVG");
11971
+ var mean = aggf("AVG");
11972
+ var mad = aggf("MAD");
11973
+ var max = aggf("MAX");
11974
+ var min = aggf("MIN");
11975
+ var sum2 = aggf("SUM", "DOUBLE");
11976
+ var product = aggf("PRODUCT");
11977
+ var median = aggf("MEDIAN");
11978
+ var quantile = aggf("QUANTILE");
11979
+ var mode = aggf("MODE");
11980
+ var variance = aggf("VARIANCE");
11981
+ var stddev = aggf("STDDEV");
11982
+ var skewness = aggf("SKEWNESS");
11983
+ var kurtosis = aggf("KURTOSIS");
11984
+ var entropy = aggf("ENTROPY");
11985
+ var varPop = aggf("VAR_POP");
11986
+ var stddevPop = aggf("STDDEV_POP");
11987
+ var corr = aggf("CORR");
11988
+ var covarPop = aggf("COVAR_POP");
11989
+ var regrIntercept = aggf("REGR_INTERCEPT");
11990
+ var regrSlope = aggf("REGR_SLOPE");
11991
+ var regrCount = aggf("REGR_COUNT");
11992
+ var regrR2 = aggf("REGR_R2");
11993
+ var regrSYY = aggf("REGR_SYY");
11994
+ var regrSXX = aggf("REGR_SXX");
11995
+ var regrSXY = aggf("REGR_SXY");
11996
+ var regrAvgX = aggf("REGR_AVGX");
11997
+ var regrAvgY = aggf("REGR_AVGY");
11998
+ var first = aggf("FIRST");
11999
+ var last = aggf("LAST");
12000
+ var argmin = aggf("ARG_MIN");
12001
+ var argmax = aggf("ARG_MAX");
12002
+ var stringAgg = aggf("STRING_AGG");
12003
+ var arrayAgg = aggf("ARRAY_AGG");
11840
12004
 
11841
12005
  // ../sql/src/datetime.js
11842
- var epoch_ms = transform(
11843
- (d) => `(1000 * (epoch(${d}) - second(${d})) + millisecond(${d}))::DOUBLE`
11844
- );
12006
+ var epoch_ms = (expr) => {
12007
+ const d = asColumn(expr);
12008
+ return sql`(1000 * (epoch(${d}) - second(${d})) + millisecond(${d}))::DOUBLE`;
12009
+ };
11845
12010
 
11846
12011
  // ../sql/src/Query.js
11847
12012
  var Query = class {
11848
- static select(...expr2) {
11849
- return new Query().select(...expr2);
12013
+ static select(...expr) {
12014
+ return new Query().select(...expr);
11850
12015
  }
11851
- static from(...expr2) {
11852
- return new Query().from(...expr2);
12016
+ static from(...expr) {
12017
+ return new Query().from(...expr);
11853
12018
  }
11854
- static with(...expr2) {
11855
- return new Query().with(...expr2);
12019
+ static with(...expr) {
12020
+ return new Query().with(...expr);
11856
12021
  }
11857
12022
  static union(...queries) {
11858
12023
  return new SetOperation("UNION", queries.flat());
@@ -11884,9 +12049,9 @@ var Query = class {
11884
12049
  q.query = { ...this.query };
11885
12050
  return q;
11886
12051
  }
11887
- with(...expr2) {
12052
+ with(...expr) {
11888
12053
  const { query } = this;
11889
- if (expr2.length === 0) {
12054
+ if (expr.length === 0) {
11890
12055
  return query.with;
11891
12056
  } else {
11892
12057
  const list = [];
@@ -11895,7 +12060,7 @@ var Query = class {
11895
12060
  query2.cteFor = this;
11896
12061
  list.push({ as, query: query2 });
11897
12062
  };
11898
- expr2.flat().forEach((e) => {
12063
+ expr.flat().forEach((e) => {
11899
12064
  if (e == null) {
11900
12065
  } else if (e.as && e.query) {
11901
12066
  add(e.as, e.query);
@@ -11909,13 +12074,13 @@ var Query = class {
11909
12074
  return this;
11910
12075
  }
11911
12076
  }
11912
- select(...expr2) {
12077
+ select(...expr) {
11913
12078
  const { query } = this;
11914
- if (expr2.length === 0) {
12079
+ if (expr.length === 0) {
11915
12080
  return query.select;
11916
12081
  } else {
11917
12082
  const list = [];
11918
- expr2.flat().forEach((e) => {
12083
+ for (const e of expr.flat()) {
11919
12084
  if (e == null) {
11920
12085
  } else if (typeof e === "string") {
11921
12086
  list.push({ as: e, expr: asColumn(e) });
@@ -11928,32 +12093,32 @@ var Query = class {
11928
12093
  list.push({ as: unquote(as), expr: asColumn(e[as]) });
11929
12094
  }
11930
12095
  }
11931
- });
12096
+ }
11932
12097
  query.select = query.select.concat(list);
11933
12098
  return this;
11934
12099
  }
11935
12100
  }
11936
- $select(...expr2) {
12101
+ $select(...expr) {
11937
12102
  this.query.select = [];
11938
- return this.select(...expr2);
12103
+ return this.select(...expr);
11939
12104
  }
11940
12105
  distinct(value = true) {
11941
12106
  this.query.distinct = !!value;
11942
12107
  return this;
11943
12108
  }
11944
- from(...expr2) {
12109
+ from(...expr) {
11945
12110
  const { query } = this;
11946
- if (expr2.length === 0) {
12111
+ if (expr.length === 0) {
11947
12112
  return query.from;
11948
12113
  } else {
11949
12114
  const list = [];
11950
- expr2.flat().forEach((e) => {
12115
+ expr.flat().forEach((e) => {
11951
12116
  if (e == null) {
11952
12117
  } else if (typeof e === "string") {
11953
12118
  list.push({ as: e, from: asRelation(e) });
11954
12119
  } else if (e instanceof Ref) {
11955
12120
  list.push({ as: e.table, from: e });
11956
- } else if (isQuery(e) || isExpression(e)) {
12121
+ } else if (isQuery(e) || isSQLExpression(e)) {
11957
12122
  list.push({ from: e });
11958
12123
  } else if (Array.isArray(e)) {
11959
12124
  list.push({ as: unquote(e[0]), from: asRelation(e[1]) });
@@ -11967,67 +12132,71 @@ var Query = class {
11967
12132
  return this;
11968
12133
  }
11969
12134
  }
11970
- $from(...expr2) {
12135
+ $from(...expr) {
11971
12136
  this.query.from = [];
11972
- return this.from(...expr2);
12137
+ return this.from(...expr);
11973
12138
  }
11974
- sample(value) {
12139
+ sample(value, method) {
11975
12140
  const { query } = this;
11976
12141
  if (arguments.length === 0) {
11977
12142
  return query.sample;
11978
12143
  } else {
11979
12144
  let spec = value;
11980
12145
  if (typeof value === "number") {
11981
- spec = value > 0 && value < 1 ? { perc: 100 * value } : { rows: Math.round(value) };
12146
+ spec = value > 0 && value < 1 ? { perc: 100 * value, method } : { rows: Math.round(value), method };
11982
12147
  }
11983
12148
  query.sample = spec;
11984
12149
  return this;
11985
12150
  }
11986
12151
  }
11987
- where(...expr2) {
12152
+ where(...expr) {
11988
12153
  const { query } = this;
11989
- if (expr2.length === 0) {
12154
+ if (expr.length === 0) {
11990
12155
  return query.where;
11991
12156
  } else {
11992
12157
  query.where = query.where.concat(
11993
- expr2.flat().filter((x) => x)
12158
+ expr.flat().filter((x) => x)
11994
12159
  );
11995
12160
  return this;
11996
12161
  }
11997
12162
  }
11998
- $where(...expr2) {
12163
+ $where(...expr) {
11999
12164
  this.query.where = [];
12000
- return this.where(...expr2);
12165
+ return this.where(...expr);
12001
12166
  }
12002
- groupby(...expr2) {
12167
+ groupby(...expr) {
12003
12168
  const { query } = this;
12004
- if (expr2.length === 0) {
12169
+ if (expr.length === 0) {
12005
12170
  return query.groupby;
12006
12171
  } else {
12007
12172
  query.groupby = query.groupby.concat(
12008
- expr2.flat().filter((x) => x).map(asColumn)
12173
+ expr.flat().filter((x) => x).map(asColumn)
12009
12174
  );
12010
12175
  return this;
12011
12176
  }
12012
12177
  }
12013
- having(...expr2) {
12178
+ $groupby(...expr) {
12179
+ this.query.groupby = [];
12180
+ return this.groupby(...expr);
12181
+ }
12182
+ having(...expr) {
12014
12183
  const { query } = this;
12015
- if (expr2.length === 0) {
12184
+ if (expr.length === 0) {
12016
12185
  return query.having;
12017
12186
  } else {
12018
12187
  query.having = query.having.concat(
12019
- expr2.flat().filter((x) => x)
12188
+ expr.flat().filter((x) => x)
12020
12189
  );
12021
12190
  return this;
12022
12191
  }
12023
12192
  }
12024
- window(...expr2) {
12193
+ window(...expr) {
12025
12194
  const { query } = this;
12026
- if (expr2.length === 0) {
12195
+ if (expr.length === 0) {
12027
12196
  return query.window;
12028
12197
  } else {
12029
12198
  const list = [];
12030
- expr2.flat().forEach((e) => {
12199
+ expr.flat().forEach((e) => {
12031
12200
  if (e == null) {
12032
12201
  } else {
12033
12202
  for (const as in e) {
@@ -12039,24 +12208,24 @@ var Query = class {
12039
12208
  return this;
12040
12209
  }
12041
12210
  }
12042
- qualify(...expr2) {
12211
+ qualify(...expr) {
12043
12212
  const { query } = this;
12044
- if (expr2.length === 0) {
12213
+ if (expr.length === 0) {
12045
12214
  return query.qualify;
12046
12215
  } else {
12047
12216
  query.qualify = query.qualify.concat(
12048
- expr2.flat().filter((x) => x)
12217
+ expr.flat().filter((x) => x)
12049
12218
  );
12050
12219
  return this;
12051
12220
  }
12052
12221
  }
12053
- orderby(...expr2) {
12222
+ orderby(...expr) {
12054
12223
  const { query } = this;
12055
- if (expr2.length === 0) {
12224
+ if (expr.length === 0) {
12056
12225
  return query.orderby;
12057
12226
  } else {
12058
12227
  query.orderby = query.orderby.concat(
12059
- expr2.flat().filter((x) => x).map(asColumn)
12228
+ expr.flat().filter((x) => x).map(asColumn)
12060
12229
  );
12061
12230
  return this;
12062
12231
  }
@@ -12116,7 +12285,7 @@ var Query = class {
12116
12285
  sql2.push(`WITH ${list.join(", ")}`);
12117
12286
  }
12118
12287
  const sels = select.map(
12119
- ({ as, expr: expr2 }) => isColumnRefFor(expr2, as) && !expr2.table ? `${expr2}` : `${expr2} AS "${as}"`
12288
+ ({ as, expr }) => isColumnRefFor(expr, as) && !expr.table ? `${expr}` : `${expr} AS "${as}"`
12120
12289
  );
12121
12290
  sql2.push(`SELECT${distinct2 ? " DISTINCT" : ""} ${sels.join(", ")}`);
12122
12291
  if (from.length) {
@@ -12126,17 +12295,17 @@ var Query = class {
12126
12295
  });
12127
12296
  sql2.push(`FROM ${rels.join(", ")}`);
12128
12297
  }
12298
+ if (where.length) {
12299
+ const clauses = where.map(String).filter((x) => x).join(" AND ");
12300
+ if (clauses)
12301
+ sql2.push(`WHERE ${clauses}`);
12302
+ }
12129
12303
  if (sample) {
12130
12304
  const { rows, perc, method, seed } = sample;
12131
12305
  const size = rows ? `${rows} ROWS` : `${perc} PERCENT`;
12132
12306
  const how = method ? ` (${method}${seed != null ? `, ${seed}` : ""})` : "";
12133
12307
  sql2.push(`USING SAMPLE ${size}${how}`);
12134
12308
  }
12135
- if (where.length) {
12136
- const clauses = where.map(String).filter((x) => x).join(" AND ");
12137
- if (clauses)
12138
- sql2.push(`WHERE ${clauses}`);
12139
- }
12140
12309
  if (groupby.length) {
12141
12310
  sql2.push(`GROUP BY ${groupby.join(", ")}`);
12142
12311
  }
@@ -12146,7 +12315,7 @@ var Query = class {
12146
12315
  sql2.push(`HAVING ${clauses}`);
12147
12316
  }
12148
12317
  if (window.length) {
12149
- const windows = window.map(({ as, expr: expr2 }) => `"${as}" AS (${expr2})`);
12318
+ const windows = window.map(({ as, expr }) => `"${as}" AS (${expr})`);
12150
12319
  sql2.push(`WINDOW ${windows.join(", ")}`);
12151
12320
  }
12152
12321
  if (qualify.length) {
@@ -12177,13 +12346,13 @@ var SetOperation = class {
12177
12346
  q.query = { ...this.query };
12178
12347
  return q;
12179
12348
  }
12180
- orderby(...expr2) {
12349
+ orderby(...expr) {
12181
12350
  const { query } = this;
12182
- if (expr2.length === 0) {
12351
+ if (expr.length === 0) {
12183
12352
  return query.orderby;
12184
12353
  } else {
12185
12354
  query.orderby = query.orderby.concat(
12186
- expr2.flat().filter((x) => x).map(asColumn)
12355
+ expr.flat().filter((x) => x).map(asColumn)
12187
12356
  );
12188
12357
  return this;
12189
12358
  }
@@ -12250,8 +12419,8 @@ var statMap = {
12250
12419
  [Min]: min,
12251
12420
  [Nulls]: (column2) => count().where(isNull(column2))
12252
12421
  };
12253
- function summarize({ table, column: column2 }, stats) {
12254
- return Query.from(table).select(stats.map((s) => [s, statMap[s](column2)]));
12422
+ function summarize({ table: table2, column: column2 }, stats) {
12423
+ return Query.from(table2).select(stats.map((s) => [s, statMap[s](column2)]));
12255
12424
  }
12256
12425
 
12257
12426
  // ../core/src/Catalog.js
@@ -12264,38 +12433,35 @@ var Catalog = class {
12264
12433
  clear() {
12265
12434
  this.tables = object();
12266
12435
  }
12267
- async tableInfo(table) {
12436
+ tableInfo(table2) {
12268
12437
  const cache = this.tables;
12269
- if (cache[table]) {
12270
- return cache[table];
12438
+ if (cache[table2]) {
12439
+ return cache[table2];
12271
12440
  }
12272
- const q = this.mc.query(
12273
- `DESCRIBE "${table}"`,
12274
- { type: "json", cache: false }
12275
- );
12276
- return cache[table] = q.then((result) => {
12277
- const columns = object();
12278
- for (const entry of result) {
12279
- columns[entry.column_name] = {
12280
- table,
12281
- column: entry.column_name,
12282
- sqlType: entry.column_type,
12283
- type: jsType(entry.column_type),
12284
- nullable: entry.null === "YES"
12285
- };
12286
- }
12287
- return columns;
12441
+ const infoPromise = getTableInfo(this.mc, table2).catch((err) => {
12442
+ cache[table2] = null;
12443
+ throw err;
12288
12444
  });
12445
+ return cache[table2] = infoPromise;
12289
12446
  }
12290
- async fieldInfo({ table, column: column2, stats }) {
12291
- const tableInfo = await this.tableInfo(table);
12447
+ async fieldInfo({ table: table2, column: column2, stats }) {
12448
+ const tableInfo = await this.tableInfo(table2);
12292
12449
  const colInfo = tableInfo[column2];
12293
12450
  if (colInfo == null)
12294
12451
  return;
12295
12452
  if (!stats?.length)
12296
12453
  return colInfo;
12297
- const result = await this.mc.query(summarize(colInfo, stats));
12454
+ const result = await this.mc.query(
12455
+ summarize(colInfo, stats),
12456
+ { persist: true }
12457
+ );
12298
12458
  const info = { ...colInfo, ...Array.from(result)[0] };
12459
+ for (const key in info) {
12460
+ const value = info[key];
12461
+ if (typeof value === "bigint") {
12462
+ info[key] = Number(value);
12463
+ }
12464
+ }
12299
12465
  return info;
12300
12466
  }
12301
12467
  async queryFields(fields) {
@@ -12304,6 +12470,23 @@ var Catalog = class {
12304
12470
  return data.filter((x) => x);
12305
12471
  }
12306
12472
  };
12473
+ async function getTableInfo(mc, table2) {
12474
+ const result = await mc.query(
12475
+ `DESCRIBE "${table2}"`,
12476
+ { type: "json", cache: false }
12477
+ );
12478
+ const columns = object();
12479
+ for (const entry of result) {
12480
+ columns[entry.column_name] = {
12481
+ table: table2,
12482
+ column: entry.column_name,
12483
+ sqlType: entry.column_type,
12484
+ type: jsType(entry.column_type),
12485
+ nullable: entry.null === "YES"
12486
+ };
12487
+ }
12488
+ return columns;
12489
+ }
12307
12490
  async function resolveFields(catalog, list) {
12308
12491
  return list.length === 1 && list[0].column === "*" ? Object.values(await catalog.tableInfo(list[0].table)) : list;
12309
12492
  }
@@ -12332,11 +12515,6 @@ function fnv_mix(a) {
12332
12515
  return a & 4294967295;
12333
12516
  }
12334
12517
 
12335
- // ../core/src/util/skip-client.js
12336
- function skipClient(client, clause) {
12337
- return clause?.clients?.has(client);
12338
- }
12339
-
12340
12518
  // ../core/src/DataTileIndexer.js
12341
12519
  var identity = (x) => x;
12342
12520
  var DataTileIndexer = class {
@@ -12351,32 +12529,38 @@ var DataTileIndexer = class {
12351
12529
  this.indices = null;
12352
12530
  this.activeView = null;
12353
12531
  }
12532
+ clear() {
12533
+ if (this.indices) {
12534
+ this.mc.cancel(Array.from(this.indices.values(), (index) => index.result));
12535
+ this.indices = null;
12536
+ }
12537
+ }
12354
12538
  index(clients, active) {
12355
12539
  if (this.clients !== clients) {
12356
- const cols = Array.from(clients).map(getIndexColumns);
12540
+ const cols = Array.from(clients, getIndexColumns);
12357
12541
  const from = cols[0]?.from;
12358
12542
  this.enabled = cols.every((c) => c && c.from === from);
12359
12543
  this.clients = clients;
12360
- this.indices = null;
12361
12544
  this.activeView = null;
12545
+ this.clear();
12362
12546
  }
12363
12547
  if (!this.enabled)
12364
12548
  return false;
12365
12549
  active = active || this.selection.active;
12366
12550
  const { source } = active;
12551
+ if (source && source === this.activeView?.source)
12552
+ return true;
12553
+ this.clear();
12367
12554
  if (!source)
12368
12555
  return false;
12369
- if (source === this.activeView?.source)
12370
- return true;
12371
12556
  const activeView = this.activeView = getActiveView(active);
12372
12557
  if (!activeView)
12373
12558
  return false;
12374
12559
  this.mc.logger().warn("DATA TILE INDEX CONSTRUCTION");
12375
- const sel = this.selection.clone().update({ source });
12560
+ const sel = this.selection.remove(source);
12376
12561
  const indices = this.indices = /* @__PURE__ */ new Map();
12377
- const promises = [];
12378
12562
  for (const client of clients) {
12379
- if (sel.cross && skipClient(client, active))
12563
+ if (sel.skip(client, active))
12380
12564
  continue;
12381
12565
  const index = getIndexColumns(client);
12382
12566
  const query = client.query(sel.predicate(client)).select({ ...activeView.columns, ...index.count }).groupby(Object.keys(activeView.columns));
@@ -12387,11 +12571,10 @@ var DataTileIndexer = class {
12387
12571
  }
12388
12572
  const sql2 = query.toString();
12389
12573
  const id = (fnv_hash(sql2) >>> 0).toString(16);
12390
- const table = `tile_index_${id}`;
12391
- indices.set(client, { table, ...index });
12392
- promises.push(createIndex(this.mc, table, sql2));
12574
+ const table2 = `tile_index_${id}`;
12575
+ const result = createIndex(this.mc, table2, sql2);
12576
+ indices.set(client, { table: table2, result, ...index });
12393
12577
  }
12394
- return promises;
12395
12578
  }
12396
12579
  async update() {
12397
12580
  const { clients, selection, activeView } = this;
@@ -12407,11 +12590,9 @@ var DataTileIndexer = class {
12407
12590
  if (!filter) {
12408
12591
  filter = this.activeView.predicate(this.selection.active.predicate);
12409
12592
  }
12410
- const { table, dims, aggr } = index;
12411
- return this.mc.updateClient(
12412
- client,
12413
- Query.select(dims, aggr).from(table).groupby(dims).where(filter)
12414
- );
12593
+ const { table: table2, dims, aggr } = index;
12594
+ const query = Query.select(dims, aggr).from(table2).groupby(dims).where(filter);
12595
+ return this.mc.updateClient(client, query);
12415
12596
  }
12416
12597
  };
12417
12598
  function getActiveView(clause) {
@@ -12419,19 +12600,19 @@ function getActiveView(clause) {
12419
12600
  let columns = clause.predicate?.columns;
12420
12601
  if (!schema || !columns)
12421
12602
  return null;
12422
- const { type, scales } = schema;
12603
+ const { type, scales, pixelSize = 1 } = schema;
12423
12604
  let predicate;
12424
12605
  if (type === "interval" && scales) {
12425
- const bins = scales.map((s) => binInterval(s));
12606
+ const bins = scales.map((s) => binInterval(s, pixelSize));
12426
12607
  if (bins.some((b) => b == null))
12427
12608
  return null;
12428
12609
  if (bins.length === 1) {
12429
- predicate = (p) => p ? isBetween("active0", p.value.map(bins[0])) : [];
12430
- columns = { active0: bins[0](clause.predicate.expr) };
12610
+ predicate = (p) => p ? isBetween("active0", p.range.map(bins[0])) : [];
12611
+ columns = { active0: bins[0](clause.predicate.field) };
12431
12612
  } else {
12432
- predicate = (p) => p ? and(p.value.map(({ value }, i) => isBetween(`active${i}`, value.map(bins[i])))) : [];
12613
+ predicate = (p) => p ? and(p.children.map(({ range }, i) => isBetween(`active${i}`, range.map(bins[i])))) : [];
12433
12614
  columns = Object.fromEntries(
12434
- clause.predicate.value.map((p, i) => [`active${i}`, bins[i](p.expr)])
12615
+ clause.predicate.children.map((p, i) => [`active${i}`, bins[i](p.field)])
12435
12616
  );
12436
12617
  }
12437
12618
  } else if (type === "point") {
@@ -12442,49 +12623,43 @@ function getActiveView(clause) {
12442
12623
  }
12443
12624
  return { source, columns, predicate };
12444
12625
  }
12445
- function binInterval(scale) {
12446
- const { type, domain, range: range2 } = scale;
12447
- let lift, sql2;
12626
+ function binInterval(scale, pixelSize) {
12627
+ const { type, domain, range } = scale;
12628
+ let lift, toSql;
12448
12629
  switch (type) {
12449
12630
  case "linear":
12450
12631
  lift = identity;
12451
- sql2 = asColumn;
12632
+ toSql = asColumn;
12452
12633
  break;
12453
12634
  case "log":
12454
12635
  lift = Math.log;
12455
- sql2 = (c) => `LN(${asColumn(c)})`;
12636
+ toSql = (c) => sql`LN(${asColumn(c)})`;
12456
12637
  break;
12457
12638
  case "symlog":
12458
12639
  lift = (x) => Math.sign(x) * Math.log1p(Math.abs(x));
12459
- sql2 = (c) => (c = asColumn(c), `SIGN(${c}) * LN(1 + ABS(${c}))`);
12640
+ toSql = (c) => (c = asColumn(c), sql`SIGN(${c}) * LN(1 + ABS(${c}))`);
12460
12641
  break;
12461
12642
  case "sqrt":
12462
12643
  lift = Math.sqrt;
12463
- sql2 = (c) => `SQRT(${asColumn(c)})`;
12644
+ toSql = (c) => sql`SQRT(${asColumn(c)})`;
12464
12645
  break;
12465
12646
  case "utc":
12466
12647
  case "time":
12467
12648
  lift = (x) => +x;
12468
- sql2 = (c) => c instanceof Date ? +c : epoch_ms(asColumn(c));
12649
+ toSql = (c) => c instanceof Date ? +c : epoch_ms(asColumn(c));
12469
12650
  break;
12470
12651
  }
12471
- return lift ? binFunction(domain, range2, lift, sql2) : null;
12652
+ return lift ? binFunction(domain, range, pixelSize, lift, toSql) : null;
12472
12653
  }
12473
- function binFunction(domain, range2, lift, sql2) {
12654
+ function binFunction(domain, range, pixelSize, lift, toSql) {
12474
12655
  const lo = lift(Math.min(domain[0], domain[1]));
12475
12656
  const hi = lift(Math.max(domain[0], domain[1]));
12476
- const a = Math.abs(lift(range2[1]) - lift(range2[0])) / (hi - lo);
12477
- return (value) => expr(
12478
- `FLOOR(${a}::DOUBLE * (${sql2(value)} - ${lo}::DOUBLE))`,
12479
- asColumn(value).columns
12480
- );
12657
+ const a = Math.abs(lift(range[1]) - lift(range[0])) / (hi - lo) / pixelSize;
12658
+ const s = pixelSize === 1 ? "" : `${pixelSize}::INTEGER * `;
12659
+ return (value) => sql`${s}FLOOR(${a}::DOUBLE * (${toSql(value)} - ${lo}::DOUBLE))::INTEGER`;
12481
12660
  }
12482
- async function createIndex(mc, table, query) {
12483
- try {
12484
- await mc.exec(`CREATE TEMP TABLE IF NOT EXISTS ${table} AS ${query}`);
12485
- } catch (err) {
12486
- mc.logger().error(err);
12487
- }
12661
+ function createIndex(mc, table2, query) {
12662
+ return mc.exec(`CREATE TEMP TABLE IF NOT EXISTS ${table2} AS ${query}`);
12488
12663
  }
12489
12664
  var NO_INDEX = { from: NaN };
12490
12665
  function getIndexColumns(client) {
@@ -12495,24 +12670,24 @@ function getIndexColumns(client) {
12495
12670
  if (!from || !q.groupby)
12496
12671
  return NO_INDEX;
12497
12672
  const g = new Set(q.groupby().map((c) => c.column));
12498
- let aggr = [];
12499
- let dims = [];
12673
+ const aggr = [];
12674
+ const dims = [];
12500
12675
  let count2;
12501
12676
  for (const { as, expr: { aggregate } } of q.select()) {
12502
12677
  switch (aggregate?.toUpperCase()) {
12503
12678
  case "COUNT":
12504
12679
  case "SUM":
12505
- aggr.push({ [as]: expr(`SUM("${as}")::DOUBLE`) });
12680
+ aggr.push({ [as]: sql`SUM("${as}")::DOUBLE` });
12506
12681
  break;
12507
12682
  case "AVG":
12508
12683
  count2 = "_count_";
12509
- aggr.push({ [as]: expr(`(SUM("${as}" * ${count2}) / SUM(${count2}))::DOUBLE`) });
12684
+ aggr.push({ [as]: sql`(SUM("${as}" * ${count2}) / SUM(${count2}))::DOUBLE` });
12510
12685
  break;
12511
12686
  case "MAX":
12512
- aggr.push({ [as]: expr(`MAX("${as}")`) });
12687
+ aggr.push({ [as]: sql`MAX("${as}")` });
12513
12688
  break;
12514
12689
  case "MIN":
12515
- aggr.push({ [as]: expr(`MIN("${as}")`) });
12690
+ aggr.push({ [as]: sql`MIN("${as}")` });
12516
12691
  break;
12517
12692
  default:
12518
12693
  if (g.has(as))
@@ -12524,7 +12699,7 @@ function getIndexColumns(client) {
12524
12699
  return {
12525
12700
  aggr,
12526
12701
  dims,
12527
- count: count2 ? { [count2]: expr("COUNT(*)") } : {},
12702
+ count: count2 ? { [count2]: sql`COUNT(*)` } : {},
12528
12703
  from
12529
12704
  };
12530
12705
  }
@@ -12537,7 +12712,7 @@ function getBaseTable(query) {
12537
12712
  if (subq.length === 0)
12538
12713
  return from[0].from.table;
12539
12714
  }
12540
- let base = getBaseTable(subq[0]);
12715
+ const base = getBaseTable(subq[0]);
12541
12716
  for (let i = 1; i < subq.length; ++i) {
12542
12717
  const from = getBaseTable(subq[i]);
12543
12718
  if (from === void 0)
@@ -12561,42 +12736,6 @@ function subqueryPushdown(query, cols) {
12561
12736
  pushdown(query);
12562
12737
  }
12563
12738
 
12564
- // ../core/src/util/throttle.js
12565
- var NIL = {};
12566
- function throttle(callback, debounce = false) {
12567
- let curr;
12568
- let next;
12569
- let pending = NIL;
12570
- function invoke(event) {
12571
- curr = callback(event).then(() => {
12572
- if (next) {
12573
- const { value } = next;
12574
- next = null;
12575
- invoke(value);
12576
- } else {
12577
- curr = null;
12578
- }
12579
- });
12580
- }
12581
- function enqueue(event) {
12582
- next = { event };
12583
- }
12584
- function process(event) {
12585
- curr ? enqueue(event) : invoke(event);
12586
- }
12587
- function delay(event) {
12588
- if (pending !== event) {
12589
- requestAnimationFrame(() => {
12590
- const e = pending;
12591
- pending = NIL;
12592
- process(e);
12593
- });
12594
- }
12595
- pending = event;
12596
- }
12597
- return debounce ? delay : process;
12598
- }
12599
-
12600
12739
  // ../core/src/FilterGroup.js
12601
12740
  var FilterGroup = class {
12602
12741
  constructor(coordinator2, selection, index = true) {
@@ -12605,10 +12744,8 @@ var FilterGroup = class {
12605
12744
  this.clients = /* @__PURE__ */ new Set();
12606
12745
  this.indexer = index ? new DataTileIndexer(this.mc, selection) : null;
12607
12746
  const { value, activate } = this.handlers = {
12608
- value: throttle(() => this.update()),
12609
- activate: (clause) => {
12610
- this.indexer?.index(this.clients, clause);
12611
- }
12747
+ value: () => this.update(),
12748
+ activate: (clause) => this.indexer?.index(this.clients, clause)
12612
12749
  };
12613
12750
  selection.addEventListener("value", value);
12614
12751
  selection.addEventListener("activate", activate);
@@ -12631,7 +12768,7 @@ var FilterGroup = class {
12631
12768
  }
12632
12769
  return this;
12633
12770
  }
12634
- async update() {
12771
+ update() {
12635
12772
  const { mc, indexer, clients, selection } = this;
12636
12773
  return indexer?.index(clients) ? indexer.update() : defaultUpdate(mc, clients, selection);
12637
12774
  }
@@ -12645,79 +12782,425 @@ function defaultUpdate(mc, clients, selection) {
12645
12782
  }));
12646
12783
  }
12647
12784
 
12648
- // ../core/src/QueryCache.js
12649
- var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
12650
- var voidCache = () => ({
12651
- get: () => void 0,
12652
- set: (key, result) => result,
12653
- clear: () => {
12785
+ // ../core/src/util/query-result.js
12786
+ function queryResult() {
12787
+ let resolve;
12788
+ let reject;
12789
+ const p = new Promise((r, e) => {
12790
+ resolve = r;
12791
+ reject = e;
12792
+ });
12793
+ p.fulfill = (value) => (resolve(value), p);
12794
+ p.reject = (err) => (reject(err), p);
12795
+ return p;
12796
+ }
12797
+
12798
+ // ../core/src/QueryConsolidator.js
12799
+ function consolidator(enqueue, cache, record) {
12800
+ let pending = [];
12801
+ let id = 0;
12802
+ function run() {
12803
+ const groups = entryGroups(pending, cache);
12804
+ pending = [];
12805
+ id = 0;
12806
+ for (const group of groups) {
12807
+ consolidate(group, enqueue, record);
12808
+ processResults(group, cache);
12809
+ }
12654
12810
  }
12655
- });
12656
- var QueryCache = class {
12657
- constructor({
12658
- max: max2 = 1e3,
12659
- // max entries
12660
- ttl = 3 * 60 * 60 * 1e3
12661
- // time-to-live, default 3 hours
12662
- } = {}) {
12663
- this.max = max2;
12664
- this.ttl = ttl;
12665
- this.clear();
12811
+ return {
12812
+ add(entry, priority) {
12813
+ if (entry.request.type === "arrow") {
12814
+ id = id || requestAnimationFrame(() => run());
12815
+ pending.push({ entry, priority, index: pending.length });
12816
+ } else {
12817
+ enqueue(entry, priority);
12818
+ }
12819
+ }
12820
+ };
12821
+ }
12822
+ function entryGroups(entries, cache) {
12823
+ const groups = [];
12824
+ const groupMap = /* @__PURE__ */ new Map();
12825
+ for (const query of entries) {
12826
+ const { entry: { request } } = query;
12827
+ const key = consolidationKey(request.query, cache);
12828
+ if (!groupMap.has(key)) {
12829
+ const list = [];
12830
+ groups.push(list);
12831
+ groupMap.set(key, list);
12832
+ }
12833
+ groupMap.get(key).push(query);
12666
12834
  }
12667
- clear() {
12668
- this.cache = /* @__PURE__ */ new Map();
12835
+ return groups;
12836
+ }
12837
+ function consolidationKey(query, cache) {
12838
+ const sql2 = `${query}`;
12839
+ if (query instanceof Query && !cache.get(sql2)) {
12840
+ if (query.orderby().length || query.where().length || query.qualify().length || query.having().length) {
12841
+ return sql2;
12842
+ }
12843
+ const q = query.clone().$select("*");
12844
+ const groupby = query.groupby();
12845
+ if (groupby.length) {
12846
+ const map = {};
12847
+ query.select().forEach(({ as, expr }) => map[as] = expr);
12848
+ q.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
12849
+ }
12850
+ return `${q}`;
12851
+ } else {
12852
+ return sql2;
12669
12853
  }
12670
- get(key) {
12671
- const entry = this.cache.get(key);
12672
- if (entry) {
12673
- entry.last = performance.now();
12674
- return entry.promise;
12854
+ }
12855
+ function consolidate(group, enqueue, record) {
12856
+ if (shouldConsolidate(group)) {
12857
+ enqueue({
12858
+ request: {
12859
+ type: "arrow",
12860
+ cache: false,
12861
+ record: false,
12862
+ query: consolidatedQuery(group, record)
12863
+ },
12864
+ result: group.result = queryResult()
12865
+ });
12866
+ } else {
12867
+ for (const { entry, priority } of group) {
12868
+ enqueue(entry, priority);
12869
+ }
12870
+ }
12871
+ }
12872
+ function shouldConsolidate(group) {
12873
+ if (group.length > 1) {
12874
+ const sql2 = `${group[0].entry.request.query}`;
12875
+ for (let i = 1; i < group.length; ++i) {
12876
+ if (sql2 !== `${group[i].entry.request.query}`) {
12877
+ return true;
12878
+ }
12879
+ }
12880
+ }
12881
+ return false;
12882
+ }
12883
+ function consolidatedQuery(group, record) {
12884
+ const maps = group.maps = [];
12885
+ const fields = /* @__PURE__ */ new Map();
12886
+ for (const item of group) {
12887
+ const { query: query2 } = item.entry.request;
12888
+ const fieldMap = [];
12889
+ maps.push(fieldMap);
12890
+ for (const { as, expr } of query2.select()) {
12891
+ const e = `${expr}`;
12892
+ if (!fields.has(e)) {
12893
+ fields.set(e, [`col${fields.size}`, expr]);
12894
+ }
12895
+ const [name] = fields.get(e);
12896
+ fieldMap.push([name, as]);
12897
+ }
12898
+ record(`${query2}`);
12899
+ }
12900
+ const query = group[0].entry.request.query.clone();
12901
+ const groupby = query.groupby();
12902
+ if (groupby.length) {
12903
+ const map = {};
12904
+ group.maps[0].forEach(([name, as]) => map[as] = name);
12905
+ query.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
12906
+ }
12907
+ return query.$select(Array.from(fields.values()));
12908
+ }
12909
+ async function processResults(group, cache) {
12910
+ const { maps, result } = group;
12911
+ if (!maps)
12912
+ return;
12913
+ let data;
12914
+ try {
12915
+ data = await result;
12916
+ } catch (err) {
12917
+ for (const { entry } of group) {
12918
+ entry.result.reject(err);
12919
+ }
12920
+ return;
12921
+ }
12922
+ group.forEach(({ entry }, index) => {
12923
+ const { request, result: result2 } = entry;
12924
+ const projected = projectResult(data, maps[index]);
12925
+ if (request.cache) {
12926
+ cache.set(String(request.query), projected);
12927
+ }
12928
+ result2.fulfill(projected);
12929
+ });
12930
+ }
12931
+ function projectResult(data, map) {
12932
+ if (map) {
12933
+ const cols = {};
12934
+ for (const [name, as] of map) {
12935
+ cols[as] = data.getChild(name);
12675
12936
  }
12937
+ return new data.constructor(cols);
12938
+ } else {
12939
+ return data;
12676
12940
  }
12677
- set(key, promise) {
12678
- const { cache, max: max2 } = this;
12679
- cache.set(key, { last: performance.now(), promise });
12680
- if (cache.size > max2)
12681
- requestIdle(() => this.evict());
12682
- return promise;
12941
+ }
12942
+
12943
+ // ../core/src/util/cache.js
12944
+ var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
12945
+ var voidCache = () => ({
12946
+ get: () => void 0,
12947
+ set: (key, value) => value,
12948
+ clear: () => {
12683
12949
  }
12684
- evict() {
12685
- const expire = performance.now() - this.ttl;
12950
+ });
12951
+ function lruCache({
12952
+ max: max2 = 1e3,
12953
+ // max entries
12954
+ ttl = 3 * 60 * 60 * 1e3
12955
+ // time-to-live, default 3 hours
12956
+ } = {}) {
12957
+ let cache = /* @__PURE__ */ new Map();
12958
+ function evict() {
12959
+ const expire = performance.now() - ttl;
12686
12960
  let lruKey = null;
12687
12961
  let lruLast = Infinity;
12688
- for (const [key, value] of this.cache) {
12962
+ for (const [key, value] of cache) {
12689
12963
  const { last: last2 } = value;
12690
12964
  if (last2 < lruLast) {
12691
12965
  lruKey = key;
12692
12966
  lruLast = last2;
12693
12967
  }
12694
12968
  if (expire > last2) {
12695
- this.cache.delete(key);
12969
+ cache.delete(key);
12696
12970
  }
12697
12971
  }
12698
12972
  if (lruKey) {
12699
- this.cache.delete(lruKey);
12973
+ cache.delete(lruKey);
12700
12974
  }
12701
12975
  }
12702
- };
12703
-
12704
- // ../core/src/util/void-logger.js
12705
- function voidLogger() {
12706
12976
  return {
12707
- debug() {
12708
- },
12709
- info() {
12710
- },
12711
- log() {
12977
+ get(key) {
12978
+ const entry = cache.get(key);
12979
+ if (entry) {
12980
+ entry.last = performance.now();
12981
+ return entry.value;
12982
+ }
12712
12983
  },
12713
- warn() {
12984
+ set(key, value) {
12985
+ cache.set(key, { last: performance.now(), value });
12986
+ if (cache.size > max2)
12987
+ requestIdle(evict);
12988
+ return value;
12714
12989
  },
12715
- error() {
12990
+ clear() {
12991
+ cache = /* @__PURE__ */ new Map();
12716
12992
  }
12717
12993
  };
12718
12994
  }
12719
12995
 
12720
- // ../core/src/Coordinator.js
12996
+ // ../core/src/util/priority-queue.js
12997
+ function priorityQueue(ranks) {
12998
+ const queue = Array.from(
12999
+ { length: ranks },
13000
+ () => ({ head: null, tail: null })
13001
+ );
13002
+ return {
13003
+ /**
13004
+ * Indicate if the queue is empty.
13005
+ * @returns [boolean] true if empty, false otherwise.
13006
+ */
13007
+ isEmpty() {
13008
+ return queue.every((list) => !list.head);
13009
+ },
13010
+ /**
13011
+ * Insert an item into the queue with a given priority rank.
13012
+ * @param {*} item The item to add.
13013
+ * @param {number} rank The integer priority rank.
13014
+ * Priority ranks are integers starting at zero.
13015
+ * Lower ranks indicate higher priority.
13016
+ */
13017
+ insert(item, rank2) {
13018
+ const list = queue[rank2];
13019
+ if (!list) {
13020
+ throw new Error(`Invalid queue priority rank: ${rank2}`);
13021
+ }
13022
+ const node = { item, next: null };
13023
+ if (list.head === null) {
13024
+ list.head = list.tail = node;
13025
+ } else {
13026
+ list.tail = list.tail.next = node;
13027
+ }
13028
+ },
13029
+ /**
13030
+ * Remove a set of items from the queue, regardless of priority rank.
13031
+ * If a provided item is not in the queue it will be ignored.
13032
+ * @param {(item: *) => boolean} test A predicate function to test
13033
+ * if an item should be removed (true to drop, false to keep).
13034
+ */
13035
+ remove(test) {
13036
+ for (const list of queue) {
13037
+ let { head, tail } = list;
13038
+ for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
13039
+ if (test(curr.item)) {
13040
+ if (curr === head) {
13041
+ head = curr.next;
13042
+ } else {
13043
+ prev.next = curr.next;
13044
+ }
13045
+ if (curr === tail)
13046
+ tail = prev || head;
13047
+ }
13048
+ }
13049
+ list.head = head;
13050
+ list.tail = tail;
13051
+ }
13052
+ },
13053
+ /**
13054
+ * Remove and return the next highest priority item.
13055
+ * @returns {*} The next item in the queue,
13056
+ * or undefined if this queue is empty.
13057
+ */
13058
+ next() {
13059
+ for (const list of queue) {
13060
+ const { head } = list;
13061
+ if (head !== null) {
13062
+ list.head = head.next;
13063
+ if (list.tail === head) {
13064
+ list.tail = null;
13065
+ }
13066
+ return head.item;
13067
+ }
13068
+ }
13069
+ }
13070
+ };
13071
+ }
13072
+
13073
+ // ../core/src/QueryManager.js
13074
+ var Priority = { High: 0, Normal: 1, Low: 2 };
13075
+ function QueryManager() {
13076
+ const queue = priorityQueue(3);
13077
+ let db;
13078
+ let clientCache;
13079
+ let logger;
13080
+ let recorders = [];
13081
+ let pending = null;
13082
+ let consolidate2;
13083
+ function next() {
13084
+ if (pending || queue.isEmpty())
13085
+ return;
13086
+ const { request, result } = queue.next();
13087
+ pending = submit(request, result);
13088
+ pending.finally(() => {
13089
+ pending = null;
13090
+ next();
13091
+ });
13092
+ }
13093
+ function enqueue(entry, priority = Priority.Normal) {
13094
+ queue.insert(entry, priority);
13095
+ next();
13096
+ }
13097
+ function recordQuery(sql2) {
13098
+ if (recorders.length && sql2) {
13099
+ recorders.forEach((rec) => rec.add(sql2));
13100
+ }
13101
+ }
13102
+ async function submit(request, result) {
13103
+ try {
13104
+ const { query, type, cache = false, record = true, options } = request;
13105
+ const sql2 = query ? `${query}` : null;
13106
+ if (record) {
13107
+ recordQuery(sql2);
13108
+ }
13109
+ if (cache) {
13110
+ const cached = clientCache.get(sql2);
13111
+ if (cached) {
13112
+ logger.debug("Cache");
13113
+ result.fulfill(cached);
13114
+ return;
13115
+ }
13116
+ }
13117
+ const t0 = performance.now();
13118
+ const data = await db.query({ type, sql: sql2, ...options });
13119
+ if (cache)
13120
+ clientCache.set(sql2, data);
13121
+ logger.debug(`Request: ${(performance.now() - t0).toFixed(1)}`);
13122
+ result.fulfill(data);
13123
+ } catch (err) {
13124
+ result.reject(err);
13125
+ }
13126
+ }
13127
+ return {
13128
+ cache(value) {
13129
+ return value !== void 0 ? clientCache = value === true ? lruCache() : value || voidCache() : clientCache;
13130
+ },
13131
+ logger(value) {
13132
+ return value ? logger = value : logger;
13133
+ },
13134
+ connector(connector) {
13135
+ return connector ? db = connector : db;
13136
+ },
13137
+ consolidate(flag) {
13138
+ if (flag && !consolidate2) {
13139
+ consolidate2 = consolidator(enqueue, clientCache, recordQuery);
13140
+ } else if (!flag && consolidate2) {
13141
+ consolidate2 = null;
13142
+ }
13143
+ },
13144
+ request(request, priority = Priority.Normal) {
13145
+ const result = queryResult();
13146
+ const entry = { request, result };
13147
+ if (consolidate2) {
13148
+ consolidate2.add(entry, priority);
13149
+ } else {
13150
+ enqueue(entry, priority);
13151
+ }
13152
+ return result;
13153
+ },
13154
+ cancel(requests) {
13155
+ const set = new Set(requests);
13156
+ queue.remove(({ result }) => set.has(result));
13157
+ },
13158
+ clear() {
13159
+ queue.remove(({ result }) => {
13160
+ result.reject("Cleared");
13161
+ return true;
13162
+ });
13163
+ },
13164
+ record() {
13165
+ let state = [];
13166
+ const recorder = {
13167
+ add(query) {
13168
+ state.push(query);
13169
+ },
13170
+ reset() {
13171
+ state = [];
13172
+ },
13173
+ snapshot() {
13174
+ return state.slice();
13175
+ },
13176
+ stop() {
13177
+ recorders = recorders.filter((x) => x !== recorder);
13178
+ return state;
13179
+ }
13180
+ };
13181
+ recorders.push(recorder);
13182
+ return recorder;
13183
+ }
13184
+ };
13185
+ }
13186
+
13187
+ // ../core/src/util/void-logger.js
13188
+ function voidLogger() {
13189
+ return {
13190
+ debug() {
13191
+ },
13192
+ info() {
13193
+ },
13194
+ log() {
13195
+ },
13196
+ warn() {
13197
+ },
13198
+ error() {
13199
+ }
13200
+ };
13201
+ }
13202
+
13203
+ // ../core/src/Coordinator.js
12721
13204
  var _instance;
12722
13205
  function coordinator(instance9) {
12723
13206
  if (instance9) {
@@ -12728,21 +13211,28 @@ function coordinator(instance9) {
12728
13211
  return _instance;
12729
13212
  }
12730
13213
  var Coordinator = class {
12731
- constructor(db = socketClient(), options = {}) {
13214
+ constructor(db = socketConnector(), options = {}) {
12732
13215
  this.catalog = new Catalog(this);
13216
+ this.manager = options.manager || QueryManager();
12733
13217
  this.logger(options.logger || console);
12734
13218
  this.configure(options);
12735
- this.databaseClient(db);
13219
+ this.databaseConnector(db);
12736
13220
  this.clear();
12737
13221
  }
12738
13222
  logger(logger) {
12739
- return arguments.length ? this._logger = logger || voidLogger() : this._logger;
13223
+ if (arguments.length) {
13224
+ this._logger = logger || voidLogger();
13225
+ this.manager.logger(this._logger);
13226
+ }
13227
+ return this._logger;
12740
13228
  }
12741
- configure({ cache = true, indexes = true }) {
12742
- this.cache = cache ? new QueryCache() : voidCache();
13229
+ configure({ cache = true, consolidate: consolidate2 = true, indexes = true }) {
13230
+ this.manager.cache(cache);
13231
+ this.manager.consolidate(consolidate2);
12743
13232
  this.indexes = indexes;
12744
13233
  }
12745
13234
  clear({ clients = true, cache = true, catalog = false } = {}) {
13235
+ this.manager.clear();
12746
13236
  if (clients) {
12747
13237
  this.clients?.forEach((client) => this.disconnect(client));
12748
13238
  this.filterGroups?.forEach((group) => group.finalize());
@@ -12750,57 +13240,59 @@ var Coordinator = class {
12750
13240
  this.filterGroups = /* @__PURE__ */ new Map();
12751
13241
  }
12752
13242
  if (cache)
12753
- this.cache.clear();
13243
+ this.manager.cache().clear();
12754
13244
  if (catalog)
12755
13245
  this.catalog.clear();
12756
13246
  }
12757
- databaseClient(db) {
12758
- if (arguments.length > 0) {
12759
- this.db = db;
12760
- }
12761
- return this.db;
13247
+ databaseConnector(db) {
13248
+ return this.manager.connector(db);
12762
13249
  }
12763
- async exec(sql2) {
12764
- try {
12765
- await this.db.query({ type: "exec", sql: sql2 });
12766
- } catch (err) {
12767
- this._logger.error(err);
12768
- }
13250
+ // -- Query Management ----
13251
+ cancel(requests) {
13252
+ this.manager.cancel(requests);
12769
13253
  }
12770
- query(query, { type = "arrow", cache = true } = {}) {
12771
- const sql2 = String(query);
12772
- const t0 = performance.now();
12773
- const cached = this.cache.get(sql2);
12774
- if (cached) {
12775
- this._logger.debug("Cache");
12776
- return cached;
12777
- } else {
12778
- const request = this.db.query({ type, sql: sql2 });
12779
- const result = cache ? this.cache.set(sql2, request) : request;
12780
- result.then(() => this._logger.debug(`Query: ${performance.now() - t0}`));
12781
- return result;
12782
- }
13254
+ exec(query, { priority = Priority.Normal } = {}) {
13255
+ return this.manager.request({ type: "exec", query }, priority);
12783
13256
  }
12784
- async updateClient(client, query) {
12785
- let result;
12786
- try {
12787
- client.queryPending();
12788
- result = await this.query(query);
12789
- } catch (err) {
12790
- this._logger.error(err);
12791
- client.queryError(err);
12792
- return;
12793
- }
12794
- try {
12795
- client.queryResult(result).update();
12796
- } catch (err) {
12797
- this._logger.error(err);
12798
- }
13257
+ query(query, {
13258
+ type = "arrow",
13259
+ cache = true,
13260
+ priority = Priority.Normal,
13261
+ ...options
13262
+ } = {}) {
13263
+ return this.manager.request({ type, query, cache, options }, priority);
13264
+ }
13265
+ prefetch(query, options = {}) {
13266
+ return this.query(query, { ...options, cache: true, priority: Priority.Low });
13267
+ }
13268
+ createBundle(name, queries, priority = Priority.Low) {
13269
+ const options = { name, queries };
13270
+ return this.manager.request({ type: "create-bundle", options }, priority);
13271
+ }
13272
+ loadBundle(name, priority = Priority.High) {
13273
+ const options = { name };
13274
+ return this.manager.request({ type: "load-bundle", options }, priority);
12799
13275
  }
12800
- async requestQuery(client, query) {
13276
+ // -- Client Management ----
13277
+ updateClient(client, query, priority = Priority.Normal) {
13278
+ client.queryPending();
13279
+ return this.query(query, { priority }).then(
13280
+ (data) => client.queryResult(data).update(),
13281
+ (err) => {
13282
+ client.queryError(err);
13283
+ this._logger.error(err);
13284
+ }
13285
+ );
13286
+ }
13287
+ requestQuery(client, query) {
12801
13288
  this.filterGroups.get(client.filterBy)?.reset();
12802
13289
  return query ? this.updateClient(client, query) : client.update();
12803
13290
  }
13291
+ /**
13292
+ * Connect a client to the coordinator.
13293
+ *
13294
+ * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
13295
+ */
12804
13296
  async connect(client) {
12805
13297
  const { catalog, clients, filterGroups, indexes } = this;
12806
13298
  if (clients.has(client)) {
@@ -12809,7 +13301,7 @@ var Coordinator = class {
12809
13301
  clients.add(client);
12810
13302
  const fields = client.fields();
12811
13303
  if (fields?.length) {
12812
- client.fieldStats(await catalog.queryFields(fields));
13304
+ client.fieldInfo(await catalog.queryFields(fields));
12813
13305
  }
12814
13306
  const filter = client.filterBy;
12815
13307
  if (filter) {
@@ -12822,6 +13314,11 @@ var Coordinator = class {
12822
13314
  }
12823
13315
  client.requestQuery();
12824
13316
  }
13317
+ /**
13318
+ * Disconnect a client from the coordinator.
13319
+ *
13320
+ * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
13321
+ */
12825
13322
  disconnect(client) {
12826
13323
  const { clients, filterGroups } = this;
12827
13324
  if (!clients.has(client))
@@ -12831,6 +13328,42 @@ var Coordinator = class {
12831
13328
  }
12832
13329
  };
12833
13330
 
13331
+ // ../core/src/util/throttle.js
13332
+ var NIL = {};
13333
+ function throttle(callback, debounce = false) {
13334
+ let curr;
13335
+ let next;
13336
+ let pending = NIL;
13337
+ function invoke(event) {
13338
+ curr = callback(event).then(() => {
13339
+ if (next) {
13340
+ const { value } = next;
13341
+ next = null;
13342
+ invoke(value);
13343
+ } else {
13344
+ curr = null;
13345
+ }
13346
+ });
13347
+ }
13348
+ function enqueue(event) {
13349
+ next = { event };
13350
+ }
13351
+ function process(event) {
13352
+ curr ? enqueue(event) : invoke(event);
13353
+ }
13354
+ function delay(event) {
13355
+ if (pending !== event) {
13356
+ requestAnimationFrame(() => {
13357
+ const e = pending;
13358
+ pending = NIL;
13359
+ process(e);
13360
+ });
13361
+ }
13362
+ pending = event;
13363
+ }
13364
+ return debounce ? delay : process;
13365
+ }
13366
+
12834
13367
  // ../core/src/MosaicClient.js
12835
13368
  var MosaicClient = class {
12836
13369
  /**
@@ -12864,10 +13397,10 @@ var MosaicClient = class {
12864
13397
  return null;
12865
13398
  }
12866
13399
  /**
12867
- * Called by the coordinator to set the field statistics for this client.
13400
+ * Called by the coordinator to set the field info for this client.
12868
13401
  * @returns {this}
12869
13402
  */
12870
- fieldStats() {
13403
+ fieldInfo() {
12871
13404
  return this;
12872
13405
  }
12873
13406
  /**
@@ -12884,6 +13417,9 @@ var MosaicClient = class {
12884
13417
  }
12885
13418
  /**
12886
13419
  * Called by the coordinator to return a query result.
13420
+ *
13421
+ * @param {*} data the query result
13422
+ * @returns {this}
12887
13423
  */
12888
13424
  queryResult() {
12889
13425
  return this;
@@ -12922,6 +13458,163 @@ var MosaicClient = class {
12922
13458
  }
12923
13459
  };
12924
13460
 
13461
+ // ../core/src/util/AsyncDispatch.js
13462
+ var AsyncDispatch = class {
13463
+ /**
13464
+ * Create a new asynchronous dispatcher instance.
13465
+ */
13466
+ constructor() {
13467
+ this._callbacks = /* @__PURE__ */ new Map();
13468
+ }
13469
+ /**
13470
+ * Add an event listener callback for the provided event type.
13471
+ * @param {string} type The event type.
13472
+ * @param {(value: *) => Promise?} callback The event handler
13473
+ * callback function to add. If the callback has already been
13474
+ * added for the event type, this method has no effect.
13475
+ */
13476
+ addEventListener(type, callback) {
13477
+ if (!this._callbacks.has(type)) {
13478
+ this._callbacks.set(type, {
13479
+ callbacks: /* @__PURE__ */ new Set(),
13480
+ pending: null,
13481
+ queue: new DispatchQueue()
13482
+ });
13483
+ }
13484
+ const entry = this._callbacks.get(type);
13485
+ entry.callbacks.add(callback);
13486
+ }
13487
+ /**
13488
+ * Remove an event listener callback for the provided event type.
13489
+ * @param {string} type The event type.
13490
+ * @param {(value: *) => Promise?} callback The event handler
13491
+ * callback function to remove.
13492
+ */
13493
+ removeEventListener(type, callback) {
13494
+ const entry = this._callbacks.get(type);
13495
+ if (entry) {
13496
+ entry.callbacks.delete(callback);
13497
+ }
13498
+ }
13499
+ /**
13500
+ * Lifecycle method that returns the event value to emit.
13501
+ * This default implementation simply returns the input value as-is.
13502
+ * Subclasses may override this method to implement custom transformations
13503
+ * prior to emitting an event value to all listeners.
13504
+ * @param {string} type The event type.
13505
+ * @param {*} value The event value.
13506
+ * @returns The (possibly transformed) event value to emit.
13507
+ */
13508
+ willEmit(type, value) {
13509
+ return value;
13510
+ }
13511
+ /**
13512
+ * Lifecycle method that returns a filter function for updating the
13513
+ * queue of unemitted event values prior to enqueueing a new value.
13514
+ * This default implementation simply returns null, indicating that
13515
+ * any other unemitted event values should be dropped (that is, all
13516
+ * queued events are filtered)
13517
+ * @param {*} value The new event value that will be enqueued.
13518
+ * @returns {(value: *) => boolean|null} A dispatch queue filter
13519
+ * function, or null if all unemitted event values should be filtered.
13520
+ */
13521
+ emitQueueFilter() {
13522
+ return null;
13523
+ }
13524
+ /**
13525
+ * Cancel all unemitted event values for the given event type.
13526
+ * @param {string} type The event type.
13527
+ */
13528
+ cancel(type) {
13529
+ const entry = this._callbacks.get(type);
13530
+ entry?.queue.clear();
13531
+ }
13532
+ /**
13533
+ * Emit an event value to listeners for the given event type.
13534
+ * If a previous emit has not yet resolved, the event value
13535
+ * will be queued to be emitted later.
13536
+ * The actual event value given to listeners will be the result
13537
+ * of passing the input value through the emitValue() method.
13538
+ * @param {string} type The event type.
13539
+ * @param {*} value The event value.
13540
+ */
13541
+ emit(type, value) {
13542
+ const entry = this._callbacks.get(type) || {};
13543
+ if (entry.pending) {
13544
+ entry.queue.enqueue(value, this.emitQueueFilter(type, value));
13545
+ } else {
13546
+ const event = this.willEmit(type, value);
13547
+ const { callbacks, queue } = entry;
13548
+ if (callbacks?.size) {
13549
+ const promise = Promise.allSettled(Array.from(callbacks, (callback) => callback(event))).then(() => {
13550
+ entry.pending = null;
13551
+ if (!queue.isEmpty()) {
13552
+ this.emit(type, queue.dequeue());
13553
+ }
13554
+ });
13555
+ entry.pending = promise;
13556
+ }
13557
+ }
13558
+ }
13559
+ };
13560
+ var DispatchQueue = class {
13561
+ /**
13562
+ * Create a new dispatch queue instance.
13563
+ */
13564
+ constructor() {
13565
+ this.clear();
13566
+ }
13567
+ /**
13568
+ * Clear the queue state of all event values.
13569
+ */
13570
+ clear() {
13571
+ this.next = null;
13572
+ }
13573
+ /**
13574
+ * Indicate if the queue is empty.
13575
+ * @returns {boolean} True if queue is empty, false otherwise.
13576
+ */
13577
+ isEmpty() {
13578
+ return !this.next;
13579
+ }
13580
+ /**
13581
+ * Add a new value to the queue, and optionally filter the
13582
+ * current queue content in response.
13583
+ * @param {*} value The value to add.
13584
+ * @param {(value: *) => boolean} [filter] An optional filter
13585
+ * function to apply to existing queue content. If unspecified
13586
+ * or falsy, all previously queued values are removed. Otherwise,
13587
+ * the provided function is applied to all queue entries. The
13588
+ * entry is retained if the filter function returns a truthy value,
13589
+ * otherwise the entry is removed.
13590
+ */
13591
+ enqueue(value, filter) {
13592
+ const tail = { value };
13593
+ if (filter && this.next) {
13594
+ let curr = this;
13595
+ while (curr.next) {
13596
+ if (filter(curr.next.value)) {
13597
+ curr = curr.next;
13598
+ } else {
13599
+ curr.next = curr.next.next;
13600
+ }
13601
+ }
13602
+ curr.next = tail;
13603
+ } else {
13604
+ this.next = tail;
13605
+ }
13606
+ }
13607
+ /**
13608
+ * Remove and return the next queued event value.
13609
+ * @returns {*} The next event value in the queue.
13610
+ */
13611
+ dequeue() {
13612
+ const { next } = this;
13613
+ this.next = next?.next;
13614
+ return next?.value;
13615
+ }
13616
+ };
13617
+
12925
13618
  // ../core/src/util/distinct.js
12926
13619
  function distinct(a, b) {
12927
13620
  return a === b ? false : a instanceof Date && b instanceof Date ? +a !== +b : Array.isArray(a) && Array.isArray(b) ? distinctArray(a, b) : true;
@@ -12940,40 +13633,74 @@ function distinctArray(a, b) {
12940
13633
  function isParam(x) {
12941
13634
  return x instanceof Param;
12942
13635
  }
12943
- var Param = class {
13636
+ var Param = class extends AsyncDispatch {
13637
+ /**
13638
+ * Create a new Param instance.
13639
+ * @param {*} value The initial value of the Param.
13640
+ */
12944
13641
  constructor(value) {
13642
+ super();
12945
13643
  this._value = value;
12946
- this._listeners = /* @__PURE__ */ new Map();
12947
13644
  }
13645
+ /**
13646
+ * Create a new Param instance with the given initial value.
13647
+ * @param {*} value The initial value of the Param.
13648
+ * @returns {Param} The new Param instance.
13649
+ */
12948
13650
  static value(value) {
12949
13651
  return new Param(value);
12950
13652
  }
13653
+ /**
13654
+ * Create a new Param instance over an array of initial values,
13655
+ * which may contain nested Params.
13656
+ * @param {*} values The initial values of the Param.
13657
+ * @returns {Param} The new Param instance.
13658
+ */
13659
+ static array(values) {
13660
+ if (values.some((v) => isParam(v))) {
13661
+ const p = new Param();
13662
+ const update2 = () => p.update(values.map((v) => isParam(v) ? v.value : v));
13663
+ update2();
13664
+ values.forEach((v) => isParam(v) ? v.addEventListener("value", update2) : 0);
13665
+ return p;
13666
+ }
13667
+ return new Param(values);
13668
+ }
13669
+ /**
13670
+ * The current value of the Param.
13671
+ */
12951
13672
  get value() {
12952
13673
  return this._value;
12953
13674
  }
13675
+ /**
13676
+ * Update the Param value
13677
+ * @param {*} value The new value of the Param.
13678
+ * @param {object} [options] The update options.
13679
+ * @param {boolean} [options.force] A boolean flag indicating if the Param
13680
+ * should emit a 'value' event even if the internal value is unchanged.
13681
+ * @returns {this} This Param instance.
13682
+ */
12954
13683
  update(value, { force } = {}) {
12955
- const changed = distinct(this._value, value);
12956
- if (changed)
12957
- this._value = value;
12958
- if (changed || force)
12959
- this.emit("value", this.value);
12960
- return this;
12961
- }
12962
- addEventListener(type, callback) {
12963
- let list = this._listeners.get(type) || [];
12964
- if (!list.includes(callback)) {
12965
- list = list.concat(callback);
13684
+ const shouldEmit = distinct(this._value, value) || force;
13685
+ if (shouldEmit) {
13686
+ this.emit("value", value);
13687
+ } else {
13688
+ this.cancel("value");
12966
13689
  }
12967
- this._listeners.set(type, list);
13690
+ return this;
12968
13691
  }
12969
- removeEventListener(type, callback) {
12970
- const list = this._listeners.get(type);
12971
- if (list?.length) {
12972
- this._listeners.set(type, list.filter((x) => x !== callback));
13692
+ /**
13693
+ * Upon value-typed updates, sets the current value to the input value
13694
+ * immediately prior to the event value being emitted to listeners.
13695
+ * @param {string} type The event type.
13696
+ * @param {*} value The input event value.
13697
+ * @returns {*} The input event value.
13698
+ */
13699
+ willEmit(type, value) {
13700
+ if (type === "value") {
13701
+ this._value = value;
12973
13702
  }
12974
- }
12975
- emit(type, event) {
12976
- this._listeners.get(type)?.forEach((l) => l(event));
13703
+ return value;
12977
13704
  }
12978
13705
  };
12979
13706
 
@@ -12982,66 +13709,254 @@ function isSelection(x) {
12982
13709
  return x instanceof Selection;
12983
13710
  }
12984
13711
  var Selection = class extends Param {
12985
- static intersect() {
12986
- return new Selection();
13712
+ /**
13713
+ * Create a new Selection instance with an
13714
+ * intersect (conjunction) resolution strategy.
13715
+ * @param {object} [options] The selection options.
13716
+ * @param {boolean} [options.cross=false] Boolean flag indicating
13717
+ * cross-filtered resolution. If true, selection clauses will not
13718
+ * be applied to the clients they are associated with.
13719
+ * @returns {Selection} The new Selection instance.
13720
+ */
13721
+ static intersect({ cross = false } = {}) {
13722
+ return new Selection(new SelectionResolver({ cross }));
12987
13723
  }
12988
- static crossfilter() {
12989
- return new Selection({ cross: true });
13724
+ /**
13725
+ * Create a new Selection instance with a
13726
+ * union (disjunction) resolution strategy.
13727
+ * @param {object} [options] The selection options.
13728
+ * @param {boolean} [options.cross=false] Boolean flag indicating
13729
+ * cross-filtered resolution. If true, selection clauses will not
13730
+ * be applied to the clients they are associated with.
13731
+ * @returns {Selection} The new Selection instance.
13732
+ */
13733
+ static union({ cross = false } = {}) {
13734
+ return new Selection(new SelectionResolver({ cross, union: true }));
12990
13735
  }
12991
- static union() {
12992
- return new Selection({ union: true });
13736
+ /**
13737
+ * Create a new Selection instance with a singular resolution strategy
13738
+ * that keeps only the most recent selection clause.
13739
+ * @param {object} [options] The selection options.
13740
+ * @param {boolean} [options.cross=false] Boolean flag indicating
13741
+ * cross-filtered resolution. If true, selection clauses will not
13742
+ * be applied to the clients they are associated with.
13743
+ * @returns {Selection} The new Selection instance.
13744
+ */
13745
+ static single({ cross = false } = {}) {
13746
+ return new Selection(new SelectionResolver({ cross, single: true }));
12993
13747
  }
12994
- static single() {
12995
- return new Selection({ single: true });
13748
+ /**
13749
+ * Create a new Selection instance with a
13750
+ * cross-filtered intersect resolution strategy.
13751
+ * @returns {Selection} The new Selection instance.
13752
+ */
13753
+ static crossfilter() {
13754
+ return new Selection(new SelectionResolver({ cross: true }));
12996
13755
  }
12997
- constructor({ union, cross, single } = {}) {
13756
+ /**
13757
+ * Create a new Selection instance.
13758
+ * @param {SelectionResolver} resolver The selection resolution
13759
+ * strategy to apply.
13760
+ */
13761
+ constructor(resolver = new SelectionResolver()) {
12998
13762
  super([]);
12999
- this.active = null;
13000
- this.union = !!union;
13001
- this.cross = !!cross;
13002
- this.single = !!single;
13763
+ this._resolved = this._value;
13764
+ this._resolver = resolver;
13003
13765
  }
13766
+ /**
13767
+ * Create a cloned copy of this Selection instance.
13768
+ * @returns {this} A clone of this selection.
13769
+ */
13004
13770
  clone() {
13005
- const s = new Selection();
13006
- s.active = this.active;
13007
- s.union = this.union;
13008
- s.cross = this.cross;
13009
- s._value = this._value;
13771
+ const s = new Selection(this._resolver);
13772
+ s._value = s._resolved = this._value;
13773
+ return s;
13774
+ }
13775
+ /**
13776
+ * Create a clone of this Selection with clauses corresponding
13777
+ * to provided source removed.
13778
+ * @param {*} source The clause source to remove.
13779
+ * @returns {this} A cloned and updated Selection.
13780
+ */
13781
+ remove(source) {
13782
+ const s = this.clone();
13783
+ s._value = s._resolved = s._resolver.resolve(this._resolved, { source });
13784
+ s._value.active = { source };
13010
13785
  return s;
13011
13786
  }
13787
+ /**
13788
+ * The current active (most recently updated) selection clause.
13789
+ */
13790
+ get active() {
13791
+ return this.clauses.active;
13792
+ }
13793
+ /**
13794
+ * The value corresponding to the current active selection clause.
13795
+ * This method ensures compatibility where a normal Param is expected.
13796
+ */
13012
13797
  get value() {
13013
- const { clauses } = this;
13014
- return clauses[clauses.length - 1]?.value;
13798
+ return this.active?.value;
13015
13799
  }
13800
+ /**
13801
+ * The current array of selection clauses.
13802
+ */
13016
13803
  get clauses() {
13017
13804
  return super.value;
13018
13805
  }
13806
+ /**
13807
+ * Indicate if this selection has a single resolution strategy.
13808
+ */
13809
+ get single() {
13810
+ return this._resolver.single;
13811
+ }
13812
+ /**
13813
+ * Emit an activate event with the given selection clause.
13814
+ * @param {*} clause The clause repesenting the potential activation.
13815
+ */
13019
13816
  activate(clause) {
13020
13817
  this.emit("activate", clause);
13021
13818
  }
13819
+ /**
13820
+ * Update the selection with a new selection clause.
13821
+ * @param {*} clause The selection clause to add.
13822
+ * @returns {this} This Selection instance.
13823
+ */
13022
13824
  update(clause) {
13825
+ this._resolved = this._resolver.resolve(this._resolved, clause, true);
13826
+ this._resolved.active = clause;
13827
+ return super.update(this._resolved);
13828
+ }
13829
+ /**
13830
+ * Upon value-typed updates, sets the current clause list to the
13831
+ * input value and returns the active clause value.
13832
+ * @param {string} type The event type.
13833
+ * @param {*} value The input event value.
13834
+ * @returns {*} For value-typed events, returns the active clause
13835
+ * values. Otherwise returns the input event value as-is.
13836
+ */
13837
+ willEmit(type, value) {
13838
+ if (type === "value") {
13839
+ this._value = value;
13840
+ return this.value;
13841
+ }
13842
+ return value;
13843
+ }
13844
+ /**
13845
+ * Upon value-typed updates, returns a dispatch queue filter function.
13846
+ * The return value depends on the selection resolution strategy.
13847
+ * @param {string} type The event type.
13848
+ * @param {*} value The input event value.
13849
+ * @returns {*} For value-typed events, returns a dispatch queue filter
13850
+ * function. Otherwise returns null.
13851
+ */
13852
+ emitQueueFilter(type, value) {
13853
+ return type === "value" ? this._resolver.queueFilter(value) : null;
13854
+ }
13855
+ /**
13856
+ * Indicates if a selection clause should not be applied to a given client.
13857
+ * The return value depends on the selection resolution strategy.
13858
+ * @param {*} client The selection clause.
13859
+ * @param {*} clause The client to test.
13860
+ * @returns True if the client should be skipped, false otherwise.
13861
+ */
13862
+ skip(client, clause) {
13863
+ return this._resolver.skip(client, clause);
13864
+ }
13865
+ /**
13866
+ * Return a selection query predicate for the given client.
13867
+ * @param {*} client The client whose data may be filtered.
13868
+ * @returns {*} The query predicate for filtering client data,
13869
+ * based on the current state of this selection.
13870
+ */
13871
+ predicate(client) {
13872
+ const { clauses } = this;
13873
+ return this._resolver.predicate(clauses, clauses.active, client);
13874
+ }
13875
+ };
13876
+ var SelectionResolver = class {
13877
+ /**
13878
+ * Create a new selection resolved instance.
13879
+ * @param {object} [options] The resolution strategy options.
13880
+ * @param {boolean} [options.union=false] Boolean flag to indicate a union strategy.
13881
+ * If false, an intersection strategy is used.
13882
+ * @param {boolean} [options.cross=false] Boolean flag to indicate cross-filtering.
13883
+ * @param {boolean} [options.single=false] Boolean flag to indicate single clauses only.
13884
+ */
13885
+ constructor({ union, cross, single } = {}) {
13886
+ this.union = !!union;
13887
+ this.cross = !!cross;
13888
+ this.single = !!single;
13889
+ }
13890
+ /**
13891
+ * Resolve a list of selection clauses according to the resolution strategy.
13892
+ * @param {*[]} clauseList An array of selection clauses.
13893
+ * @param {*} clause A new selection clause to add.
13894
+ * @returns {*[]} An updated array of selection clauses.
13895
+ */
13896
+ resolve(clauseList, clause, reset = false) {
13023
13897
  const { source, predicate } = clause;
13024
- this.active = clause;
13025
- const clauses = this.single ? [] : this.clauses.filter((c) => source !== c.source);
13898
+ const filtered = clauseList.filter((c) => source !== c.source);
13899
+ const clauses = this.single ? [] : filtered;
13900
+ if (this.single && reset)
13901
+ filtered.forEach((c) => c.source?.reset?.());
13026
13902
  if (predicate)
13027
13903
  clauses.push(clause);
13028
- return super.update(clauses);
13904
+ return clauses;
13029
13905
  }
13030
- predicate(client) {
13031
- const { active, clauses, cross, union } = this;
13032
- if (cross && skipClient(client, active))
13906
+ /**
13907
+ * Indicates if a selection clause should not be applied to a given client.
13908
+ * The return value depends on the resolution strategy.
13909
+ * @param {*} client The selection clause.
13910
+ * @param {*} clause The client to test.
13911
+ * @returns True if the client should be skipped, false otherwise.
13912
+ */
13913
+ skip(client, clause) {
13914
+ return this.cross && clause?.clients?.has(client);
13915
+ }
13916
+ /**
13917
+ * Return a selection query predicate for the given client.
13918
+ * @param {*[]} clauseList An array of selection clauses.
13919
+ * @param {*} active The current active selection clause.
13920
+ * @param {*} client The client whose data may be filtered.
13921
+ * @returns {*} The query predicate for filtering client data,
13922
+ * based on the current state of this selection.
13923
+ */
13924
+ predicate(clauseList, active, client) {
13925
+ const { union } = this;
13926
+ if (this.skip(client, active))
13033
13927
  return void 0;
13034
- const list = (cross ? clauses.filter((clause) => !skipClient(client, clause)) : clauses).map((s) => s.predicate);
13035
- return union && list.length > 1 ? or(list) : list;
13928
+ const predicates = clauseList.filter((clause) => !this.skip(client, clause)).map((clause) => clause.predicate);
13929
+ return union && predicates.length > 1 ? or(predicates) : predicates;
13930
+ }
13931
+ /**
13932
+ * Returns a filter function for queued selection updates.
13933
+ * @param {*} value The new event value that will be enqueued.
13934
+ * @returns {(value: *) => boolean|null} A dispatch queue filter
13935
+ * function, or null if all unemitted event values should be filtered.
13936
+ */
13937
+ queueFilter(value) {
13938
+ if (this.cross) {
13939
+ const source = value.active?.source;
13940
+ return (clauses) => clauses.active?.source !== source;
13941
+ }
13036
13942
  }
13037
13943
  };
13038
13944
 
13945
+ // src/input.js
13946
+ function input(InputClass, options) {
13947
+ const input2 = new InputClass(options);
13948
+ coordinator().connect(input2);
13949
+ return input2.element;
13950
+ }
13951
+
13039
13952
  // src/Menu.js
13040
13953
  var isObject2 = (v) => {
13041
13954
  return v && typeof v === "object" && !Array.isArray(v);
13042
13955
  };
13956
+ var menu = (options) => input(Menu, options);
13043
13957
  var Menu = class extends MosaicClient {
13044
13958
  constructor({
13959
+ element,
13045
13960
  filterBy,
13046
13961
  from,
13047
13962
  column: column2,
@@ -13057,7 +13972,7 @@ var Menu = class extends MosaicClient {
13057
13972
  this.column = column2;
13058
13973
  this.selection = as;
13059
13974
  this.format = format2;
13060
- this.element = document.createElement("div");
13975
+ this.element = element ?? document.createElement("div");
13061
13976
  this.element.setAttribute("class", "input");
13062
13977
  this.element.value = this;
13063
13978
  const lab = document.createElement("label");
@@ -13069,13 +13984,12 @@ var Menu = class extends MosaicClient {
13069
13984
  this.update();
13070
13985
  }
13071
13986
  value = value ?? this.selection?.value ?? this.data?.[0]?.value;
13072
- this.select.value = value;
13073
13987
  if (this.selection?.value === void 0)
13074
13988
  this.publish(value);
13075
13989
  this.element.appendChild(this.select);
13076
13990
  if (this.selection) {
13077
13991
  this.select.addEventListener("input", () => {
13078
- this.publish(this.selectedValue() || null);
13992
+ this.publish(this.selectedValue() ?? null);
13079
13993
  });
13080
13994
  if (!isSelection(this.selection)) {
13081
13995
  this.selection.addEventListener("value", (value2) => {
@@ -13091,9 +14005,17 @@ var Menu = class extends MosaicClient {
13091
14005
  const index = this.select.selectedIndex;
13092
14006
  return this.data[index].value;
13093
14007
  } else {
13094
- this.select.value = String(value);
14008
+ const index = this.data?.findIndex((opt) => opt.value === value);
14009
+ if (index >= 0) {
14010
+ this.select.selectedIndex = index;
14011
+ } else {
14012
+ this.select.value = String(value);
14013
+ }
13095
14014
  }
13096
14015
  }
14016
+ reset() {
14017
+ this.select.selectedIndex = this.from ? 0 : -1;
14018
+ }
13097
14019
  publish(value) {
13098
14020
  const { selection, column: column2 } = this;
13099
14021
  if (isSelection(selection)) {
@@ -13127,7 +14049,7 @@ var Menu = class extends MosaicClient {
13127
14049
  this.select.appendChild(opt);
13128
14050
  }
13129
14051
  if (this.selection) {
13130
- this.select.value = this.selection?.value || "";
14052
+ this.selectedValue(this.selection?.value ?? "");
13131
14053
  }
13132
14054
  return this;
13133
14055
  }
@@ -13136,13 +14058,15 @@ var Menu = class extends MosaicClient {
13136
14058
  // src/Search.js
13137
14059
  var FUNCTIONS = { contains, prefix, suffix, regexp: regexp_matches };
13138
14060
  var _id = 0;
14061
+ var search = (options) => input(Search, options);
13139
14062
  var Search = class extends MosaicClient {
13140
14063
  constructor({
14064
+ element,
13141
14065
  filterBy,
13142
14066
  from,
13143
14067
  column: column2,
13144
14068
  label,
13145
- type,
14069
+ type = "contains",
13146
14070
  as
13147
14071
  } = {}) {
13148
14072
  super(filterBy);
@@ -13151,7 +14075,7 @@ var Search = class extends MosaicClient {
13151
14075
  this.from = from;
13152
14076
  this.column = column2;
13153
14077
  this.selection = as;
13154
- this.element = document.createElement("div");
14078
+ this.element = element ?? document.createElement("div");
13155
14079
  this.element.setAttribute("class", "input");
13156
14080
  this.element.value = this;
13157
14081
  if (label) {
@@ -13178,6 +14102,9 @@ var Search = class extends MosaicClient {
13178
14102
  }
13179
14103
  }
13180
14104
  }
14105
+ reset() {
14106
+ this.searchbox.value = "";
14107
+ }
13181
14108
  publish(value) {
13182
14109
  const { selection, column: column2, type } = this;
13183
14110
  if (isSelection(selection)) {
@@ -13220,8 +14147,10 @@ var Search = class extends MosaicClient {
13220
14147
 
13221
14148
  // src/Slider.js
13222
14149
  var _id2 = 0;
14150
+ var slider = (options) => input(Slider, options);
13223
14151
  var Slider = class extends MosaicClient {
13224
14152
  constructor({
14153
+ element,
13225
14154
  filterBy,
13226
14155
  as,
13227
14156
  min: min2,
@@ -13230,7 +14159,8 @@ var Slider = class extends MosaicClient {
13230
14159
  from,
13231
14160
  column: column2,
13232
14161
  label = column2,
13233
- value = as?.value
14162
+ value = as?.value,
14163
+ width
13234
14164
  } = {}) {
13235
14165
  super(filterBy);
13236
14166
  this.id = "slider_" + ++_id2;
@@ -13240,7 +14170,7 @@ var Slider = class extends MosaicClient {
13240
14170
  this.min = min2;
13241
14171
  this.max = max2;
13242
14172
  this.step = step;
13243
- this.element = document.createElement("div");
14173
+ this.element = element || document.createElement("div");
13244
14174
  this.element.setAttribute("class", "input");
13245
14175
  this.element.value = this;
13246
14176
  if (label) {
@@ -13252,6 +14182,8 @@ var Slider = class extends MosaicClient {
13252
14182
  this.slider = document.createElement("input");
13253
14183
  this.slider.setAttribute("id", this.id);
13254
14184
  this.slider.setAttribute("type", "range");
14185
+ if (width != null)
14186
+ this.slider.style.width = `${+width}px`;
13255
14187
  if (min2 != null)
13256
14188
  this.slider.setAttribute("min", min2);
13257
14189
  if (max2 != null)
@@ -13347,33 +14279,40 @@ function localize(f) {
13347
14279
 
13348
14280
  // src/Table.js
13349
14281
  var _id3 = -1;
14282
+ var table = (options) => input(Table2, options);
13350
14283
  var Table2 = class extends MosaicClient {
13351
14284
  constructor({
14285
+ element,
13352
14286
  filterBy,
13353
14287
  from,
13354
14288
  columns = ["*"],
14289
+ align = {},
13355
14290
  format: format2,
13356
- rowBatch = 100,
13357
14291
  width,
13358
- height = 500
14292
+ maxWidth,
14293
+ height = 500,
14294
+ rowBatch = 100
13359
14295
  } = {}) {
13360
14296
  super(filterBy);
13361
14297
  this.id = `table-${++_id3}`;
13362
14298
  this.from = from;
13363
14299
  this.columns = columns;
13364
14300
  this.format = format2;
14301
+ this.align = align;
14302
+ this.widths = typeof width === "object" ? width : {};
13365
14303
  this.offset = 0;
13366
14304
  this.limit = +rowBatch;
13367
14305
  this.pending = false;
13368
14306
  this.sortHeader = null;
13369
14307
  this.sortColumn = null;
13370
14308
  this.sortDesc = false;
13371
- this.element = document.createElement("div");
14309
+ this.element = element || document.createElement("div");
13372
14310
  this.element.setAttribute("id", this.id);
13373
14311
  this.element.value = this;
13374
- if (width) {
13375
- this.element.style.maxWidth = `${width}px`;
13376
- }
14312
+ if (typeof width === "number")
14313
+ this.element.style.width = `${width}px`;
14314
+ if (maxWidth)
14315
+ this.element.style.maxWidth = `${maxWidth}px`;
13377
14316
  this.element.style.maxHeight = `${height}px`;
13378
14317
  this.element.style.overflow = "auto";
13379
14318
  let prevScrollTop = -1;
@@ -13386,9 +14325,7 @@ var Table2 = class extends MosaicClient {
13386
14325
  return;
13387
14326
  if (scrollHeight - scrollTop < 2 * clientHeight) {
13388
14327
  this.pending = true;
13389
- this.offset += this.limit;
13390
- const query = this.queryInternal(this.filterBy?.predicate(this));
13391
- this.requestQuery(query);
14328
+ this.requestData(this.offset + this.limit);
13392
14329
  }
13393
14330
  });
13394
14331
  this.tbl = document.createElement("table");
@@ -13400,15 +14337,21 @@ var Table2 = class extends MosaicClient {
13400
14337
  this.style = document.createElement("style");
13401
14338
  this.element.appendChild(this.style);
13402
14339
  }
14340
+ requestData(offset = 0) {
14341
+ this.offset = offset;
14342
+ const query = this.query(this.filterBy?.predicate(this));
14343
+ this.requestQuery(query);
14344
+ coordinator().prefetch(query.clone().offset(offset + this.limit));
14345
+ }
13403
14346
  fields() {
13404
14347
  return this.columns.map((name) => column(this.from, name));
13405
14348
  }
13406
- fieldStats(stats) {
13407
- this.stats = stats;
14349
+ fieldInfo(info) {
14350
+ this.schema = info;
13408
14351
  const thead = this.head;
13409
14352
  thead.innerHTML = "";
13410
14353
  const tr = document.createElement("tr");
13411
- for (const { column: column2 } of stats) {
14354
+ for (const { column: column2 } of info) {
13412
14355
  const th = document.createElement("th");
13413
14356
  th.addEventListener("click", (evt) => this.sort(evt, column2));
13414
14357
  th.appendChild(document.createElement("span"));
@@ -13416,17 +14359,17 @@ var Table2 = class extends MosaicClient {
13416
14359
  tr.appendChild(th);
13417
14360
  }
13418
14361
  thead.appendChild(tr);
13419
- this.formats = formatof(this.format, stats);
13420
- this.style.innerText = tableCSS(this.id, alignof({}, stats));
14362
+ this.formats = formatof(this.format, info);
14363
+ this.style.innerText = tableCSS(
14364
+ this.id,
14365
+ alignof(this.align, info),
14366
+ widthof(this.widths, info)
14367
+ );
13421
14368
  return this;
13422
14369
  }
13423
- query(filter) {
13424
- this.offset = 0;
13425
- return this.queryInternal(filter);
13426
- }
13427
- queryInternal(filter = []) {
13428
- const { from, limit, offset, stats, sortColumn, sortDesc } = this;
13429
- return Query.from(from).select(stats.map((s) => s.column)).where(filter).orderby(sortColumn ? sortDesc ? desc(sortColumn) : sortColumn : []).limit(limit).offset(offset);
14370
+ query(filter = []) {
14371
+ const { from, limit, offset, schema, sortColumn, sortDesc } = this;
14372
+ return Query.from(from).select(schema.map((s) => s.column)).where(filter).orderby(sortColumn ? sortDesc ? desc(sortColumn) : sortColumn : []).limit(limit).offset(offset);
13430
14373
  }
13431
14374
  queryResult(data) {
13432
14375
  if (!this.pending) {
@@ -13437,14 +14380,14 @@ var Table2 = class extends MosaicClient {
13437
14380
  return this;
13438
14381
  }
13439
14382
  update() {
13440
- const { body, formats, data, stats, limit } = this;
13441
- const nf = stats.length;
14383
+ const { body, formats, data, schema, limit } = this;
14384
+ const nf = schema.length;
13442
14385
  let count2 = 0;
13443
14386
  for (const row of data) {
13444
14387
  ++count2;
13445
14388
  const tr = document.createElement("tr");
13446
14389
  for (let i = 0; i < nf; ++i) {
13447
- const value = row[stats[i].column];
14390
+ const value = row[schema[i].column];
13448
14391
  const td = document.createElement("td");
13449
14392
  td.innerText = value == null ? "" : formats[i](value);
13450
14393
  tr.appendChild(td);
@@ -13476,12 +14419,11 @@ var Table2 = class extends MosaicClient {
13476
14419
  this.sortHeader = th;
13477
14420
  th.firstChild.textContent = this.sortDesc ? "\u25BE" : "\u25B4";
13478
14421
  }
13479
- const query = this.query(this.filterBy?.predicate(this));
13480
- this.requestQuery(query);
14422
+ this.requestData();
13481
14423
  }
13482
14424
  };
13483
- function formatof(base = {}, stats, locale) {
13484
- return stats.map(({ column: column2, type }) => {
14425
+ function formatof(base = {}, schema, locale) {
14426
+ return schema.map(({ column: column2, type }) => {
13485
14427
  if (column2 in base) {
13486
14428
  return base[column2];
13487
14429
  } else {
@@ -13496,8 +14438,8 @@ function formatof(base = {}, stats, locale) {
13496
14438
  }
13497
14439
  });
13498
14440
  }
13499
- function alignof(base = {}, stats) {
13500
- return stats.map(({ column: column2, type }) => {
14441
+ function alignof(base = {}, schema) {
14442
+ return schema.map(({ column: column2, type }) => {
13501
14443
  if (column2 in base) {
13502
14444
  return base[column2];
13503
14445
  } else if (type === "number") {
@@ -13507,11 +14449,17 @@ function alignof(base = {}, stats) {
13507
14449
  }
13508
14450
  });
13509
14451
  }
13510
- function tableCSS(id, align) {
14452
+ function widthof(base = {}, schema) {
14453
+ return schema.map(({ column: column2 }) => base[column2]);
14454
+ }
14455
+ function tableCSS(id, aligns, widths) {
13511
14456
  const styles = [];
13512
- align.forEach((a, i) => {
13513
- if (a !== "left") {
13514
- styles.push(`#${id} tr>:nth-child(${i + 1}) {text-align:${a}}`);
14457
+ aligns.forEach((a, i) => {
14458
+ const w = +widths[i];
14459
+ if (a !== "left" || w) {
14460
+ const align = a !== "left" ? `text-align:${a};` : "";
14461
+ const width = w ? `width:${w}px;max-width:${w}px;` : "";
14462
+ styles.push(`#${id} tr>:nth-child(${i + 1}) {${align}${width}}`);
13515
14463
  }
13516
14464
  });
13517
14465
  return styles.join(" ");
@@ -13520,5 +14468,9 @@ export {
13520
14468
  Menu,
13521
14469
  Search,
13522
14470
  Slider,
13523
- Table2 as Table
14471
+ Table2 as Table,
14472
+ menu,
14473
+ search,
14474
+ slider,
14475
+ table
13524
14476
  };