@uwdata/mosaic-core 0.8.0 → 0.9.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.
@@ -4914,10 +4914,10 @@ var getDictionary = (data, index) => {
4914
4914
  var getInterval = (data, index) => data.type.unit === IntervalUnit.DAY_TIME ? getIntervalDayTime(data, index) : getIntervalYearMonth(data, index);
4915
4915
  var getIntervalDayTime = ({ values }, index) => values.subarray(2 * index, 2 * (index + 1));
4916
4916
  var getIntervalYearMonth = ({ values }, index) => {
4917
- const interval = values[index];
4917
+ const interval2 = values[index];
4918
4918
  const int32s = new Int32Array(2);
4919
- int32s[0] = Math.trunc(interval / 12);
4920
- int32s[1] = Math.trunc(interval % 12);
4919
+ int32s[0] = Math.trunc(interval2 / 12);
4920
+ int32s[1] = Math.trunc(interval2 % 12);
4921
4921
  return int32s;
4922
4922
  };
4923
4923
  var getDurationSecond = ({ values }, index) => values[index];
@@ -11839,6 +11839,13 @@ function socketConnector(uri = "ws://localhost:3000/") {
11839
11839
  get connected() {
11840
11840
  return connected;
11841
11841
  },
11842
+ /**
11843
+ * Query the DuckDB server.
11844
+ * @param {object} query
11845
+ * @param {'exec' | 'arrow' | 'json'} [query.type] The query type: 'exec', 'arrow', or 'json'.
11846
+ * @param {string} query.sql A SQL query string.
11847
+ * @returns the query result
11848
+ */
11842
11849
  query(query) {
11843
11850
  return new Promise(
11844
11851
  (resolve, reject) => enqueue(query, resolve, reject)
@@ -11909,7 +11916,7 @@ function literalToSQL(value) {
11909
11916
  case "boolean":
11910
11917
  return value ? "TRUE" : "FALSE";
11911
11918
  case "string":
11912
- return `'${value}'`;
11919
+ return `'${value.replace(`'`, `''`)}'`;
11913
11920
  case "number":
11914
11921
  return Number.isFinite(value) ? String(value) : "NULL";
11915
11922
  default:
@@ -11992,7 +11999,7 @@ var SQLExpression = class {
11992
11999
  /**
11993
12000
  * Annotate this expression instance with additional properties.
11994
12001
  * @param {object[]} [props] One or more objects with properties to add.
11995
- * @returns {this} This SQL expression.
12002
+ * @returns This SQL expression.
11996
12003
  */
11997
12004
  annotate(...props) {
11998
12005
  return Object.assign(this, ...props);
@@ -12050,6 +12057,12 @@ function sql(strings, ...exprs) {
12050
12057
  return new SQLExpression(spans, cols);
12051
12058
  }
12052
12059
 
12060
+ // ../sql/src/literal.js
12061
+ var literal = (value) => ({
12062
+ value,
12063
+ toString: () => literalToSQL(value)
12064
+ });
12065
+
12053
12066
  // ../sql/src/operators.js
12054
12067
  function visit(callback) {
12055
12068
  callback(this.op, this);
@@ -12248,6 +12261,9 @@ var last_value = winf("LAST_VALUE");
12248
12261
  var nth_value = winf("NTH_VALUE");
12249
12262
 
12250
12263
  // ../sql/src/aggregates.js
12264
+ function agg(strings, ...exprs) {
12265
+ return sql(strings, ...exprs).annotate({ aggregate: true });
12266
+ }
12251
12267
  var AggregateFunction = class _AggregateFunction extends SQLExpression {
12252
12268
  /**
12253
12269
  * Create a new AggregateFunction instance.
@@ -12378,6 +12394,7 @@ var entropy = aggf("ENTROPY");
12378
12394
  var varPop = aggf("VAR_POP");
12379
12395
  var stddevPop = aggf("STDDEV_POP");
12380
12396
  var corr = aggf("CORR");
12397
+ var covariance = aggf("COVAR_SAMP");
12381
12398
  var covarPop = aggf("COVAR_POP");
12382
12399
  var regrIntercept = aggf("REGR_INTERCEPT");
12383
12400
  var regrSlope = aggf("REGR_SLOPE");
@@ -12520,7 +12537,8 @@ var Query = class _Query {
12520
12537
  }
12521
12538
  }
12522
12539
  }
12523
- query.select = query.select.concat(list);
12540
+ const keys = new Set(list.map((x3) => x3.as));
12541
+ query.select = query.select.filter((x3) => !keys.has(x3.as)).concat(list.filter((x3) => x3.expr));
12524
12542
  return this;
12525
12543
  }
12526
12544
  }
@@ -12993,6 +13011,7 @@ function scaleTime() {
12993
13011
  };
12994
13012
  }
12995
13013
  var scales = {
13014
+ identity: scaleLinear,
12996
13015
  linear: scaleLinear,
12997
13016
  log: scaleLog,
12998
13017
  symlog: scaleSymlog,
@@ -13038,6 +13057,247 @@ function fnv_mix(a2) {
13038
13057
  return a2 & 4294967295;
13039
13058
  }
13040
13059
 
13060
+ // src/util/index-columns.js
13061
+ var NO_INDEX = { from: NaN };
13062
+ function indexColumns(client) {
13063
+ if (!client.filterIndexable) return NO_INDEX;
13064
+ const q2 = client.query();
13065
+ const from = getBaseTable(q2);
13066
+ if (typeof from !== "string" || !q2.groupby) return NO_INDEX;
13067
+ const g2 = new Set(q2.groupby().map((c) => c.column));
13068
+ const aggr = [];
13069
+ const dims = [];
13070
+ const aux = {};
13071
+ for (const entry of q2.select()) {
13072
+ const { as, expr: { aggregate, args } } = entry;
13073
+ const op = aggregate?.toUpperCase?.();
13074
+ switch (op) {
13075
+ case "COUNT":
13076
+ case "SUM":
13077
+ aggr.push({ [as]: agg`SUM("${as}")::DOUBLE` });
13078
+ break;
13079
+ case "AVG":
13080
+ aggr.push({ [as]: avgExpr(aux, as, args[0]) });
13081
+ break;
13082
+ case "ARG_MAX":
13083
+ aggr.push({ [as]: argmaxExpr(aux, as, args) });
13084
+ break;
13085
+ case "ARG_MIN":
13086
+ aggr.push({ [as]: argminExpr(aux, as, args) });
13087
+ break;
13088
+ case "VARIANCE":
13089
+ case "VAR_SAMP":
13090
+ aux[as] = null;
13091
+ aggr.push({ [as]: varianceExpr(aux, args[0], from) });
13092
+ break;
13093
+ case "VAR_POP":
13094
+ aux[as] = null;
13095
+ aggr.push({ [as]: varianceExpr(aux, args[0], from, false) });
13096
+ break;
13097
+ case "STDDEV":
13098
+ case "STDDEV_SAMP":
13099
+ aux[as] = null;
13100
+ aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0], from)})` });
13101
+ break;
13102
+ case "STDDEV_POP":
13103
+ aux[as] = null;
13104
+ aggr.push({ [as]: agg`SQRT(${varianceExpr(aux, args[0], from, false)})` });
13105
+ break;
13106
+ case "COVAR_SAMP":
13107
+ aux[as] = null;
13108
+ aggr.push({ [as]: covarianceExpr(aux, args, from) });
13109
+ break;
13110
+ case "COVAR_POP":
13111
+ aux[as] = null;
13112
+ aggr.push({ [as]: covarianceExpr(aux, args, from, false) });
13113
+ break;
13114
+ case "CORR":
13115
+ aux[as] = null;
13116
+ aggr.push({ [as]: corrExpr(aux, args, from) });
13117
+ break;
13118
+ case "REGR_COUNT":
13119
+ aux[as] = null;
13120
+ aggr.push({ [as]: agg`${regrCountExpr(aux, args)}::DOUBLE` });
13121
+ break;
13122
+ case "REGR_AVGX":
13123
+ aux[as] = null;
13124
+ aggr.push({ [as]: regrAvgXExpr(aux, args) });
13125
+ break;
13126
+ case "REGR_AVGY":
13127
+ aux[as] = null;
13128
+ aggr.push({ [as]: regrAvgYExpr(aux, args) });
13129
+ break;
13130
+ case "REGR_SYY":
13131
+ aux[as] = null;
13132
+ aggr.push({ [as]: regrVarExpr(aux, 0, args, from) });
13133
+ break;
13134
+ case "REGR_SXX":
13135
+ aux[as] = null;
13136
+ aggr.push({ [as]: regrVarExpr(aux, 1, args, from) });
13137
+ break;
13138
+ case "REGR_SXY":
13139
+ aux[as] = null;
13140
+ aggr.push({ [as]: covarianceExpr(aux, args, from, null) });
13141
+ break;
13142
+ case "REGR_SLOPE":
13143
+ aux[as] = null;
13144
+ aggr.push({ [as]: regrSlopeExpr(aux, args, from) });
13145
+ break;
13146
+ case "REGR_INTERCEPT":
13147
+ aux[as] = null;
13148
+ aggr.push({ [as]: regrInterceptExpr(aux, args, from) });
13149
+ break;
13150
+ case "REGR_R2":
13151
+ aux[as] = null;
13152
+ aggr.push({ [as]: agg`(${corrExpr(aux, args, from)}) ** 2` });
13153
+ break;
13154
+ case "MAX":
13155
+ case "MIN":
13156
+ case "BIT_AND":
13157
+ case "BIT_OR":
13158
+ case "BIT_XOR":
13159
+ case "BOOL_AND":
13160
+ case "BOOL_OR":
13161
+ case "PRODUCT":
13162
+ aggr.push({ [as]: agg`${op}("${as}")` });
13163
+ break;
13164
+ default:
13165
+ if (g2.has(as)) dims.push(as);
13166
+ else return null;
13167
+ }
13168
+ }
13169
+ return { from, dims, aggr, aux };
13170
+ }
13171
+ function auxName(type, ...args) {
13172
+ const cols = args.length ? "_" + args.map(sanitize).join("_") : "";
13173
+ return `__${type}${cols}__`;
13174
+ }
13175
+ function sanitize(col) {
13176
+ return `${col}`.replaceAll('"', "").replaceAll(" ", "_");
13177
+ }
13178
+ function getBaseTable(query) {
13179
+ const subq = query.subqueries;
13180
+ if (query.select) {
13181
+ const from = query.from();
13182
+ if (!from.length) return void 0;
13183
+ if (subq.length === 0) return from[0].from.table;
13184
+ }
13185
+ const base = getBaseTable(subq[0]);
13186
+ for (let i = 1; i < subq.length; ++i) {
13187
+ const from = getBaseTable(subq[i]);
13188
+ if (from === void 0) continue;
13189
+ if (from !== base) return NaN;
13190
+ }
13191
+ return base;
13192
+ }
13193
+ function countExpr(aux, arg) {
13194
+ const n = auxName("count", arg);
13195
+ aux[n] = agg`COUNT(${arg})`;
13196
+ return agg`SUM(${n})`.annotate({ name: n });
13197
+ }
13198
+ function avgExpr(aux, as, arg) {
13199
+ const n = countExpr(aux, arg);
13200
+ return agg`(SUM("${as}" * ${n.name}) / ${n})`;
13201
+ }
13202
+ function avg2(x3, from) {
13203
+ return sql`(SELECT AVG(${x3}) FROM "${from}")`;
13204
+ }
13205
+ function argmaxExpr(aux, as, [, y3]) {
13206
+ const max2 = auxName("max", y3);
13207
+ aux[max2] = agg`MAX(${y3})`;
13208
+ return agg`ARG_MAX("${as}", ${max2})`;
13209
+ }
13210
+ function argminExpr(aux, as, [, y3]) {
13211
+ const min2 = auxName("min", y3);
13212
+ aux[min2] = agg`MIN(${y3})`;
13213
+ return agg`ARG_MIN("${as}", ${min2})`;
13214
+ }
13215
+ function varianceExpr(aux, x3, from, correction = true) {
13216
+ const n = countExpr(aux, x3);
13217
+ const ssq = auxName("rssq", x3);
13218
+ const sum3 = auxName("rsum", x3);
13219
+ const delta = sql`${x3} - ${avg2(x3, from)}`;
13220
+ aux[ssq] = agg`SUM((${delta}) ** 2)`;
13221
+ aux[sum3] = agg`SUM(${delta})`;
13222
+ const adj = correction ? ` - 1` : "";
13223
+ return agg`(SUM(${ssq}) - (SUM(${sum3}) ** 2 / ${n})) / (${n}${adj})`;
13224
+ }
13225
+ function covarianceExpr(aux, args, from, correction = true) {
13226
+ const n = regrCountExpr(aux, args);
13227
+ const sxy = regrSumXYExpr(aux, args, from);
13228
+ const sx = regrSumExpr(aux, 1, args, from);
13229
+ const sy = regrSumExpr(aux, 0, args, from);
13230
+ const adj = correction === null ? "" : correction ? ` / (${n} - 1)` : ` / ${n}`;
13231
+ return agg`(${sxy} - ${sx} * ${sy} / ${n})${adj}`;
13232
+ }
13233
+ function corrExpr(aux, args, from) {
13234
+ const n = regrCountExpr(aux, args);
13235
+ const sxy = regrSumXYExpr(aux, args, from);
13236
+ const sxx = regrSumSqExpr(aux, 1, args, from);
13237
+ const syy = regrSumSqExpr(aux, 0, args, from);
13238
+ const sx = regrSumExpr(aux, 1, args, from);
13239
+ const sy = regrSumExpr(aux, 0, args, from);
13240
+ const vx = agg`(${sxx} - (${sx} ** 2) / ${n})`;
13241
+ const vy = agg`(${syy} - (${sy} ** 2) / ${n})`;
13242
+ return agg`(${sxy} - ${sx} * ${sy} / ${n}) / SQRT(${vx} * ${vy})`;
13243
+ }
13244
+ function regrCountExpr(aux, [y3, x3]) {
13245
+ const n = auxName("count", y3, x3);
13246
+ aux[n] = agg`REGR_COUNT(${y3}, ${x3})`;
13247
+ return agg`SUM(${n})`.annotate({ name: n });
13248
+ }
13249
+ function regrSumExpr(aux, i, args, from) {
13250
+ const v2 = args[i];
13251
+ const o = args[1 - i];
13252
+ const sum3 = auxName("rs", v2);
13253
+ aux[sum3] = agg`SUM(${v2} - ${avg2(v2, from)}) FILTER (${o} IS NOT NULL)`;
13254
+ return agg`SUM(${sum3})`;
13255
+ }
13256
+ function regrSumSqExpr(aux, i, args, from) {
13257
+ const v2 = args[i];
13258
+ const u = args[1 - i];
13259
+ const ssq = auxName("rss", v2);
13260
+ aux[ssq] = agg`SUM((${v2} - ${avg2(v2, from)}) ** 2) FILTER (${u} IS NOT NULL)`;
13261
+ return agg`SUM(${ssq})`;
13262
+ }
13263
+ function regrSumXYExpr(aux, args, from) {
13264
+ const [y3, x3] = args;
13265
+ const sxy = auxName("sxy", y3, x3);
13266
+ aux[sxy] = agg`SUM((${x3} - ${avg2(x3, from)}) * (${y3} - ${avg2(y3, from)}))`;
13267
+ return agg`SUM(${sxy})`;
13268
+ }
13269
+ function regrAvgXExpr(aux, args) {
13270
+ const [y3, x3] = args;
13271
+ const n = regrCountExpr(aux, args);
13272
+ const a2 = auxName("avg", x3, y3);
13273
+ aux[a2] = agg`REGR_AVGX(${y3}, ${x3})`;
13274
+ return agg`(SUM(${a2} * ${n.name}) / ${n})`;
13275
+ }
13276
+ function regrAvgYExpr(aux, args) {
13277
+ const [y3, x3] = args;
13278
+ const n = regrCountExpr(aux, args);
13279
+ const a2 = auxName("avg", y3, x3);
13280
+ aux[a2] = agg`REGR_AVGY(${y3}, ${x3})`;
13281
+ return agg`(SUM(${a2} * ${n.name}) / ${n})`;
13282
+ }
13283
+ function regrVarExpr(aux, i, args, from) {
13284
+ const n = regrCountExpr(aux, args);
13285
+ const sum3 = regrSumExpr(aux, i, args, from);
13286
+ const ssq = regrSumSqExpr(aux, i, args, from);
13287
+ return agg`(${ssq} - (${sum3} ** 2 / ${n}))`;
13288
+ }
13289
+ function regrSlopeExpr(aux, args, from) {
13290
+ const cov = covarianceExpr(aux, args, from, null);
13291
+ const varx = regrVarExpr(aux, 1, args, from);
13292
+ return agg`(${cov}) / ${varx}`;
13293
+ }
13294
+ function regrInterceptExpr(aux, args, from) {
13295
+ const ax = regrAvgXExpr(aux, args);
13296
+ const ay = regrAvgYExpr(aux, args);
13297
+ const m2 = regrSlopeExpr(aux, args, from);
13298
+ return agg`${ay} - (${m2}) * ${ax}`;
13299
+ }
13300
+
13041
13301
  // src/DataCubeIndexer.js
13042
13302
  var DataCubeIndexer = class {
13043
13303
  /**
@@ -13055,42 +13315,49 @@ var DataCubeIndexer = class {
13055
13315
  this.enabled = false;
13056
13316
  this.clients = null;
13057
13317
  this.indices = null;
13058
- this.activeView = null;
13318
+ this.active = null;
13059
13319
  }
13060
13320
  clear() {
13061
13321
  if (this.indices) {
13062
- this.mc.cancel(Array.from(this.indices.values(), (index) => index.result));
13322
+ this.mc.cancel(Array.from(this.indices.values(), (index) => index?.result));
13063
13323
  this.indices = null;
13064
13324
  }
13065
13325
  }
13066
- index(clients, active) {
13326
+ index(clients, activeClause) {
13067
13327
  if (this.clients !== clients) {
13068
- const cols = Array.from(clients, getIndexColumns);
13328
+ const cols = Array.from(clients, indexColumns).filter((x3) => x3);
13069
13329
  const from = cols[0]?.from;
13070
- this.enabled = cols.every((c) => c && c.from === from);
13330
+ this.enabled = cols.length && cols.every((c) => c.from === from);
13071
13331
  this.clients = clients;
13072
- this.activeView = null;
13332
+ this.active = null;
13073
13333
  this.clear();
13074
13334
  }
13075
13335
  if (!this.enabled) return false;
13076
- active = active || this.selection.active;
13077
- const { source } = active;
13078
- if (source && source === this.activeView?.source) return true;
13336
+ activeClause = activeClause || this.selection.active;
13337
+ const { source } = activeClause;
13338
+ if (source && source === this.active?.source) return true;
13079
13339
  this.clear();
13080
13340
  if (!source) return false;
13081
- const activeView = this.activeView = getActiveView(active);
13082
- if (!activeView) return false;
13083
- this.mc.logger().warn("DATA CUBE INDEX CONSTRUCTION");
13341
+ const active = this.active = activeColumns(activeClause);
13342
+ if (!active) return false;
13343
+ const logger = this.mc.logger();
13344
+ logger.warn("DATA CUBE INDEX CONSTRUCTION");
13084
13345
  const sel = this.selection.remove(source);
13085
13346
  const indices = this.indices = /* @__PURE__ */ new Map();
13086
13347
  const { mc, temp } = this;
13087
13348
  for (const client of clients) {
13088
- if (sel.skip(client, active)) continue;
13089
- const index = getIndexColumns(client);
13090
- const query = client.query(sel.predicate(client)).select({ ...activeView.columns, ...index.aux }).groupby(Object.keys(activeView.columns));
13349
+ if (sel.skip(client, activeClause)) {
13350
+ indices.set(client, null);
13351
+ continue;
13352
+ }
13353
+ const index = indexColumns(client);
13354
+ if (!index) {
13355
+ continue;
13356
+ }
13357
+ const query = client.query(sel.predicate(client)).select({ ...active.columns, ...index.aux }).groupby(Object.keys(active.columns));
13091
13358
  const [subq] = query.subqueries;
13092
13359
  if (subq) {
13093
- const cols = Object.values(activeView.columns).map((c) => c.columns[0]);
13360
+ const cols = Object.values(active.columns).flatMap((c) => c.columns);
13094
13361
  subqueryPushdown(subq, cols);
13095
13362
  }
13096
13363
  const order = query.orderby();
@@ -13099,36 +13366,40 @@ var DataCubeIndexer = class {
13099
13366
  const id = (fnv_hash(sql2) >>> 0).toString(16);
13100
13367
  const table = `cube_index_${id}`;
13101
13368
  const result = mc.exec(create(table, sql2, { temp }));
13369
+ result.catch((e) => logger.error(e));
13102
13370
  indices.set(client, { table, result, order, ...index });
13103
13371
  }
13104
13372
  return true;
13105
13373
  }
13106
13374
  async update() {
13107
- const { clients, selection, activeView } = this;
13108
- const filter = activeView.predicate(selection.active.predicate);
13375
+ const { clients, selection, active } = this;
13376
+ const filter = active.predicate(selection.active.predicate);
13109
13377
  return Promise.all(
13110
13378
  Array.from(clients).map((client) => this.updateClient(client, filter))
13111
13379
  );
13112
13380
  }
13113
13381
  async updateClient(client, filter) {
13382
+ const { mc, indices, selection } = this;
13383
+ if (!indices.has(client)) {
13384
+ filter = selection.predicate(client);
13385
+ return mc.updateClient(client, client.query(filter));
13386
+ }
13387
+ ;
13114
13388
  const index = this.indices.get(client);
13115
13389
  if (!index) return;
13116
- if (!filter) {
13117
- filter = this.activeView.predicate(this.selection.active.predicate);
13118
- }
13119
13390
  const { table, dims, aggr, order = [] } = index;
13120
13391
  const query = Query.select(dims, aggr).from(table).groupby(dims).where(filter).orderby(order);
13121
- return this.mc.updateClient(client, query);
13392
+ return mc.updateClient(client, query);
13122
13393
  }
13123
13394
  };
13124
- function getActiveView(clause) {
13125
- const { source, schema } = clause;
13395
+ function activeColumns(clause) {
13396
+ const { source, meta } = clause;
13126
13397
  let columns = clause.predicate?.columns;
13127
- if (!schema || !columns) return null;
13128
- const { type, scales: scales2, pixelSize = 1 } = schema;
13398
+ if (!meta || !columns) return null;
13399
+ const { type, scales: scales2, bin, pixelSize = 1 } = meta;
13129
13400
  let predicate;
13130
13401
  if (type === "interval" && scales2) {
13131
- const bins = scales2.map((s) => binInterval(s, pixelSize));
13402
+ const bins = scales2.map((s) => binInterval(s, pixelSize, bin));
13132
13403
  if (bins.some((b2) => b2 == null)) return null;
13133
13404
  if (bins.length === 1) {
13134
13405
  predicate = (p2) => p2 ? isBetween("active0", p2.range.map(bins[0])) : [];
@@ -13141,85 +13412,23 @@ function getActiveView(clause) {
13141
13412
  }
13142
13413
  } else if (type === "point") {
13143
13414
  predicate = (x3) => x3;
13144
- columns = Object.fromEntries(columns.map((col) => [col.toString(), col]));
13415
+ columns = Object.fromEntries(columns.map((col) => [`${col}`, asColumn(col)]));
13145
13416
  } else {
13146
13417
  return null;
13147
13418
  }
13148
13419
  return { source, columns, predicate };
13149
13420
  }
13150
- function binInterval(scale, pixelSize) {
13151
- const { apply, sqlApply } = scaleTransform(scale);
13152
- if (apply) {
13153
- const { domain, range } = scale;
13154
- const lo = apply(Math.min(...domain));
13155
- const hi = apply(Math.max(...domain));
13156
- const a2 = Math.abs(range[1] - range[0]) / (hi - lo) / pixelSize;
13157
- const s = pixelSize === 1 ? "" : `${pixelSize}::INTEGER * `;
13158
- return (value) => sql`${s}FLOOR(${a2}::DOUBLE * (${sqlApply(value)} - ${lo}::DOUBLE))::INTEGER`;
13159
- }
13160
- }
13161
- var NO_INDEX = { from: NaN };
13162
- function getIndexColumns(client) {
13163
- if (!client.filterIndexable) return NO_INDEX;
13164
- const q2 = client.query();
13165
- const from = getBaseTable(q2);
13166
- if (!from || !q2.groupby) return NO_INDEX;
13167
- const g2 = new Set(q2.groupby().map((c) => c.column));
13168
- const aggr = [];
13169
- const dims = [];
13170
- const aux = {};
13171
- let auxAs;
13172
- for (const entry of q2.select()) {
13173
- const { as, expr: { aggregate, args } } = entry;
13174
- const op = aggregate?.toUpperCase?.();
13175
- switch (op) {
13176
- case "COUNT":
13177
- case "SUM":
13178
- aggr.push({ [as]: sql`SUM("${as}")::DOUBLE` });
13179
- break;
13180
- case "AVG":
13181
- aux[auxAs = "__count__"] = sql`COUNT(*)`;
13182
- aggr.push({ [as]: sql`(SUM("${as}" * ${auxAs}) / SUM(${auxAs}))::DOUBLE` });
13183
- break;
13184
- case "ARG_MAX":
13185
- aux[auxAs = `__max_${as}__`] = sql`MAX(${args[1]})`;
13186
- aggr.push({ [as]: sql`ARG_MAX("${as}", ${auxAs})` });
13187
- break;
13188
- case "ARG_MIN":
13189
- aux[auxAs = `__min_${as}__`] = sql`MIN(${args[1]})`;
13190
- aggr.push({ [as]: sql`ARG_MIN("${as}", ${auxAs})` });
13191
- break;
13192
- case "MAX":
13193
- case "MIN":
13194
- case "BIT_AND":
13195
- case "BIT_OR":
13196
- case "BIT_XOR":
13197
- case "BOOL_AND":
13198
- case "BOOL_OR":
13199
- case "PRODUCT":
13200
- aggr.push({ [as]: sql`${op}("${as}")` });
13201
- break;
13202
- default:
13203
- if (g2.has(as)) dims.push(as);
13204
- else return null;
13205
- }
13206
- }
13207
- return { aggr, dims, aux, from };
13208
- }
13209
- function getBaseTable(query) {
13210
- const subq = query.subqueries;
13211
- if (query.select) {
13212
- const from = query.from();
13213
- if (!from.length) return void 0;
13214
- if (subq.length === 0) return from[0].from.table;
13215
- }
13216
- const base = getBaseTable(subq[0]);
13217
- for (let i = 1; i < subq.length; ++i) {
13218
- const from = getBaseTable(subq[i]);
13219
- if (from === void 0) continue;
13220
- if (from !== base) return NaN;
13221
- }
13222
- return base;
13421
+ var BIN = { ceil: "CEIL", round: "ROUND" };
13422
+ function binInterval(scale, pixelSize, bin) {
13423
+ const { type, domain, range, apply, sqlApply } = scaleTransform(scale);
13424
+ if (!apply) return;
13425
+ const fn = BIN[`${bin}`.toLowerCase()] || "FLOOR";
13426
+ const lo = apply(Math.min(...domain));
13427
+ const hi = apply(Math.max(...domain));
13428
+ const a2 = type === "identity" ? 1 : Math.abs(range[1] - range[0]) / (hi - lo);
13429
+ const s = a2 / pixelSize === 1 ? "" : `${a2 / pixelSize}::DOUBLE * `;
13430
+ const d = lo === 0 ? "" : ` - ${lo}::DOUBLE`;
13431
+ return (value) => sql`${fn}(${s}(${sqlApply(value)}${d}))::INTEGER`;
13223
13432
  }
13224
13433
  function subqueryPushdown(query, cols) {
13225
13434
  const memo = /* @__PURE__ */ new Set();
@@ -13234,1299 +13443,1400 @@ function subqueryPushdown(query, cols) {
13234
13443
  pushdown(query);
13235
13444
  }
13236
13445
 
13237
- // src/FilterGroup.js
13238
- var FilterGroup = class {
13446
+ // src/util/AsyncDispatch.js
13447
+ var AsyncDispatch = class {
13239
13448
  /**
13240
- * @param {import('./Coordinator.js').Coordinator} coordinator The Mosaic coordinator.
13241
- * @param {*} selection The shared filter selection.
13242
- * @param {*} index Boolean flag or options hash for data cube indexer.
13243
- * Falsy values disable indexing.
13449
+ * Create a new asynchronous dispatcher instance.
13244
13450
  */
13245
- constructor(coordinator2, selection, index = true) {
13246
- this.mc = coordinator2;
13247
- this.selection = selection;
13248
- this.clients = /* @__PURE__ */ new Set();
13249
- this.indexer = index ? new DataCubeIndexer(this.mc, { ...index, selection }) : null;
13250
- const { value, activate } = this.handlers = {
13251
- value: () => this.update(),
13252
- activate: (clause) => this.indexer?.index(this.clients, clause)
13253
- };
13254
- selection.addEventListener("value", value);
13255
- selection.addEventListener("activate", activate);
13451
+ constructor() {
13452
+ this._callbacks = /* @__PURE__ */ new Map();
13256
13453
  }
13257
- finalize() {
13258
- const { value, activate } = this.handlers;
13259
- this.selection.removeEventListener("value", value);
13260
- this.selection.removeEventListener("activate", activate);
13454
+ /**
13455
+ * Add an event listener callback for the provided event type.
13456
+ * @param {string} type The event type.
13457
+ * @param {(value: *) => void | Promise} callback The event handler
13458
+ * callback function to add. If the callback has already been
13459
+ * added for the event type, this method has no effect.
13460
+ */
13461
+ addEventListener(type, callback) {
13462
+ if (!this._callbacks.has(type)) {
13463
+ this._callbacks.set(type, {
13464
+ callbacks: /* @__PURE__ */ new Set(),
13465
+ pending: null,
13466
+ queue: new DispatchQueue()
13467
+ });
13468
+ }
13469
+ const entry = this._callbacks.get(type);
13470
+ entry.callbacks.add(callback);
13261
13471
  }
13262
- reset() {
13263
- this.indexer?.reset();
13472
+ /**
13473
+ * Remove an event listener callback for the provided event type.
13474
+ * @param {string} type The event type.
13475
+ * @param {(value: *) => void | Promise} callback The event handler
13476
+ * callback function to remove.
13477
+ */
13478
+ removeEventListener(type, callback) {
13479
+ const entry = this._callbacks.get(type);
13480
+ if (entry) {
13481
+ entry.callbacks.delete(callback);
13482
+ }
13264
13483
  }
13265
- add(client) {
13266
- (this.clients = new Set(this.clients)).add(client);
13267
- return this;
13484
+ /**
13485
+ * Lifecycle method that returns the event value to emit.
13486
+ * This default implementation simply returns the input value as-is.
13487
+ * Subclasses may override this method to implement custom transformations
13488
+ * prior to emitting an event value to all listeners.
13489
+ * @param {string} type The event type.
13490
+ * @param {*} value The event value.
13491
+ * @returns The (possibly transformed) event value to emit.
13492
+ */
13493
+ willEmit(type, value) {
13494
+ return value;
13268
13495
  }
13269
- remove(client) {
13270
- if (this.clients.has(client)) {
13271
- (this.clients = new Set(this.clients)).delete(client);
13272
- }
13273
- return this;
13496
+ /**
13497
+ * Lifecycle method that returns a filter function for updating the
13498
+ * queue of unemitted event values prior to enqueueing a new value.
13499
+ * This default implementation simply returns null, indicating that
13500
+ * any other unemitted event values should be dropped (that is, all
13501
+ * queued events are filtered)
13502
+ * @param {string} type The event type.
13503
+ * @param {*} value The new event value that will be enqueued.
13504
+ * @returns {(value: *) => boolean|null} A dispatch queue filter
13505
+ * function, or null if all unemitted event values should be filtered.
13506
+ */
13507
+ emitQueueFilter(type, value) {
13508
+ return null;
13274
13509
  }
13275
13510
  /**
13276
- * Internal method to process a selection update.
13277
- * The return value is passed as a selection callback value.
13278
- * @returns {Promise} A Promise that resolves when the update completes.
13511
+ * Cancel all unemitted event values for the given event type.
13512
+ * @param {string} type The event type.
13279
13513
  */
13280
- update() {
13281
- const { mc, indexer, clients, selection } = this;
13282
- const hasIndex = indexer?.index(clients);
13283
- return hasIndex ? indexer.update() : defaultUpdate(mc, clients, selection);
13514
+ cancel(type) {
13515
+ const entry = this._callbacks.get(type);
13516
+ entry?.queue.clear();
13284
13517
  }
13285
- };
13286
- function defaultUpdate(mc, clients, selection) {
13287
- return Promise.all(Array.from(clients).map((client) => {
13288
- const filter = selection.predicate(client);
13289
- if (filter != null) {
13290
- return mc.updateClient(client, client.query(filter));
13291
- }
13292
- }));
13293
- }
13294
-
13295
- // src/util/query-result.js
13296
- function queryResult() {
13297
- let resolve;
13298
- let reject;
13299
- const p2 = new Promise((r, e) => {
13300
- resolve = r;
13301
- reject = e;
13302
- });
13303
- return Object.assign(p2, {
13304
- fulfill: (value) => (resolve(value), p2),
13305
- reject: (err) => (reject(err), p2)
13306
- });
13307
- }
13308
-
13309
- // src/QueryConsolidator.js
13310
- function wait(callback) {
13311
- const method = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : typeof setImmediate !== "undefined" ? setImmediate : setTimeout;
13312
- return method(callback);
13313
- }
13314
- function consolidator(enqueue, cache, record) {
13315
- let pending = [];
13316
- let id = 0;
13317
- function run() {
13318
- const groups = entryGroups(pending, cache);
13319
- pending = [];
13320
- id = 0;
13321
- for (const group of groups) {
13322
- consolidate(group, enqueue, record);
13323
- processResults(group, cache);
13324
- }
13325
- }
13326
- return {
13327
- add(entry, priority) {
13328
- if (entry.request.type === "arrow") {
13329
- id = id || wait(() => run());
13330
- pending.push({ entry, priority, index: pending.length });
13331
- } else {
13332
- enqueue(entry, priority);
13518
+ /**
13519
+ * Emit an event value to listeners for the given event type.
13520
+ * If a previous emit has not yet resolved, the event value
13521
+ * will be queued to be emitted later.
13522
+ * The actual event value given to listeners will be the result
13523
+ * of passing the input value through the emitValue() method.
13524
+ * @param {string} type The event type.
13525
+ * @param {*} value The event value.
13526
+ */
13527
+ emit(type, value) {
13528
+ const entry = this._callbacks.get(type) || {};
13529
+ if (entry.pending) {
13530
+ entry.queue.enqueue(value, this.emitQueueFilter(type, value));
13531
+ } else {
13532
+ const event = this.willEmit(type, value);
13533
+ const { callbacks, queue } = entry;
13534
+ if (callbacks?.size) {
13535
+ const callbackValues = Array.from(callbacks, (cb) => cb(event));
13536
+ entry.pending = Promise.allSettled(callbackValues).then(() => {
13537
+ entry.pending = null;
13538
+ if (!queue.isEmpty()) {
13539
+ this.emit(type, queue.dequeue());
13540
+ }
13541
+ });
13333
13542
  }
13334
13543
  }
13335
- };
13336
- }
13337
- function entryGroups(entries, cache) {
13338
- const groups = [];
13339
- const groupMap = /* @__PURE__ */ new Map();
13340
- for (const query of entries) {
13341
- const { entry: { request } } = query;
13342
- const key = consolidationKey(request.query, cache);
13343
- if (!groupMap.has(key)) {
13344
- const list = [];
13345
- groups.push(list);
13346
- groupMap.set(key, list);
13347
- }
13348
- groupMap.get(key).push(query);
13349
13544
  }
13350
- return groups;
13351
- }
13352
- function consolidationKey(query, cache) {
13353
- const sql2 = `${query}`;
13354
- if (query instanceof Query && !cache.get(sql2)) {
13355
- if (
13356
- // @ts-ignore
13357
- query.orderby().length || query.where().length || // @ts-ignore
13358
- query.qualify().length || query.having().length
13359
- ) {
13360
- return sql2;
13361
- }
13362
- const q2 = query.clone().$select("*");
13363
- const groupby = query.groupby();
13364
- if (groupby.length) {
13365
- const map = {};
13366
- query.select().forEach(({ as, expr }) => map[as] = expr);
13367
- q2.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
13368
- }
13369
- return `${q2}`;
13370
- } else {
13371
- return sql2;
13545
+ };
13546
+ var DispatchQueue = class {
13547
+ /**
13548
+ * Create a new dispatch queue instance.
13549
+ */
13550
+ constructor() {
13551
+ this.clear();
13372
13552
  }
13373
- }
13374
- function consolidate(group, enqueue, record) {
13375
- if (shouldConsolidate(group)) {
13376
- enqueue({
13377
- request: {
13378
- type: "arrow",
13379
- cache: false,
13380
- record: false,
13381
- query: group.query = consolidatedQuery(group, record)
13382
- },
13383
- result: group.result = queryResult()
13384
- });
13385
- } else {
13386
- for (const { entry, priority } of group) {
13387
- enqueue(entry, priority);
13388
- }
13553
+ /**
13554
+ * Clear the queue state of all event values.
13555
+ */
13556
+ clear() {
13557
+ this.next = null;
13389
13558
  }
13390
- }
13391
- function shouldConsolidate(group) {
13392
- if (group.length > 1) {
13393
- const sql2 = `${group[0].entry.request.query}`;
13394
- for (let i = 1; i < group.length; ++i) {
13395
- if (sql2 !== `${group[i].entry.request.query}`) {
13396
- return true;
13397
- }
13398
- }
13559
+ /**
13560
+ * Indicate if the queue is empty.
13561
+ * @returns {boolean} True if queue is empty, false otherwise.
13562
+ */
13563
+ isEmpty() {
13564
+ return !this.next;
13399
13565
  }
13400
- return false;
13401
- }
13402
- function consolidatedQuery(group, record) {
13403
- const maps = group.maps = [];
13404
- const fields = /* @__PURE__ */ new Map();
13405
- for (const item of group) {
13406
- const { query: query2 } = item.entry.request;
13407
- const fieldMap = [];
13408
- maps.push(fieldMap);
13409
- for (const { as, expr } of query2.select()) {
13410
- const e = `${expr}`;
13411
- if (!fields.has(e)) {
13412
- fields.set(e, [`col${fields.size}`, expr]);
13566
+ /**
13567
+ * Add a new value to the queue, and optionally filter the
13568
+ * current queue content in response.
13569
+ * @param {*} value The value to add.
13570
+ * @param {(value: *) => boolean} [filter] An optional filter
13571
+ * function to apply to existing queue content. If unspecified
13572
+ * or falsy, all previously queued values are removed. Otherwise,
13573
+ * the provided function is applied to all queue entries. The
13574
+ * entry is retained if the filter function returns a truthy value,
13575
+ * otherwise the entry is removed.
13576
+ */
13577
+ enqueue(value, filter) {
13578
+ const tail = { value };
13579
+ if (filter && this.next) {
13580
+ let curr = this;
13581
+ while (curr.next) {
13582
+ if (filter(curr.next.value)) {
13583
+ curr = curr.next;
13584
+ } else {
13585
+ curr.next = curr.next.next;
13586
+ }
13413
13587
  }
13414
- const [name] = fields.get(e);
13415
- fieldMap.push([name, as]);
13588
+ curr.next = tail;
13589
+ } else {
13590
+ this.next = tail;
13416
13591
  }
13417
- record(`${query2}`);
13418
13592
  }
13419
- const query = group[0].entry.request.query.clone();
13420
- const groupby = query.groupby();
13421
- if (groupby.length) {
13422
- const map = {};
13423
- group.maps[0].forEach(([name, as]) => map[as] = name);
13424
- query.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
13593
+ /**
13594
+ * Remove and return the next queued event value.
13595
+ * @returns {*} The next event value in the queue.
13596
+ */
13597
+ dequeue() {
13598
+ const { next } = this;
13599
+ this.next = next?.next;
13600
+ return next?.value;
13425
13601
  }
13426
- return query.$select(Array.from(fields.values()));
13602
+ };
13603
+
13604
+ // src/util/distinct.js
13605
+ function distinct(a2, b2) {
13606
+ return a2 === b2 ? false : a2 instanceof Date && b2 instanceof Date ? +a2 !== +b2 : Array.isArray(a2) && Array.isArray(b2) ? distinctArray(a2, b2) : true;
13427
13607
  }
13428
- async function processResults(group, cache) {
13429
- const { maps, query, result } = group;
13430
- if (!maps) return;
13431
- let data;
13432
- try {
13433
- data = await result;
13434
- } catch (err) {
13435
- for (const { entry } of group) {
13436
- entry.result.reject(err);
13437
- }
13438
- return;
13608
+ function distinctArray(a2, b2) {
13609
+ if (a2.length !== b2.length) return true;
13610
+ for (let i = 0; i < a2.length; ++i) {
13611
+ if (a2[i] !== b2[i]) return true;
13439
13612
  }
13440
- const describe = isDescribeQuery(query);
13441
- group.forEach(({ entry }, index) => {
13442
- const { request, result: result2 } = entry;
13443
- const map = maps[index];
13444
- const extract = describe && map ? filterResult(data, map) : map ? projectResult(data, map) : data;
13445
- if (request.cache) {
13446
- cache.set(String(request.query), extract);
13447
- }
13448
- result2.fulfill(extract);
13449
- });
13613
+ return false;
13450
13614
  }
13451
- function projectResult(data, map) {
13452
- const cols = {};
13453
- for (const [name, as] of map) {
13454
- cols[as] = data.getChild(name);
13455
- }
13456
- return new data.constructor(cols);
13615
+
13616
+ // src/Param.js
13617
+ function isParam(x3) {
13618
+ return x3 instanceof Param;
13457
13619
  }
13458
- function filterResult(data, map) {
13459
- const lookup = new Map(map);
13460
- const result = [];
13461
- for (const d of data) {
13462
- if (lookup.has(d.column_name)) {
13463
- result.push({ ...d, column_name: lookup.get(d.column_name) });
13464
- }
13620
+ var Param = class _Param extends AsyncDispatch {
13621
+ /**
13622
+ * Create a new Param instance.
13623
+ * @param {*} value The initial value of the Param.
13624
+ */
13625
+ constructor(value) {
13626
+ super();
13627
+ this._value = value;
13465
13628
  }
13466
- return result;
13467
- }
13468
-
13469
- // src/util/cache.js
13470
- var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
13471
- var voidCache = () => ({
13472
- get: () => void 0,
13473
- set: (key, value) => value,
13474
- clear: () => {
13629
+ /**
13630
+ * Create a new Param instance with the given initial value.
13631
+ * @param {*} value The initial value of the Param.
13632
+ * @returns {Param} The new Param instance.
13633
+ */
13634
+ static value(value) {
13635
+ return new _Param(value);
13475
13636
  }
13476
- });
13477
- function lruCache({
13478
- max: max2 = 1e3,
13479
- // max entries
13480
- ttl = 3 * 60 * 60 * 1e3
13481
- // time-to-live, default 3 hours
13482
- } = {}) {
13483
- let cache = /* @__PURE__ */ new Map();
13484
- function evict() {
13485
- const expire = performance.now() - ttl;
13486
- let lruKey = null;
13487
- let lruLast = Infinity;
13488
- for (const [key, value] of cache) {
13489
- const { last: last2 } = value;
13490
- if (last2 < lruLast) {
13491
- lruKey = key;
13492
- lruLast = last2;
13493
- }
13494
- if (expire > last2) {
13495
- cache.delete(key);
13496
- }
13637
+ /**
13638
+ * Create a new Param instance over an array of initial values,
13639
+ * which may contain nested Params.
13640
+ * @param {*} values The initial values of the Param.
13641
+ * @returns {Param} The new Param instance.
13642
+ */
13643
+ static array(values) {
13644
+ if (values.some((v2) => isParam(v2))) {
13645
+ const p2 = new _Param();
13646
+ const update2 = () => {
13647
+ p2.update(values.map((v2) => isParam(v2) ? v2.value : v2));
13648
+ };
13649
+ update2();
13650
+ values.forEach((v2) => isParam(v2) ? v2.addEventListener("value", update2) : 0);
13651
+ return p2;
13497
13652
  }
13498
- if (lruKey) {
13499
- cache.delete(lruKey);
13653
+ return new _Param(values);
13654
+ }
13655
+ /**
13656
+ * The current value of the Param.
13657
+ */
13658
+ get value() {
13659
+ return this._value;
13660
+ }
13661
+ /**
13662
+ * Update the Param value
13663
+ * @param {*} value The new value of the Param.
13664
+ * @param {object} [options] The update options.
13665
+ * @param {boolean} [options.force] A boolean flag indicating if the Param
13666
+ * should emit a 'value' event even if the internal value is unchanged.
13667
+ * @returns {this} This Param instance.
13668
+ */
13669
+ update(value, { force } = {}) {
13670
+ const shouldEmit = distinct(this._value, value) || force;
13671
+ if (shouldEmit) {
13672
+ this.emit("value", value);
13673
+ } else {
13674
+ this.cancel("value");
13500
13675
  }
13676
+ return this;
13501
13677
  }
13502
- return {
13503
- get(key) {
13504
- const entry = cache.get(key);
13505
- if (entry) {
13506
- entry.last = performance.now();
13507
- return entry.value;
13508
- }
13509
- },
13510
- set(key, value) {
13511
- cache.set(key, { last: performance.now(), value });
13512
- if (cache.size > max2) requestIdle(evict);
13513
- return value;
13514
- },
13515
- clear() {
13516
- cache = /* @__PURE__ */ new Map();
13678
+ /**
13679
+ * Upon value-typed updates, sets the current value to the input value
13680
+ * immediately prior to the event value being emitted to listeners.
13681
+ * @param {string} type The event type.
13682
+ * @param {*} value The input event value.
13683
+ * @returns {*} The input event value.
13684
+ */
13685
+ willEmit(type, value) {
13686
+ if (type === "value") {
13687
+ this._value = value;
13517
13688
  }
13518
- };
13519
- }
13689
+ return value;
13690
+ }
13691
+ };
13520
13692
 
13521
- // src/util/priority-queue.js
13522
- function priorityQueue(ranks) {
13523
- const queue = Array.from(
13524
- { length: ranks },
13525
- () => ({ head: null, tail: null })
13526
- );
13527
- return {
13528
- /**
13529
- * Indicate if the queue is empty.
13530
- * @returns [boolean] true if empty, false otherwise.
13531
- */
13532
- isEmpty() {
13533
- return queue.every((list) => !list.head);
13534
- },
13535
- /**
13536
- * Insert an item into the queue with a given priority rank.
13537
- * @param {*} item The item to add.
13538
- * @param {number} rank The integer priority rank.
13539
- * Priority ranks are integers starting at zero.
13540
- * Lower ranks indicate higher priority.
13541
- */
13542
- insert(item, rank2) {
13543
- const list = queue[rank2];
13544
- if (!list) {
13545
- throw new Error(`Invalid queue priority rank: ${rank2}`);
13546
- }
13547
- const node = { item, next: null };
13548
- if (list.head === null) {
13549
- list.head = list.tail = node;
13550
- } else {
13551
- list.tail = list.tail.next = node;
13552
- }
13553
- },
13554
- /**
13555
- * Remove a set of items from the queue, regardless of priority rank.
13556
- * If a provided item is not in the queue it will be ignored.
13557
- * @param {(item: *) => boolean} test A predicate function to test
13558
- * if an item should be removed (true to drop, false to keep).
13559
- */
13560
- remove(test) {
13561
- for (const list of queue) {
13562
- let { head, tail } = list;
13563
- for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
13564
- if (test(curr.item)) {
13565
- if (curr === head) {
13566
- head = curr.next;
13567
- } else {
13568
- prev.next = curr.next;
13569
- }
13570
- if (curr === tail) tail = prev || head;
13571
- }
13572
- }
13573
- list.head = head;
13574
- list.tail = tail;
13575
- }
13576
- },
13577
- /**
13578
- * Remove and return the next highest priority item.
13579
- * @returns {*} The next item in the queue,
13580
- * or undefined if this queue is empty.
13581
- */
13582
- next() {
13583
- for (const list of queue) {
13584
- const { head } = list;
13585
- if (head !== null) {
13586
- list.head = head.next;
13587
- if (list.tail === head) {
13588
- list.tail = null;
13589
- }
13590
- return head.item;
13591
- }
13592
- }
13693
+ // src/Selection.js
13694
+ function isSelection(x3) {
13695
+ return x3 instanceof Selection;
13696
+ }
13697
+ var Selection = class _Selection extends Param {
13698
+ /**
13699
+ * Create a new Selection instance with an
13700
+ * intersect (conjunction) resolution strategy.
13701
+ * @param {object} [options] The selection options.
13702
+ * @param {boolean} [options.cross=false] Boolean flag indicating
13703
+ * cross-filtered resolution. If true, selection clauses will not
13704
+ * be applied to the clients they are associated with.
13705
+ * @returns {Selection} The new Selection instance.
13706
+ */
13707
+ static intersect({ cross = false } = {}) {
13708
+ return new _Selection(new SelectionResolver({ cross }));
13709
+ }
13710
+ /**
13711
+ * Create a new Selection instance with a
13712
+ * union (disjunction) resolution strategy.
13713
+ * @param {object} [options] The selection options.
13714
+ * @param {boolean} [options.cross=false] Boolean flag indicating
13715
+ * cross-filtered resolution. If true, selection clauses will not
13716
+ * be applied to the clients they are associated with.
13717
+ * @returns {Selection} The new Selection instance.
13718
+ */
13719
+ static union({ cross = false } = {}) {
13720
+ return new _Selection(new SelectionResolver({ cross, union: true }));
13721
+ }
13722
+ /**
13723
+ * Create a new Selection instance with a singular resolution strategy
13724
+ * that keeps only the most recent selection clause.
13725
+ * @param {object} [options] The selection options.
13726
+ * @param {boolean} [options.cross=false] Boolean flag indicating
13727
+ * cross-filtered resolution. If true, selection clauses will not
13728
+ * be applied to the clients they are associated with.
13729
+ * @returns {Selection} The new Selection instance.
13730
+ */
13731
+ static single({ cross = false } = {}) {
13732
+ return new _Selection(new SelectionResolver({ cross, single: true }));
13733
+ }
13734
+ /**
13735
+ * Create a new Selection instance with a
13736
+ * cross-filtered intersect resolution strategy.
13737
+ * @returns {Selection} The new Selection instance.
13738
+ */
13739
+ static crossfilter() {
13740
+ return new _Selection(new SelectionResolver({ cross: true }));
13741
+ }
13742
+ /**
13743
+ * Create a new Selection instance.
13744
+ * @param {SelectionResolver} resolver The selection resolution
13745
+ * strategy to apply.
13746
+ */
13747
+ constructor(resolver = new SelectionResolver()) {
13748
+ super([]);
13749
+ this._resolved = this._value;
13750
+ this._resolver = resolver;
13751
+ }
13752
+ /**
13753
+ * Create a cloned copy of this Selection instance.
13754
+ * @returns {Selection} A clone of this selection.
13755
+ */
13756
+ clone() {
13757
+ const s = new _Selection(this._resolver);
13758
+ s._value = s._resolved = this._value;
13759
+ return s;
13760
+ }
13761
+ /**
13762
+ * Create a clone of this Selection with clauses corresponding
13763
+ * to the provided source removed.
13764
+ * @param {*} source The clause source to remove.
13765
+ * @returns {Selection} A cloned and updated Selection.
13766
+ */
13767
+ remove(source) {
13768
+ const s = this.clone();
13769
+ s._value = s._resolved = s._resolver.resolve(this._resolved, { source });
13770
+ s._value.active = { source };
13771
+ return s;
13772
+ }
13773
+ /**
13774
+ * The selection clause resolver.
13775
+ */
13776
+ get resolver() {
13777
+ return this._resolver;
13778
+ }
13779
+ /**
13780
+ * Indicate if this selection has a single resolution strategy.
13781
+ */
13782
+ get single() {
13783
+ return this._resolver.single;
13784
+ }
13785
+ /**
13786
+ * The current array of selection clauses.
13787
+ */
13788
+ get clauses() {
13789
+ return super.value;
13790
+ }
13791
+ /**
13792
+ * The current active (most recently updated) selection clause.
13793
+ */
13794
+ get active() {
13795
+ return this.clauses.active;
13796
+ }
13797
+ /**
13798
+ * The value corresponding to the current active selection clause.
13799
+ * This method ensures compatibility where a normal Param is expected.
13800
+ */
13801
+ get value() {
13802
+ return this.active?.value;
13803
+ }
13804
+ /**
13805
+ * The value corresponding to a given source. Returns undefined if
13806
+ * this selection does not include a clause from this source.
13807
+ * @param {*} source The clause source to look up the value for.
13808
+ */
13809
+ valueFor(source) {
13810
+ return this.clauses.find((c) => c.source === source)?.value;
13811
+ }
13812
+ /**
13813
+ * Emit an activate event with the given selection clause.
13814
+ * @param {*} clause The clause repesenting the potential activation.
13815
+ */
13816
+ activate(clause) {
13817
+ this.emit("activate", clause);
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
+ */
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;
13593
13841
  }
13594
- };
13595
- }
13596
-
13597
- // src/QueryManager.js
13598
- var Priority = { High: 0, Normal: 1, Low: 2 };
13599
- function QueryManager() {
13600
- const queue = priorityQueue(3);
13601
- let db;
13602
- let clientCache;
13603
- let logger;
13604
- let recorders = [];
13605
- let pending = null;
13606
- let consolidate2;
13607
- function next() {
13608
- if (pending || queue.isEmpty()) return;
13609
- const { request, result } = queue.next();
13610
- pending = submit(request, result);
13611
- pending.finally(() => {
13612
- pending = null;
13613
- next();
13614
- });
13842
+ return value;
13615
13843
  }
13616
- function enqueue(entry, priority = Priority.Normal) {
13617
- queue.insert(entry, priority);
13618
- next();
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 new event value that will be enqueued.
13849
+ * @returns {(value: *) => boolean|null} For value-typed events,
13850
+ * returns a dispatch queue filter function. Otherwise returns null.
13851
+ */
13852
+ emitQueueFilter(type, value) {
13853
+ return type === "value" ? this._resolver.queueFilter(value) : null;
13619
13854
  }
13620
- function recordQuery(sql2) {
13621
- if (recorders.length && sql2) {
13622
- recorders.forEach((rec) => rec.add(sql2));
13623
- }
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);
13624
13864
  }
13625
- async function submit(request, result) {
13626
- try {
13627
- const { query, type, cache = false, record = true, options } = request;
13628
- const sql2 = query ? `${query}` : null;
13629
- if (record) {
13630
- recordQuery(sql2);
13631
- }
13632
- if (cache) {
13633
- const cached = clientCache.get(sql2);
13634
- if (cached) {
13635
- logger.debug("Cache");
13636
- result.fulfill(cached);
13637
- return;
13638
- }
13639
- }
13640
- const t0 = performance.now();
13641
- const data = await db.query({ type, sql: sql2, ...options });
13642
- if (cache) clientCache.set(sql2, data);
13643
- logger.debug(`Request: ${(performance.now() - t0).toFixed(1)}`);
13644
- result.fulfill(data);
13645
- } catch (err) {
13646
- result.reject(err);
13647
- }
13865
+ /**
13866
+ * Return a selection query predicate for the given client.
13867
+ * @param {*} client The client whose data may be filtered.
13868
+ * @param {boolean} [noSkip=false] Disable skipping of active
13869
+ * cross-filtered sources. If set true, the source of the active
13870
+ * clause in a cross-filtered selection will not be skipped.
13871
+ * @returns {*} The query predicate for filtering client data,
13872
+ * based on the current state of this selection.
13873
+ */
13874
+ predicate(client, noSkip = false) {
13875
+ const { clauses } = this;
13876
+ const active = noSkip ? null : clauses.active;
13877
+ return this._resolver.predicate(clauses, active, client);
13648
13878
  }
13649
- return {
13650
- cache(value) {
13651
- return value !== void 0 ? clientCache = value === true ? lruCache() : value || voidCache() : clientCache;
13652
- },
13653
- logger(value) {
13654
- return value ? logger = value : logger;
13655
- },
13656
- connector(connector) {
13657
- return connector ? db = connector : db;
13658
- },
13659
- consolidate(flag) {
13660
- if (flag && !consolidate2) {
13661
- consolidate2 = consolidator(enqueue, clientCache, recordQuery);
13662
- } else if (!flag && consolidate2) {
13663
- consolidate2 = null;
13664
- }
13665
- },
13666
- request(request, priority = Priority.Normal) {
13667
- const result = queryResult();
13668
- const entry = { request, result };
13669
- if (consolidate2) {
13670
- consolidate2.add(entry, priority);
13671
- } else {
13672
- enqueue(entry, priority);
13673
- }
13674
- return result;
13675
- },
13676
- cancel(requests) {
13677
- const set = new Set(requests);
13678
- queue.remove(({ result }) => set.has(result));
13679
- },
13680
- clear() {
13681
- queue.remove(({ result }) => {
13682
- result.reject("Cleared");
13683
- return true;
13684
- });
13685
- },
13686
- record() {
13687
- let state = [];
13688
- const recorder = {
13689
- add(query) {
13690
- state.push(query);
13691
- },
13692
- reset() {
13693
- state = [];
13694
- },
13695
- snapshot() {
13696
- return state.slice();
13697
- },
13698
- stop() {
13699
- recorders = recorders.filter((x3) => x3 !== recorder);
13700
- return state;
13701
- }
13702
- };
13703
- recorders.push(recorder);
13704
- return recorder;
13705
- }
13706
- };
13707
- }
13708
-
13709
- // src/util/js-type.js
13710
- function jsType(type) {
13711
- switch (type) {
13712
- case "BIGINT":
13713
- case "HUGEINT":
13714
- case "INTEGER":
13715
- case "SMALLINT":
13716
- case "TINYINT":
13717
- case "UBIGINT":
13718
- case "UINTEGER":
13719
- case "USMALLINT":
13720
- case "UTINYINT":
13721
- case "DOUBLE":
13722
- case "FLOAT":
13723
- case "REAL":
13724
- return "number";
13725
- case "DATE":
13726
- case "TIMESTAMP":
13727
- case "TIMESTAMPTZ":
13728
- case "TIMESTAMP WITH TIME ZONE":
13729
- case "TIME":
13730
- case "TIMESTAMP_NS":
13731
- return "date";
13732
- case "BOOLEAN":
13733
- return "boolean";
13734
- case "VARCHAR":
13735
- case "UUID":
13736
- case "JSON":
13737
- return "string";
13738
- case "ARRAY":
13739
- case "LIST":
13740
- return "array";
13741
- case "BLOB":
13742
- case "STRUCT":
13743
- case "MAP":
13744
- case "GEOMETRY":
13745
- return "object";
13746
- default:
13747
- if (type.startsWith("DECIMAL")) {
13748
- return "number";
13749
- } else if (type.startsWith("STRUCT") || type.startsWith("MAP")) {
13750
- return "object";
13751
- } else if (type.endsWith("]")) {
13752
- return "array";
13753
- }
13754
- throw new Error(`Unsupported type: ${type}`);
13879
+ };
13880
+ var SelectionResolver = class {
13881
+ /**
13882
+ * Create a new selection resolved instance.
13883
+ * @param {object} [options] The resolution strategy options.
13884
+ * @param {boolean} [options.union=false] Boolean flag to indicate a union strategy.
13885
+ * If false, an intersection strategy is used.
13886
+ * @param {boolean} [options.cross=false] Boolean flag to indicate cross-filtering.
13887
+ * @param {boolean} [options.single=false] Boolean flag to indicate single clauses only.
13888
+ */
13889
+ constructor({ union, cross, single } = {}) {
13890
+ this.union = !!union;
13891
+ this.cross = !!cross;
13892
+ this.single = !!single;
13755
13893
  }
13756
- }
13757
-
13758
- // src/util/convert-arrow.js
13759
- function isArrowTable(values) {
13760
- return typeof values?.getChild === "function";
13761
- }
13762
- function convertArrowArrayType(type) {
13763
- return DataType.isInt(type) || DataType.isFloat(type) || DataType.isDecimal(type) ? Float64Array : Array;
13764
- }
13765
- function convertArrowValue(type) {
13766
- if (DataType.isTimestamp(type)) {
13767
- return (v2) => v2 == null ? v2 : new Date(v2);
13894
+ /**
13895
+ * Resolve a list of selection clauses according to the resolution strategy.
13896
+ * @param {*[]} clauseList An array of selection clauses.
13897
+ * @param {*} clause A new selection clause to add.
13898
+ * @returns {*[]} An updated array of selection clauses.
13899
+ */
13900
+ resolve(clauseList, clause, reset = false) {
13901
+ const { source, predicate } = clause;
13902
+ const filtered = clauseList.filter((c) => source !== c.source);
13903
+ const clauses = this.single ? [] : filtered;
13904
+ if (this.single && reset) filtered.forEach((c) => c.source?.reset?.());
13905
+ if (predicate) clauses.push(clause);
13906
+ return clauses;
13768
13907
  }
13769
- if (DataType.isInt(type) && type.bitWidth >= 64) {
13770
- return (v2) => v2 == null ? v2 : Number(v2);
13908
+ /**
13909
+ * Indicates if a selection clause should not be applied to a given client.
13910
+ * The return value depends on the resolution strategy.
13911
+ * @param {*} client The selection clause.
13912
+ * @param {*} clause The client to test.
13913
+ * @returns True if the client should be skipped, false otherwise.
13914
+ */
13915
+ skip(client, clause) {
13916
+ return this.cross && clause?.clients?.has(client);
13771
13917
  }
13772
- if (DataType.isDecimal(type)) {
13773
- const scale = 1 / Math.pow(10, type.scale);
13774
- return (v2) => v2 == null ? v2 : decimalToNumber(v2, scale);
13918
+ /**
13919
+ * Return a selection query predicate for the given client.
13920
+ * @param {*[]} clauseList An array of selection clauses.
13921
+ * @param {*} active The current active selection clause.
13922
+ * @param {*} client The client whose data may be filtered.
13923
+ * @returns {*} The query predicate for filtering client data,
13924
+ * based on the current state of this selection.
13925
+ */
13926
+ predicate(clauseList, active, client) {
13927
+ const { union } = this;
13928
+ if (this.skip(client, active)) return void 0;
13929
+ const predicates = clauseList.filter((clause) => !this.skip(client, clause)).map((clause) => clause.predicate);
13930
+ return union && predicates.length > 1 ? or(predicates) : predicates;
13775
13931
  }
13776
- return (v2) => v2;
13777
- }
13778
- function convertArrowColumn(column2) {
13779
- const { type } = column2;
13780
- if (DataType.isTimestamp(type)) {
13781
- const size = column2.length;
13782
- const array = new Array(size);
13783
- for (let row = 0; row < size; ++row) {
13784
- const v2 = column2.get(row);
13785
- array[row] = v2 == null ? null : new Date(v2);
13932
+ /**
13933
+ * Returns a filter function for queued selection updates.
13934
+ * @param {*} value The new event value that will be enqueued.
13935
+ * @returns {(value: *) => boolean|null} A dispatch queue filter
13936
+ * function, or null if all unemitted event values should be filtered.
13937
+ */
13938
+ queueFilter(value) {
13939
+ if (this.cross) {
13940
+ const source = value.active?.source;
13941
+ return (clauses) => clauses.active?.source !== source;
13786
13942
  }
13787
- return array;
13943
+ return null;
13788
13944
  }
13789
- if (DataType.isInt(type) && type.bitWidth >= 64) {
13790
- const size = column2.length;
13791
- const array = new Float64Array(size);
13792
- for (let row = 0; row < size; ++row) {
13793
- const v2 = column2.get(row);
13794
- array[row] = v2 == null ? NaN : Number(v2);
13795
- }
13796
- return array;
13945
+ };
13946
+
13947
+ // src/FilterGroup.js
13948
+ var FilterGroup = class {
13949
+ /**
13950
+ * @param {Coordinator} coordinator The Mosaic coordinator.
13951
+ * @param {Selection} selection The shared filter selection.
13952
+ * @param {object|boolean} index Boolean flag or options hash for
13953
+ * a data cube indexer. Falsy values disable indexing.
13954
+ */
13955
+ constructor(coordinator2, selection, index = true) {
13956
+ this.mc = coordinator2;
13957
+ this.selection = selection;
13958
+ this.clients = /* @__PURE__ */ new Set();
13959
+ this.indexer = null;
13960
+ this.index(index);
13961
+ const { value, activate } = this.handlers = {
13962
+ value: () => this.update(),
13963
+ activate: (clause) => {
13964
+ this.indexer?.index(this.clients, clause);
13965
+ }
13966
+ };
13967
+ selection.addEventListener("value", value);
13968
+ selection.addEventListener("activate", activate);
13969
+ }
13970
+ finalize() {
13971
+ const { value, activate } = this.handlers;
13972
+ this.selection.removeEventListener("value", value);
13973
+ this.selection.removeEventListener("activate", activate);
13974
+ }
13975
+ index(state) {
13976
+ const { selection } = this;
13977
+ const { resolver } = selection;
13978
+ this.indexer = state && (resolver.single || !resolver.union) ? new DataCubeIndexer(this.mc, { ...state, selection }) : null;
13979
+ }
13980
+ reset() {
13981
+ this.indexer?.reset();
13797
13982
  }
13798
- if (DataType.isDecimal(type)) {
13799
- const scale = 1 / Math.pow(10, type.scale);
13800
- const size = column2.length;
13801
- const array = new Float64Array(size);
13802
- for (let row = 0; row < size; ++row) {
13803
- const v2 = column2.get(row);
13804
- array[row] = v2 == null ? NaN : decimalToNumber(v2, scale);
13805
- }
13806
- return array;
13983
+ add(client) {
13984
+ (this.clients = new Set(this.clients)).add(client);
13985
+ return this;
13807
13986
  }
13808
- return column2.toArray();
13809
- }
13810
- var BASE32 = Array.from(
13811
- { length: 8 },
13812
- (_2, i) => Math.pow(2, i * 32)
13813
- );
13814
- function decimalToNumber(v2, scale) {
13815
- const n = v2.length;
13816
- let x3 = 0;
13817
- if (v2.signed && (v2[n - 1] | 0) < 0) {
13818
- for (let i = 0; i < n; ++i) {
13819
- x3 += ~v2[i] * BASE32[i];
13820
- }
13821
- x3 = -(x3 + 1);
13822
- } else {
13823
- for (let i = 0; i < n; ++i) {
13824
- x3 += v2[i] * BASE32[i];
13987
+ remove(client) {
13988
+ if (this.clients.has(client)) {
13989
+ (this.clients = new Set(this.clients)).delete(client);
13825
13990
  }
13991
+ return this;
13826
13992
  }
13827
- return x3 * scale;
13993
+ /**
13994
+ * Internal method to process a selection update.
13995
+ * The return value is passed as a selection callback value.
13996
+ * @returns {Promise} A Promise that resolves when the update completes.
13997
+ */
13998
+ update() {
13999
+ const { mc, indexer, clients, selection } = this;
14000
+ const hasIndex = indexer?.index(clients);
14001
+ return hasIndex ? indexer.update() : defaultUpdate(mc, clients, selection);
14002
+ }
14003
+ };
14004
+ function defaultUpdate(mc, clients, selection) {
14005
+ return Promise.all(Array.from(clients).map((client) => {
14006
+ const filter = selection.predicate(client);
14007
+ if (filter != null) {
14008
+ return mc.updateClient(client, client.query(filter));
14009
+ }
14010
+ }));
13828
14011
  }
13829
14012
 
13830
- // src/util/field-info.js
13831
- var Count = "count";
13832
- var Nulls = "nulls";
13833
- var Max = "max";
13834
- var Min = "min";
13835
- var Distinct = "distinct";
13836
- var statMap = {
13837
- [Count]: count,
13838
- [Distinct]: (column2) => count(column2).distinct(),
13839
- [Max]: max,
13840
- [Min]: min,
13841
- [Nulls]: (column2) => count().where(isNull(column2))
13842
- };
13843
- function summarize(table, column2, stats) {
13844
- return Query.from(table).select(Array.from(stats, (s) => [s, statMap[s](column2)]));
14013
+ // src/util/query-result.js
14014
+ function queryResult() {
14015
+ let resolve;
14016
+ let reject;
14017
+ const p2 = new Promise((r, e) => {
14018
+ resolve = r;
14019
+ reject = e;
14020
+ });
14021
+ return Object.assign(p2, {
14022
+ fulfill: (value) => (resolve(value), p2),
14023
+ reject: (err) => (reject(err), p2)
14024
+ });
13845
14025
  }
13846
- async function queryFieldInfo(mc, fields) {
13847
- if (fields.length === 1 && `${fields[0].column}` === "*") {
13848
- return getTableInfo(mc, fields[0].table);
13849
- } else {
13850
- return (await Promise.all(fields.map((f2) => getFieldInfo(mc, f2)))).filter((x3) => x3);
13851
- }
14026
+
14027
+ // src/QueryConsolidator.js
14028
+ function wait(callback) {
14029
+ const method = typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : typeof setImmediate !== "undefined" ? setImmediate : setTimeout;
14030
+ return method(callback);
13852
14031
  }
13853
- async function getFieldInfo(mc, { table, column: column2, stats }) {
13854
- const q2 = Query.from({ source: table }).select({ column: column2 }).groupby(column2.aggregate ? sql`ALL` : []);
13855
- const [desc2] = Array.from(await mc.query(Query.describe(q2)));
13856
- const info = {
13857
- table,
13858
- column: `${column2}`,
13859
- sqlType: desc2.column_type,
13860
- type: jsType(desc2.column_type),
13861
- nullable: desc2.null === "YES"
13862
- };
13863
- if (!(stats?.length || stats?.size)) return info;
13864
- const result = await mc.query(
13865
- summarize(table, column2, stats),
13866
- { persist: true }
13867
- );
13868
- for (let i = 0; i < result.numCols; ++i) {
13869
- const { name } = result.schema.fields[i];
13870
- const child = result.getChildAt(i);
13871
- const convert = convertArrowValue(child.type);
13872
- info[name] = convert(child.get(0));
14032
+ function consolidator(enqueue, cache, record) {
14033
+ let pending = [];
14034
+ let id = 0;
14035
+ function run() {
14036
+ const groups = entryGroups(pending, cache);
14037
+ pending = [];
14038
+ id = 0;
14039
+ for (const group of groups) {
14040
+ consolidate(group, enqueue, record);
14041
+ processResults(group, cache);
14042
+ }
13873
14043
  }
13874
- return info;
13875
- }
13876
- async function getTableInfo(mc, table) {
13877
- const result = await mc.query(`DESCRIBE ${asRelation(table)}`);
13878
- return Array.from(result).map((desc2) => ({
13879
- table,
13880
- column: desc2.column_name,
13881
- sqlType: desc2.column_type,
13882
- type: jsType(desc2.column_type),
13883
- nullable: desc2.null === "YES"
13884
- }));
13885
- }
13886
-
13887
- // src/util/void-logger.js
13888
- function voidLogger() {
13889
14044
  return {
13890
- debug() {
13891
- },
13892
- info() {
13893
- },
13894
- log() {
13895
- },
13896
- warn() {
13897
- },
13898
- error() {
14045
+ add(entry, priority) {
14046
+ if (entry.request.type === "arrow") {
14047
+ id = id || wait(() => run());
14048
+ pending.push({ entry, priority, index: pending.length });
14049
+ } else {
14050
+ enqueue(entry, priority);
14051
+ }
13899
14052
  }
13900
14053
  };
13901
14054
  }
13902
-
13903
- // src/Coordinator.js
13904
- var _instance;
13905
- function coordinator(instance16) {
13906
- if (instance16) {
13907
- _instance = instance16;
13908
- } else if (_instance == null) {
13909
- _instance = new Coordinator();
14055
+ function entryGroups(entries, cache) {
14056
+ const groups = [];
14057
+ const groupMap = /* @__PURE__ */ new Map();
14058
+ for (const query of entries) {
14059
+ const { entry: { request } } = query;
14060
+ const key = consolidationKey(request.query, cache);
14061
+ if (!groupMap.has(key)) {
14062
+ const list = [];
14063
+ groups.push(list);
14064
+ groupMap.set(key, list);
14065
+ }
14066
+ groupMap.get(key).push(query);
13910
14067
  }
13911
- return _instance;
14068
+ return groups;
13912
14069
  }
13913
- var Coordinator = class {
13914
- constructor(db = socketConnector(), options = {}) {
13915
- const {
13916
- logger = console,
13917
- manager = QueryManager()
13918
- } = options;
13919
- this.manager = manager;
13920
- this.logger(logger);
13921
- this.configure(options);
13922
- this.databaseConnector(db);
13923
- this.clear();
14070
+ function consolidationKey(query, cache) {
14071
+ const sql2 = `${query}`;
14072
+ if (query instanceof Query && !cache.get(sql2)) {
14073
+ if (
14074
+ // @ts-ignore
14075
+ query.orderby().length || query.where().length || // @ts-ignore
14076
+ query.qualify().length || query.having().length
14077
+ ) {
14078
+ return sql2;
14079
+ }
14080
+ const q2 = query.clone().$select("*");
14081
+ const groupby = query.groupby();
14082
+ if (groupby.length) {
14083
+ const map = {};
14084
+ query.select().forEach(({ as, expr }) => map[as] = expr);
14085
+ q2.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
14086
+ }
14087
+ return `${q2}`;
14088
+ } else {
14089
+ return sql2;
13924
14090
  }
13925
- logger(logger) {
13926
- if (arguments.length) {
13927
- this._logger = logger || voidLogger();
13928
- this.manager.logger(this._logger);
14091
+ }
14092
+ function consolidate(group, enqueue, record) {
14093
+ if (shouldConsolidate(group)) {
14094
+ enqueue({
14095
+ request: {
14096
+ type: "arrow",
14097
+ cache: false,
14098
+ record: false,
14099
+ query: group.query = consolidatedQuery(group, record)
14100
+ },
14101
+ result: group.result = queryResult()
14102
+ });
14103
+ } else {
14104
+ for (const { entry, priority } of group) {
14105
+ enqueue(entry, priority);
13929
14106
  }
13930
- return this._logger;
13931
14107
  }
13932
- /**
13933
- * Set configuration options for this coordinator.
13934
- * @param {object} [options] Configration options.
13935
- * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
13936
- * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
13937
- * @param {boolean|object} [options.indexes=true] Boolean flag to enable/disable
13938
- * automatic data cube indexes or an index options object.
13939
- */
13940
- configure({ cache = true, consolidate: consolidate2 = true, indexes = true } = {}) {
13941
- this.manager.cache(cache);
13942
- this.manager.consolidate(consolidate2);
13943
- this.indexes = indexes;
14108
+ }
14109
+ function shouldConsolidate(group) {
14110
+ if (group.length > 1) {
14111
+ const sql2 = `${group[0].entry.request.query}`;
14112
+ for (let i = 1; i < group.length; ++i) {
14113
+ if (sql2 !== `${group[i].entry.request.query}`) {
14114
+ return true;
14115
+ }
14116
+ }
13944
14117
  }
13945
- clear({ clients = true, cache = true } = {}) {
13946
- this.manager.clear();
13947
- if (clients) {
13948
- this.clients?.forEach((client) => this.disconnect(client));
13949
- this.filterGroups?.forEach((group) => group.finalize());
13950
- this.clients = /* @__PURE__ */ new Set();
13951
- this.filterGroups = /* @__PURE__ */ new Map();
14118
+ return false;
14119
+ }
14120
+ function consolidatedQuery(group, record) {
14121
+ const maps = group.maps = [];
14122
+ const fields = /* @__PURE__ */ new Map();
14123
+ for (const item of group) {
14124
+ const { query: query2 } = item.entry.request;
14125
+ const fieldMap = [];
14126
+ maps.push(fieldMap);
14127
+ for (const { as, expr } of query2.select()) {
14128
+ const e = `${expr}`;
14129
+ if (!fields.has(e)) {
14130
+ fields.set(e, [`col${fields.size}`, expr]);
14131
+ }
14132
+ const [name] = fields.get(e);
14133
+ fieldMap.push([name, as]);
13952
14134
  }
13953
- if (cache) this.manager.cache().clear();
13954
- }
13955
- databaseConnector(db) {
13956
- return this.manager.connector(db);
13957
- }
13958
- // -- Query Management ----
13959
- cancel(requests) {
13960
- this.manager.cancel(requests);
14135
+ record(`${query2}`);
13961
14136
  }
13962
- exec(query, { priority = Priority.Normal } = {}) {
13963
- query = Array.isArray(query) ? query.join(";\n") : query;
13964
- return this.manager.request({ type: "exec", query }, priority);
14137
+ const query = group[0].entry.request.query.clone();
14138
+ const groupby = query.groupby();
14139
+ if (groupby.length) {
14140
+ const map = {};
14141
+ group.maps[0].forEach(([name, as]) => map[as] = name);
14142
+ query.$groupby(groupby.map((e) => e instanceof Ref && map[e.column] || e));
13965
14143
  }
13966
- query(query, {
13967
- type = "arrow",
13968
- cache = true,
13969
- priority = Priority.Normal,
13970
- ...options
13971
- } = {}) {
13972
- return this.manager.request({ type, query, cache, options }, priority);
14144
+ return query.$select(Array.from(fields.values()));
14145
+ }
14146
+ async function processResults(group, cache) {
14147
+ const { maps, query, result } = group;
14148
+ if (!maps) return;
14149
+ let data;
14150
+ try {
14151
+ data = await result;
14152
+ } catch (err) {
14153
+ for (const { entry } of group) {
14154
+ entry.result.reject(err);
14155
+ }
14156
+ return;
13973
14157
  }
13974
- prefetch(query, options = {}) {
13975
- return this.query(query, { ...options, cache: true, priority: Priority.Low });
14158
+ const describe = isDescribeQuery(query);
14159
+ group.forEach(({ entry }, index) => {
14160
+ const { request, result: result2 } = entry;
14161
+ const map = maps[index];
14162
+ const extract = describe && map ? filterResult(data, map) : map ? projectResult(data, map) : data;
14163
+ if (request.cache) {
14164
+ cache.set(String(request.query), extract);
14165
+ }
14166
+ result2.fulfill(extract);
14167
+ });
14168
+ }
14169
+ function projectResult(data, map) {
14170
+ const cols = {};
14171
+ for (const [name, as] of map) {
14172
+ cols[as] = data.getChild(name);
13976
14173
  }
13977
- createBundle(name, queries, priority = Priority.Low) {
13978
- const options = { name, queries };
13979
- return this.manager.request({ type: "create-bundle", options }, priority);
14174
+ return new data.constructor(cols);
14175
+ }
14176
+ function filterResult(data, map) {
14177
+ const lookup = new Map(map);
14178
+ const result = [];
14179
+ for (const d of data) {
14180
+ if (lookup.has(d.column_name)) {
14181
+ result.push({ ...d, column_name: lookup.get(d.column_name) });
14182
+ }
13980
14183
  }
13981
- loadBundle(name, priority = Priority.High) {
13982
- const options = { name };
13983
- return this.manager.request({ type: "load-bundle", options }, priority);
14184
+ return result;
14185
+ }
14186
+
14187
+ // src/util/cache.js
14188
+ var requestIdle = typeof requestIdleCallback !== "undefined" ? requestIdleCallback : setTimeout;
14189
+ var voidCache = () => ({
14190
+ get: () => void 0,
14191
+ set: (key, value) => value,
14192
+ clear: () => {
13984
14193
  }
13985
- // -- Client Management ----
13986
- updateClient(client, query, priority = Priority.Normal) {
13987
- client.queryPending();
13988
- return this.query(query, { priority }).then(
13989
- (data) => client.queryResult(data).update(),
13990
- (err) => {
13991
- client.queryError(err);
13992
- this._logger.error(err);
14194
+ });
14195
+ function lruCache({
14196
+ max: max2 = 1e3,
14197
+ // max entries
14198
+ ttl = 3 * 60 * 60 * 1e3
14199
+ // time-to-live, default 3 hours
14200
+ } = {}) {
14201
+ let cache = /* @__PURE__ */ new Map();
14202
+ function evict() {
14203
+ const expire = performance.now() - ttl;
14204
+ let lruKey = null;
14205
+ let lruLast = Infinity;
14206
+ for (const [key, value] of cache) {
14207
+ const { last: last2 } = value;
14208
+ if (last2 < lruLast) {
14209
+ lruKey = key;
14210
+ lruLast = last2;
14211
+ }
14212
+ if (expire > last2) {
14213
+ cache.delete(key);
13993
14214
  }
13994
- );
13995
- }
13996
- requestQuery(client, query) {
13997
- this.filterGroups.get(client.filterBy)?.reset();
13998
- return query ? this.updateClient(client, query) : client.update();
13999
- }
14000
- /**
14001
- * Connect a client to the coordinator.
14002
- * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
14003
- */
14004
- async connect(client) {
14005
- const { clients, filterGroups, indexes } = this;
14006
- if (clients.has(client)) {
14007
- throw new Error("Client already connected.");
14008
14215
  }
14009
- clients.add(client);
14010
- client.coordinator = this;
14011
- const fields = client.fields();
14012
- if (fields?.length) {
14013
- client.fieldInfo(await queryFieldInfo(this, fields));
14216
+ if (lruKey) {
14217
+ cache.delete(lruKey);
14014
14218
  }
14015
- const filter = client.filterBy;
14016
- if (filter) {
14017
- if (filterGroups.has(filter)) {
14018
- filterGroups.get(filter).add(client);
14219
+ }
14220
+ return {
14221
+ get(key) {
14222
+ const entry = cache.get(key);
14223
+ if (entry) {
14224
+ entry.last = performance.now();
14225
+ return entry.value;
14226
+ }
14227
+ },
14228
+ set(key, value) {
14229
+ cache.set(key, { last: performance.now(), value });
14230
+ if (cache.size > max2) requestIdle(evict);
14231
+ return value;
14232
+ },
14233
+ clear() {
14234
+ cache = /* @__PURE__ */ new Map();
14235
+ }
14236
+ };
14237
+ }
14238
+
14239
+ // src/util/priority-queue.js
14240
+ function priorityQueue(ranks) {
14241
+ const queue = Array.from(
14242
+ { length: ranks },
14243
+ () => ({ head: null, tail: null })
14244
+ );
14245
+ return {
14246
+ /**
14247
+ * Indicate if the queue is empty.
14248
+ * @returns [boolean] true if empty, false otherwise.
14249
+ */
14250
+ isEmpty() {
14251
+ return queue.every((list) => !list.head);
14252
+ },
14253
+ /**
14254
+ * Insert an item into the queue with a given priority rank.
14255
+ * @param {*} item The item to add.
14256
+ * @param {number} rank The integer priority rank.
14257
+ * Priority ranks are integers starting at zero.
14258
+ * Lower ranks indicate higher priority.
14259
+ */
14260
+ insert(item, rank2) {
14261
+ const list = queue[rank2];
14262
+ if (!list) {
14263
+ throw new Error(`Invalid queue priority rank: ${rank2}`);
14264
+ }
14265
+ const node = { item, next: null };
14266
+ if (list.head === null) {
14267
+ list.head = list.tail = node;
14019
14268
  } else {
14020
- const group = new FilterGroup(this, filter, indexes);
14021
- filterGroups.set(filter, group.add(client));
14269
+ list.tail = list.tail.next = node;
14270
+ }
14271
+ },
14272
+ /**
14273
+ * Remove a set of items from the queue, regardless of priority rank.
14274
+ * If a provided item is not in the queue it will be ignored.
14275
+ * @param {(item: *) => boolean} test A predicate function to test
14276
+ * if an item should be removed (true to drop, false to keep).
14277
+ */
14278
+ remove(test) {
14279
+ for (const list of queue) {
14280
+ let { head, tail } = list;
14281
+ for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
14282
+ if (test(curr.item)) {
14283
+ if (curr === head) {
14284
+ head = curr.next;
14285
+ } else {
14286
+ prev.next = curr.next;
14287
+ }
14288
+ if (curr === tail) tail = prev || head;
14289
+ }
14290
+ }
14291
+ list.head = head;
14292
+ list.tail = tail;
14293
+ }
14294
+ },
14295
+ /**
14296
+ * Remove and return the next highest priority item.
14297
+ * @returns {*} The next item in the queue,
14298
+ * or undefined if this queue is empty.
14299
+ */
14300
+ next() {
14301
+ for (const list of queue) {
14302
+ const { head } = list;
14303
+ if (head !== null) {
14304
+ list.head = head.next;
14305
+ if (list.tail === head) {
14306
+ list.tail = null;
14307
+ }
14308
+ return head.item;
14309
+ }
14022
14310
  }
14023
14311
  }
14024
- client.requestQuery();
14025
- }
14026
- /**
14027
- * Disconnect a client from the coordinator.
14028
- *
14029
- * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
14030
- */
14031
- disconnect(client) {
14032
- const { clients, filterGroups } = this;
14033
- if (!clients.has(client)) return;
14034
- clients.delete(client);
14035
- filterGroups.get(client.filterBy)?.remove(client);
14036
- client.coordinator = null;
14037
- }
14038
- };
14312
+ };
14313
+ }
14039
14314
 
14040
- // src/util/AsyncDispatch.js
14041
- var AsyncDispatch = class {
14042
- /**
14043
- * Create a new asynchronous dispatcher instance.
14044
- */
14315
+ // src/QueryManager.js
14316
+ var Priority = { High: 0, Normal: 1, Low: 2 };
14317
+ var QueryManager = class {
14045
14318
  constructor() {
14046
- this._callbacks = /* @__PURE__ */ new Map();
14319
+ this.queue = priorityQueue(3);
14320
+ this.db = null;
14321
+ this.clientCache = null;
14322
+ this._logger = null;
14323
+ this._logQueries = false;
14324
+ this.recorders = [];
14325
+ this.pending = null;
14326
+ this._consolidate = null;
14047
14327
  }
14048
- /**
14049
- * Add an event listener callback for the provided event type.
14050
- * @param {string} type The event type.
14051
- * @param {(value: *) => void | Promise} callback The event handler
14052
- * callback function to add. If the callback has already been
14053
- * added for the event type, this method has no effect.
14054
- */
14055
- addEventListener(type, callback) {
14056
- if (!this._callbacks.has(type)) {
14057
- this._callbacks.set(type, {
14058
- callbacks: /* @__PURE__ */ new Set(),
14059
- pending: null,
14060
- queue: new DispatchQueue()
14061
- });
14328
+ next() {
14329
+ if (this.pending || this.queue.isEmpty()) return;
14330
+ const { request, result } = this.queue.next();
14331
+ this.pending = this.submit(request, result);
14332
+ this.pending.finally(() => {
14333
+ this.pending = null;
14334
+ this.next();
14335
+ });
14336
+ }
14337
+ enqueue(entry, priority = Priority.Normal) {
14338
+ this.queue.insert(entry, priority);
14339
+ this.next();
14340
+ }
14341
+ recordQuery(sql2) {
14342
+ if (this.recorders.length && sql2) {
14343
+ this.recorders.forEach((rec) => rec.add(sql2));
14062
14344
  }
14063
- const entry = this._callbacks.get(type);
14064
- entry.callbacks.add(callback);
14065
14345
  }
14066
- /**
14067
- * Remove an event listener callback for the provided event type.
14068
- * @param {string} type The event type.
14069
- * @param {(value: *) => void | Promise} callback The event handler
14070
- * callback function to remove.
14071
- */
14072
- removeEventListener(type, callback) {
14073
- const entry = this._callbacks.get(type);
14074
- if (entry) {
14075
- entry.callbacks.delete(callback);
14346
+ async submit(request, result) {
14347
+ try {
14348
+ const { query, type, cache = false, record = true, options } = request;
14349
+ const sql2 = query ? `${query}` : null;
14350
+ if (record) {
14351
+ this.recordQuery(sql2);
14352
+ }
14353
+ if (cache) {
14354
+ const cached = this.clientCache.get(sql2);
14355
+ if (cached) {
14356
+ this._logger.debug("Cache");
14357
+ result.fulfill(cached);
14358
+ return;
14359
+ }
14360
+ }
14361
+ const t0 = performance.now();
14362
+ if (this._logQueries) {
14363
+ this._logger.debug("Query", { type, sql: sql2, ...options });
14364
+ }
14365
+ const data = await this.db.query({ type, sql: sql2, ...options });
14366
+ if (cache) this.clientCache.set(sql2, data);
14367
+ this._logger.debug(`Request: ${(performance.now() - t0).toFixed(1)}`);
14368
+ result.fulfill(data);
14369
+ } catch (err) {
14370
+ result.reject(err);
14076
14371
  }
14077
14372
  }
14078
- /**
14079
- * Lifecycle method that returns the event value to emit.
14080
- * This default implementation simply returns the input value as-is.
14081
- * Subclasses may override this method to implement custom transformations
14082
- * prior to emitting an event value to all listeners.
14083
- * @param {string} type The event type.
14084
- * @param {*} value The event value.
14085
- * @returns The (possibly transformed) event value to emit.
14086
- */
14087
- willEmit(type, value) {
14088
- return value;
14373
+ cache(value) {
14374
+ return value !== void 0 ? this.clientCache = value === true ? lruCache() : value || voidCache() : this.clientCache;
14089
14375
  }
14090
- /**
14091
- * Lifecycle method that returns a filter function for updating the
14092
- * queue of unemitted event values prior to enqueueing a new value.
14093
- * This default implementation simply returns null, indicating that
14094
- * any other unemitted event values should be dropped (that is, all
14095
- * queued events are filtered)
14096
- * @param {string} type The event type.
14097
- * @param {*} value The new event value that will be enqueued.
14098
- * @returns {(value: *) => boolean|null} A dispatch queue filter
14099
- * function, or null if all unemitted event values should be filtered.
14100
- */
14101
- emitQueueFilter(type, value) {
14102
- return null;
14376
+ logger(value) {
14377
+ return value ? this._logger = value : this._logger;
14103
14378
  }
14104
- /**
14105
- * Cancel all unemitted event values for the given event type.
14106
- * @param {string} type The event type.
14107
- */
14108
- cancel(type) {
14109
- const entry = this._callbacks.get(type);
14110
- entry?.queue.clear();
14379
+ logQueries(value) {
14380
+ return value !== void 0 ? this._logQueries = !!value : this._logQueries;
14111
14381
  }
14112
- /**
14113
- * Emit an event value to listeners for the given event type.
14114
- * If a previous emit has not yet resolved, the event value
14115
- * will be queued to be emitted later.
14116
- * The actual event value given to listeners will be the result
14117
- * of passing the input value through the emitValue() method.
14118
- * @param {string} type The event type.
14119
- * @param {*} value The event value.
14120
- */
14121
- emit(type, value) {
14122
- const entry = this._callbacks.get(type) || {};
14123
- if (entry.pending) {
14124
- entry.queue.enqueue(value, this.emitQueueFilter(type, value));
14382
+ connector(connector) {
14383
+ return connector ? this.db = connector : this.db;
14384
+ }
14385
+ consolidate(flag) {
14386
+ if (flag && !this._consolidate) {
14387
+ this._consolidate = consolidator(this.enqueue.bind(this), this.clientCache, this.recordQuery.bind(this));
14388
+ } else if (!flag && this._consolidate) {
14389
+ this._consolidate = null;
14390
+ }
14391
+ }
14392
+ request(request, priority = Priority.Normal) {
14393
+ const result = queryResult();
14394
+ const entry = { request, result };
14395
+ if (this._consolidate) {
14396
+ this._consolidate.add(entry, priority);
14125
14397
  } else {
14126
- const event = this.willEmit(type, value);
14127
- const { callbacks, queue } = entry;
14128
- if (callbacks?.size) {
14129
- const callbackValues = Array.from(callbacks, (cb) => cb(event));
14130
- entry.pending = Promise.allSettled(callbackValues).then(() => {
14131
- entry.pending = null;
14132
- if (!queue.isEmpty()) {
14133
- this.emit(type, queue.dequeue());
14134
- }
14135
- });
14136
- }
14398
+ this.enqueue(entry, priority);
14137
14399
  }
14400
+ return result;
14138
14401
  }
14139
- };
14140
- var DispatchQueue = class {
14141
- /**
14142
- * Create a new dispatch queue instance.
14143
- */
14144
- constructor() {
14145
- this.clear();
14402
+ cancel(requests) {
14403
+ const set = new Set(requests);
14404
+ this.queue.remove(({ result }) => set.has(result));
14146
14405
  }
14147
- /**
14148
- * Clear the queue state of all event values.
14149
- */
14150
14406
  clear() {
14151
- this.next = null;
14152
- }
14153
- /**
14154
- * Indicate if the queue is empty.
14155
- * @returns {boolean} True if queue is empty, false otherwise.
14156
- */
14157
- isEmpty() {
14158
- return !this.next;
14407
+ this.queue.remove(({ result }) => {
14408
+ result.reject("Cleared");
14409
+ return true;
14410
+ });
14159
14411
  }
14160
- /**
14161
- * Add a new value to the queue, and optionally filter the
14162
- * current queue content in response.
14163
- * @param {*} value The value to add.
14164
- * @param {(value: *) => boolean} [filter] An optional filter
14165
- * function to apply to existing queue content. If unspecified
14166
- * or falsy, all previously queued values are removed. Otherwise,
14167
- * the provided function is applied to all queue entries. The
14168
- * entry is retained if the filter function returns a truthy value,
14169
- * otherwise the entry is removed.
14170
- */
14171
- enqueue(value, filter) {
14172
- const tail = { value };
14173
- if (filter && this.next) {
14174
- let curr = this;
14175
- while (curr.next) {
14176
- if (filter(curr.next.value)) {
14177
- curr = curr.next;
14178
- } else {
14179
- curr.next = curr.next.next;
14180
- }
14412
+ record() {
14413
+ let state = [];
14414
+ const recorder = {
14415
+ add(query) {
14416
+ state.push(query);
14417
+ },
14418
+ reset() {
14419
+ state = [];
14420
+ },
14421
+ snapshot() {
14422
+ return state.slice();
14423
+ },
14424
+ stop() {
14425
+ this.recorders = this.recorders.filter((x3) => x3 !== recorder);
14426
+ return state;
14181
14427
  }
14182
- curr.next = tail;
14183
- } else {
14184
- this.next = tail;
14185
- }
14186
- }
14187
- /**
14188
- * Remove and return the next queued event value.
14189
- * @returns {*} The next event value in the queue.
14190
- */
14191
- dequeue() {
14192
- const { next } = this;
14193
- this.next = next?.next;
14194
- return next?.value;
14428
+ };
14429
+ this.recorders.push(recorder);
14430
+ return recorder;
14195
14431
  }
14196
14432
  };
14197
14433
 
14198
- // src/util/distinct.js
14199
- function distinct(a2, b2) {
14200
- return a2 === b2 ? false : a2 instanceof Date && b2 instanceof Date ? +a2 !== +b2 : Array.isArray(a2) && Array.isArray(b2) ? distinctArray(a2, b2) : true;
14201
- }
14202
- function distinctArray(a2, b2) {
14203
- if (a2.length !== b2.length) return true;
14204
- for (let i = 0; i < a2.length; ++i) {
14205
- if (a2[i] !== b2[i]) return true;
14206
- }
14207
- return false;
14208
- }
14209
-
14210
- // src/Param.js
14211
- function isParam(x3) {
14212
- return x3 instanceof Param;
14213
- }
14214
- var Param = class _Param extends AsyncDispatch {
14215
- /**
14216
- * Create a new Param instance.
14217
- * @param {*} value The initial value of the Param.
14218
- */
14219
- constructor(value) {
14220
- super();
14221
- this._value = value;
14222
- }
14223
- /**
14224
- * Create a new Param instance with the given initial value.
14225
- * @param {*} value The initial value of the Param.
14226
- * @returns {Param} The new Param instance.
14227
- */
14228
- static value(value) {
14229
- return new _Param(value);
14230
- }
14231
- /**
14232
- * Create a new Param instance over an array of initial values,
14233
- * which may contain nested Params.
14234
- * @param {*} values The initial values of the Param.
14235
- * @returns {Param} The new Param instance.
14236
- */
14237
- static array(values) {
14238
- if (values.some((v2) => isParam(v2))) {
14239
- const p2 = new _Param();
14240
- const update2 = () => {
14241
- p2.update(values.map((v2) => isParam(v2) ? v2.value : v2));
14242
- };
14243
- update2();
14244
- values.forEach((v2) => isParam(v2) ? v2.addEventListener("value", update2) : 0);
14245
- return p2;
14246
- }
14247
- return new _Param(values);
14434
+ // src/util/js-type.js
14435
+ function jsType(type) {
14436
+ switch (type) {
14437
+ case "BIGINT":
14438
+ case "HUGEINT":
14439
+ case "INTEGER":
14440
+ case "SMALLINT":
14441
+ case "TINYINT":
14442
+ case "UBIGINT":
14443
+ case "UINTEGER":
14444
+ case "USMALLINT":
14445
+ case "UTINYINT":
14446
+ case "DOUBLE":
14447
+ case "FLOAT":
14448
+ case "REAL":
14449
+ return "number";
14450
+ case "DATE":
14451
+ case "TIMESTAMP":
14452
+ case "TIMESTAMPTZ":
14453
+ case "TIMESTAMP WITH TIME ZONE":
14454
+ case "TIME":
14455
+ case "TIMESTAMP_NS":
14456
+ return "date";
14457
+ case "BOOLEAN":
14458
+ return "boolean";
14459
+ case "VARCHAR":
14460
+ case "UUID":
14461
+ case "JSON":
14462
+ return "string";
14463
+ case "ARRAY":
14464
+ case "LIST":
14465
+ return "array";
14466
+ case "BLOB":
14467
+ case "STRUCT":
14468
+ case "MAP":
14469
+ case "GEOMETRY":
14470
+ return "object";
14471
+ default:
14472
+ if (type.startsWith("DECIMAL")) {
14473
+ return "number";
14474
+ } else if (type.startsWith("STRUCT") || type.startsWith("MAP")) {
14475
+ return "object";
14476
+ } else if (type.endsWith("]")) {
14477
+ return "array";
14478
+ }
14479
+ throw new Error(`Unsupported type: ${type}`);
14248
14480
  }
14249
- /**
14250
- * The current value of the Param.
14251
- */
14252
- get value() {
14253
- return this._value;
14481
+ }
14482
+
14483
+ // src/util/convert-arrow.js
14484
+ function isArrowTable(values) {
14485
+ return typeof values?.getChild === "function";
14486
+ }
14487
+ function convertArrowArrayType(type) {
14488
+ return DataType.isInt(type) || DataType.isFloat(type) || DataType.isDecimal(type) ? Float64Array : Array;
14489
+ }
14490
+ function convertArrowValue(type) {
14491
+ if (DataType.isTimestamp(type)) {
14492
+ return (v2) => v2 == null ? v2 : new Date(v2);
14254
14493
  }
14255
- /**
14256
- * Update the Param value
14257
- * @param {*} value The new value of the Param.
14258
- * @param {object} [options] The update options.
14259
- * @param {boolean} [options.force] A boolean flag indicating if the Param
14260
- * should emit a 'value' event even if the internal value is unchanged.
14261
- * @returns {this} This Param instance.
14262
- */
14263
- update(value, { force } = {}) {
14264
- const shouldEmit = distinct(this._value, value) || force;
14265
- if (shouldEmit) {
14266
- this.emit("value", value);
14267
- } else {
14268
- this.cancel("value");
14269
- }
14270
- return this;
14494
+ if (DataType.isInt(type) && type.bitWidth >= 64) {
14495
+ return (v2) => v2 == null ? v2 : Number(v2);
14271
14496
  }
14272
- /**
14273
- * Upon value-typed updates, sets the current value to the input value
14274
- * immediately prior to the event value being emitted to listeners.
14275
- * @param {string} type The event type.
14276
- * @param {*} value The input event value.
14277
- * @returns {*} The input event value.
14278
- */
14279
- willEmit(type, value) {
14280
- if (type === "value") {
14281
- this._value = value;
14282
- }
14283
- return value;
14497
+ if (DataType.isDecimal(type)) {
14498
+ const scale = 1 / Math.pow(10, type.scale);
14499
+ return (v2) => v2 == null ? v2 : decimalToNumber(v2, scale);
14284
14500
  }
14285
- };
14286
-
14287
- // src/Selection.js
14288
- function isSelection(x3) {
14289
- return x3 instanceof Selection;
14501
+ return (v2) => v2;
14290
14502
  }
14291
- var Selection = class _Selection extends Param {
14292
- /**
14293
- * Create a new Selection instance with an
14294
- * intersect (conjunction) resolution strategy.
14295
- * @param {object} [options] The selection options.
14296
- * @param {boolean} [options.cross=false] Boolean flag indicating
14297
- * cross-filtered resolution. If true, selection clauses will not
14298
- * be applied to the clients they are associated with.
14299
- * @returns {Selection} The new Selection instance.
14300
- */
14301
- static intersect({ cross = false } = {}) {
14302
- return new _Selection(new SelectionResolver({ cross }));
14503
+ function convertArrowColumn(column2) {
14504
+ const { type } = column2;
14505
+ if (DataType.isTimestamp(type)) {
14506
+ const size = column2.length;
14507
+ const array = new Array(size);
14508
+ for (let row = 0; row < size; ++row) {
14509
+ const v2 = column2.get(row);
14510
+ array[row] = v2 == null ? null : new Date(v2);
14511
+ }
14512
+ return array;
14303
14513
  }
14304
- /**
14305
- * Create a new Selection instance with a
14306
- * union (disjunction) resolution strategy.
14307
- * @param {object} [options] The selection options.
14308
- * @param {boolean} [options.cross=false] Boolean flag indicating
14309
- * cross-filtered resolution. If true, selection clauses will not
14310
- * be applied to the clients they are associated with.
14311
- * @returns {Selection} The new Selection instance.
14312
- */
14313
- static union({ cross = false } = {}) {
14314
- return new _Selection(new SelectionResolver({ cross, union: true }));
14514
+ if (DataType.isInt(type) && type.bitWidth >= 64) {
14515
+ const size = column2.length;
14516
+ const array = column2.nullCount ? new Array(size) : new Float64Array(size);
14517
+ for (let row = 0; row < size; ++row) {
14518
+ const v2 = column2.get(row);
14519
+ array[row] = v2 == null ? null : Number(v2);
14520
+ }
14521
+ return array;
14315
14522
  }
14316
- /**
14317
- * Create a new Selection instance with a singular resolution strategy
14318
- * that keeps only the most recent selection clause.
14319
- * @param {object} [options] The selection options.
14320
- * @param {boolean} [options.cross=false] Boolean flag indicating
14321
- * cross-filtered resolution. If true, selection clauses will not
14322
- * be applied to the clients they are associated with.
14323
- * @returns {Selection} The new Selection instance.
14324
- */
14325
- static single({ cross = false } = {}) {
14326
- return new _Selection(new SelectionResolver({ cross, single: true }));
14523
+ if (DataType.isDecimal(type)) {
14524
+ const scale = 1 / Math.pow(10, type.scale);
14525
+ const size = column2.length;
14526
+ const array = column2.nullCount ? new Array(size) : new Float64Array(size);
14527
+ for (let row = 0; row < size; ++row) {
14528
+ const v2 = column2.get(row);
14529
+ array[row] = v2 == null ? null : decimalToNumber(v2, scale);
14530
+ }
14531
+ return array;
14327
14532
  }
14328
- /**
14329
- * Create a new Selection instance with a
14330
- * cross-filtered intersect resolution strategy.
14331
- * @returns {Selection} The new Selection instance.
14332
- */
14333
- static crossfilter() {
14334
- return new _Selection(new SelectionResolver({ cross: true }));
14533
+ if (column2.nullCount) {
14534
+ return Array.from(column2);
14335
14535
  }
14336
- /**
14337
- * Create a new Selection instance.
14338
- * @param {SelectionResolver} resolver The selection resolution
14339
- * strategy to apply.
14340
- */
14341
- constructor(resolver = new SelectionResolver()) {
14342
- super([]);
14343
- this._resolved = this._value;
14344
- this._resolver = resolver;
14536
+ return column2.toArray();
14537
+ }
14538
+ var BASE32 = Array.from(
14539
+ { length: 8 },
14540
+ (_2, i) => Math.pow(2, i * 32)
14541
+ );
14542
+ function decimalToNumber(v2, scale) {
14543
+ const n = v2.length;
14544
+ let x3 = 0;
14545
+ if (v2.signed && (v2[n - 1] | 0) < 0) {
14546
+ for (let i = 0; i < n; ++i) {
14547
+ x3 += ~v2[i] * BASE32[i];
14548
+ }
14549
+ x3 = -(x3 + 1);
14550
+ } else {
14551
+ for (let i = 0; i < n; ++i) {
14552
+ x3 += v2[i] * BASE32[i];
14553
+ }
14345
14554
  }
14346
- /**
14347
- * Create a cloned copy of this Selection instance.
14348
- * @returns {Selection} A clone of this selection.
14349
- */
14350
- clone() {
14351
- const s = new _Selection(this._resolver);
14352
- s._value = s._resolved = this._value;
14353
- return s;
14555
+ return x3 * scale;
14556
+ }
14557
+
14558
+ // src/util/field-info.js
14559
+ var Count = "count";
14560
+ var Nulls = "nulls";
14561
+ var Max = "max";
14562
+ var Min = "min";
14563
+ var Distinct = "distinct";
14564
+ var statMap = {
14565
+ [Count]: count,
14566
+ [Distinct]: (column2) => count(column2).distinct(),
14567
+ [Max]: max,
14568
+ [Min]: min,
14569
+ [Nulls]: (column2) => count().where(isNull(column2))
14570
+ };
14571
+ function summarize(table, column2, stats) {
14572
+ return Query.from(table).select(Array.from(stats, (s) => [s, statMap[s](column2)]));
14573
+ }
14574
+ async function queryFieldInfo(mc, fields) {
14575
+ if (fields.length === 1 && `${fields[0].column}` === "*") {
14576
+ return getTableInfo(mc, fields[0].table);
14577
+ } else {
14578
+ return (await Promise.all(fields.map((f2) => getFieldInfo(mc, f2)))).filter((x3) => x3);
14354
14579
  }
14355
- /**
14356
- * Create a clone of this Selection with clauses corresponding
14357
- * to the provided source removed.
14358
- * @param {*} source The clause source to remove.
14359
- * @returns {Selection} A cloned and updated Selection.
14360
- */
14361
- remove(source) {
14362
- const s = this.clone();
14363
- s._value = s._resolved = s._resolver.resolve(this._resolved, { source });
14364
- s._value.active = { source };
14365
- return s;
14580
+ }
14581
+ async function getFieldInfo(mc, { table, column: column2, stats }) {
14582
+ const q2 = Query.from({ source: table }).select({ column: column2 }).groupby(column2.aggregate ? sql`ALL` : []);
14583
+ const [desc2] = Array.from(await mc.query(Query.describe(q2)));
14584
+ const info = {
14585
+ table,
14586
+ column: `${column2}`,
14587
+ sqlType: desc2.column_type,
14588
+ type: jsType(desc2.column_type),
14589
+ nullable: desc2.null === "YES"
14590
+ };
14591
+ if (!(stats?.length || stats?.size)) return info;
14592
+ const result = await mc.query(
14593
+ summarize(table, column2, stats),
14594
+ { persist: true }
14595
+ );
14596
+ for (let i = 0; i < result.numCols; ++i) {
14597
+ const { name } = result.schema.fields[i];
14598
+ const child = result.getChildAt(i);
14599
+ const convert = convertArrowValue(child.type);
14600
+ info[name] = convert(child.get(0));
14366
14601
  }
14367
- /**
14368
- * The current active (most recently updated) selection clause.
14369
- */
14370
- get active() {
14371
- return this.clauses.active;
14602
+ return info;
14603
+ }
14604
+ async function getTableInfo(mc, table) {
14605
+ const result = await mc.query(`DESCRIBE ${asRelation(table)}`);
14606
+ return Array.from(result).map((desc2) => ({
14607
+ table,
14608
+ column: desc2.column_name,
14609
+ sqlType: desc2.column_type,
14610
+ type: jsType(desc2.column_type),
14611
+ nullable: desc2.null === "YES"
14612
+ }));
14613
+ }
14614
+
14615
+ // src/util/void-logger.js
14616
+ function voidLogger() {
14617
+ return {
14618
+ debug() {
14619
+ },
14620
+ info() {
14621
+ },
14622
+ log() {
14623
+ },
14624
+ warn() {
14625
+ },
14626
+ error() {
14627
+ }
14628
+ };
14629
+ }
14630
+
14631
+ // src/Coordinator.js
14632
+ var _instance;
14633
+ function coordinator(instance16) {
14634
+ if (instance16) {
14635
+ _instance = instance16;
14636
+ } else if (_instance == null) {
14637
+ _instance = new Coordinator();
14372
14638
  }
14373
- /**
14374
- * The value corresponding to the current active selection clause.
14375
- * This method ensures compatibility where a normal Param is expected.
14376
- */
14377
- get value() {
14378
- return this.active?.value;
14639
+ return _instance;
14640
+ }
14641
+ var Coordinator = class {
14642
+ constructor(db = socketConnector(), options = {}) {
14643
+ const {
14644
+ logger = console,
14645
+ manager = new QueryManager()
14646
+ } = options;
14647
+ this.manager = manager;
14648
+ this.logger(logger);
14649
+ this.configure(options);
14650
+ this.databaseConnector(db);
14651
+ this.clear();
14379
14652
  }
14380
- /**
14381
- * The current array of selection clauses.
14382
- */
14383
- get clauses() {
14384
- return super.value;
14653
+ logger(logger) {
14654
+ if (arguments.length) {
14655
+ this._logger = logger || voidLogger();
14656
+ this.manager.logger(this._logger);
14657
+ }
14658
+ return this._logger;
14385
14659
  }
14386
14660
  /**
14387
- * Indicate if this selection has a single resolution strategy.
14661
+ * Set configuration options for this coordinator.
14662
+ * @param {object} [options] Configration options.
14663
+ * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
14664
+ * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
14665
+ * @param {boolean|object} [options.indexes=true] Boolean flag to enable/disable
14666
+ * automatic data cube indexes or an index options object.
14388
14667
  */
14389
- get single() {
14390
- return this._resolver.single;
14668
+ configure({ cache = true, consolidate: consolidate2 = true, indexes = true } = {}) {
14669
+ this.manager.cache(cache);
14670
+ this.manager.consolidate(consolidate2);
14671
+ this.indexes = indexes;
14391
14672
  }
14392
- /**
14393
- * Emit an activate event with the given selection clause.
14394
- * @param {*} clause The clause repesenting the potential activation.
14395
- */
14396
- activate(clause) {
14397
- this.emit("activate", clause);
14673
+ clear({ clients = true, cache = true } = {}) {
14674
+ this.manager.clear();
14675
+ if (clients) {
14676
+ this.clients?.forEach((client) => this.disconnect(client));
14677
+ this.filterGroups?.forEach((group) => group.finalize());
14678
+ this.clients = /* @__PURE__ */ new Set();
14679
+ this.filterGroups = /* @__PURE__ */ new Map();
14680
+ }
14681
+ if (cache) this.manager.cache().clear();
14398
14682
  }
14399
- /**
14400
- * Update the selection with a new selection clause.
14401
- * @param {*} clause The selection clause to add.
14402
- * @returns {this} This Selection instance.
14403
- */
14404
- update(clause) {
14405
- this._resolved = this._resolver.resolve(this._resolved, clause, true);
14406
- this._resolved.active = clause;
14407
- return super.update(this._resolved);
14683
+ databaseConnector(db) {
14684
+ return this.manager.connector(db);
14408
14685
  }
14409
- /**
14410
- * Upon value-typed updates, sets the current clause list to the
14411
- * input value and returns the active clause value.
14412
- * @param {string} type The event type.
14413
- * @param {*} value The input event value.
14414
- * @returns {*} For value-typed events, returns the active clause
14415
- * values. Otherwise returns the input event value as-is.
14416
- */
14417
- willEmit(type, value) {
14418
- if (type === "value") {
14419
- this._value = value;
14420
- return this.value;
14421
- }
14422
- return value;
14686
+ // -- Query Management ----
14687
+ cancel(requests) {
14688
+ this.manager.cancel(requests);
14423
14689
  }
14424
- /**
14425
- * Upon value-typed updates, returns a dispatch queue filter function.
14426
- * The return value depends on the selection resolution strategy.
14427
- * @param {string} type The event type.
14428
- * @param {*} value The new event value that will be enqueued.
14429
- * @returns {(value: *) => boolean|null} For value-typed events,
14430
- * returns a dispatch queue filter function. Otherwise returns null.
14431
- */
14432
- emitQueueFilter(type, value) {
14433
- return type === "value" ? this._resolver.queueFilter(value) : null;
14690
+ exec(query, { priority = Priority.Normal } = {}) {
14691
+ query = Array.isArray(query) ? query.join(";\n") : query;
14692
+ return this.manager.request({ type: "exec", query }, priority);
14434
14693
  }
14435
- /**
14436
- * Indicates if a selection clause should not be applied to a given client.
14437
- * The return value depends on the selection resolution strategy.
14438
- * @param {*} client The selection clause.
14439
- * @param {*} clause The client to test.
14440
- * @returns True if the client should be skipped, false otherwise.
14441
- */
14442
- skip(client, clause) {
14443
- return this._resolver.skip(client, clause);
14694
+ query(query, {
14695
+ type = "arrow",
14696
+ cache = true,
14697
+ priority = Priority.Normal,
14698
+ ...options
14699
+ } = {}) {
14700
+ return this.manager.request({ type, query, cache, options }, priority);
14444
14701
  }
14445
- /**
14446
- * Return a selection query predicate for the given client.
14447
- * @param {*} client The client whose data may be filtered.
14448
- * @param {boolean} [noSkip=false] Disable skipping of active
14449
- * cross-filtered sources. If set true, the source of the active
14450
- * clause in a cross-filtered selection will not be skipped.
14451
- * @returns {*} The query predicate for filtering client data,
14452
- * based on the current state of this selection.
14453
- */
14454
- predicate(client, noSkip = false) {
14455
- const { clauses } = this;
14456
- const active = noSkip ? null : clauses.active;
14457
- return this._resolver.predicate(clauses, active, client);
14702
+ prefetch(query, options = {}) {
14703
+ return this.query(query, { ...options, cache: true, priority: Priority.Low });
14458
14704
  }
14459
- };
14460
- var SelectionResolver = class {
14461
- /**
14462
- * Create a new selection resolved instance.
14463
- * @param {object} [options] The resolution strategy options.
14464
- * @param {boolean} [options.union=false] Boolean flag to indicate a union strategy.
14465
- * If false, an intersection strategy is used.
14466
- * @param {boolean} [options.cross=false] Boolean flag to indicate cross-filtering.
14467
- * @param {boolean} [options.single=false] Boolean flag to indicate single clauses only.
14468
- */
14469
- constructor({ union, cross, single } = {}) {
14470
- this.union = !!union;
14471
- this.cross = !!cross;
14472
- this.single = !!single;
14705
+ createBundle(name, queries, priority = Priority.Low) {
14706
+ const options = { name, queries };
14707
+ return this.manager.request({ type: "create-bundle", options }, priority);
14473
14708
  }
14474
- /**
14475
- * Resolve a list of selection clauses according to the resolution strategy.
14476
- * @param {*[]} clauseList An array of selection clauses.
14477
- * @param {*} clause A new selection clause to add.
14478
- * @returns {*[]} An updated array of selection clauses.
14479
- */
14480
- resolve(clauseList, clause, reset = false) {
14481
- const { source, predicate } = clause;
14482
- const filtered = clauseList.filter((c) => source !== c.source);
14483
- const clauses = this.single ? [] : filtered;
14484
- if (this.single && reset) filtered.forEach((c) => c.source?.reset?.());
14485
- if (predicate) clauses.push(clause);
14486
- return clauses;
14709
+ loadBundle(name, priority = Priority.High) {
14710
+ const options = { name };
14711
+ return this.manager.request({ type: "load-bundle", options }, priority);
14487
14712
  }
14488
- /**
14489
- * Indicates if a selection clause should not be applied to a given client.
14490
- * The return value depends on the resolution strategy.
14491
- * @param {*} client The selection clause.
14492
- * @param {*} clause The client to test.
14493
- * @returns True if the client should be skipped, false otherwise.
14494
- */
14495
- skip(client, clause) {
14496
- return this.cross && clause?.clients?.has(client);
14713
+ // -- Client Management ----
14714
+ updateClient(client, query, priority = Priority.Normal) {
14715
+ client.queryPending();
14716
+ return this.query(query, { priority }).then(
14717
+ (data) => client.queryResult(data).update(),
14718
+ (err) => {
14719
+ client.queryError(err);
14720
+ this._logger.error(err);
14721
+ }
14722
+ );
14723
+ }
14724
+ requestQuery(client, query) {
14725
+ this.filterGroups.get(client.filterBy)?.reset();
14726
+ return query ? this.updateClient(client, query) : client.update();
14497
14727
  }
14498
14728
  /**
14499
- * Return a selection query predicate for the given client.
14500
- * @param {*[]} clauseList An array of selection clauses.
14501
- * @param {*} active The current active selection clause.
14502
- * @param {*} client The client whose data may be filtered.
14503
- * @returns {*} The query predicate for filtering client data,
14504
- * based on the current state of this selection.
14729
+ * Connect a client to the coordinator.
14730
+ * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
14505
14731
  */
14506
- predicate(clauseList, active, client) {
14507
- const { union } = this;
14508
- if (this.skip(client, active)) return void 0;
14509
- const predicates = clauseList.filter((clause) => !this.skip(client, clause)).map((clause) => clause.predicate);
14510
- return union && predicates.length > 1 ? or(predicates) : predicates;
14732
+ async connect(client) {
14733
+ const { clients, filterGroups, indexes } = this;
14734
+ if (clients.has(client)) {
14735
+ throw new Error("Client already connected.");
14736
+ }
14737
+ clients.add(client);
14738
+ client.coordinator = this;
14739
+ const fields = client.fields();
14740
+ if (fields?.length) {
14741
+ client.fieldInfo(await queryFieldInfo(this, fields));
14742
+ }
14743
+ const filter = client.filterBy;
14744
+ if (filter) {
14745
+ if (filterGroups.has(filter)) {
14746
+ filterGroups.get(filter).add(client);
14747
+ } else {
14748
+ const group = new FilterGroup(this, filter, indexes);
14749
+ filterGroups.set(filter, group.add(client));
14750
+ }
14751
+ }
14752
+ client.requestQuery();
14511
14753
  }
14512
14754
  /**
14513
- * Returns a filter function for queued selection updates.
14514
- * @param {*} value The new event value that will be enqueued.
14515
- * @returns {(value: *) => boolean|null} A dispatch queue filter
14516
- * function, or null if all unemitted event values should be filtered.
14755
+ * Disconnect a client from the coordinator.
14756
+ *
14757
+ * @param {import('./MosaicClient.js').MosaicClient} client the client to disconnect
14517
14758
  */
14518
- queueFilter(value) {
14519
- if (this.cross) {
14520
- const source = value.active?.source;
14521
- return (clauses) => clauses.active?.source !== source;
14522
- }
14523
- return null;
14759
+ disconnect(client) {
14760
+ const { clients, filterGroups } = this;
14761
+ if (!clients.has(client)) return;
14762
+ clients.delete(client);
14763
+ filterGroups.get(client.filterBy)?.remove(client);
14764
+ client.coordinator = null;
14524
14765
  }
14525
14766
  };
14526
14767
 
14768
+ // src/SelectionClause.js
14769
+ function point(field, value, { source, clients = void 0 }) {
14770
+ const predicate = value !== void 0 ? isNotDistinct(field, literal(value)) : null;
14771
+ return {
14772
+ meta: { type: "point" },
14773
+ source,
14774
+ clients,
14775
+ value,
14776
+ predicate
14777
+ };
14778
+ }
14779
+ function points(fields, value, { source, clients = void 0 }) {
14780
+ let predicate = null;
14781
+ if (value) {
14782
+ const clauses = value.map((vals) => {
14783
+ const list = vals.map((v2, i) => isNotDistinct(fields[i], literal(v2)));
14784
+ return list.length > 1 ? and(list) : list[0];
14785
+ });
14786
+ predicate = clauses.length > 1 ? or(clauses) : clauses[0];
14787
+ }
14788
+ return {
14789
+ meta: { type: "point" },
14790
+ source,
14791
+ clients,
14792
+ value,
14793
+ predicate
14794
+ };
14795
+ }
14796
+ function interval(field, value, {
14797
+ source,
14798
+ clients,
14799
+ bin,
14800
+ scale,
14801
+ pixelSize = 1
14802
+ }) {
14803
+ const predicate = value != null ? isBetween(field, value) : null;
14804
+ const meta = { type: "interval", scales: [scale], bin, pixelSize };
14805
+ return { meta, source, clients, value, predicate };
14806
+ }
14807
+ function intervals(fields, value, {
14808
+ source,
14809
+ clients,
14810
+ bin,
14811
+ scales: scales2 = [],
14812
+ pixelSize = 1
14813
+ }) {
14814
+ const predicate = value != null ? and(fields.map((f2, i) => isBetween(f2, value[i]))) : null;
14815
+ const meta = { type: "interval", scales: scales2, bin, pixelSize };
14816
+ return { meta, source, clients, value, predicate };
14817
+ }
14818
+ var MATCH_METHODS = { contains, prefix, suffix, regexp: regexp_matches };
14819
+ function match(field, value, {
14820
+ source,
14821
+ clients = void 0,
14822
+ method = "contains"
14823
+ }) {
14824
+ let fn = MATCH_METHODS[method];
14825
+ const predicate = value ? fn(field, literal(value)) : null;
14826
+ const meta = { type: "match", method };
14827
+ return { meta, source, clients, value, predicate };
14828
+ }
14829
+
14527
14830
  // src/connectors/rest.js
14528
14831
  function restConnector(uri = "http://localhost:3000/") {
14529
14832
  return {
14833
+ /**
14834
+ * Query the DuckDB server.
14835
+ * @param {object} query
14836
+ * @param {'exec' | 'arrow' | 'json'} [query.type] The query type: 'exec', 'arrow', or 'json'.
14837
+ * @param {string} query.sql A SQL query string.
14838
+ * @returns the query result
14839
+ */
14530
14840
  async query(query) {
14531
14841
  const req = fetch(uri, {
14532
14842
  method: "POST",
@@ -16932,10 +17242,10 @@ var getDictionary2 = (data, index) => {
16932
17242
  var getInterval2 = (data, index) => data.type.unit === IntervalUnit2.DAY_TIME ? getIntervalDayTime2(data, index) : getIntervalYearMonth2(data, index);
16933
17243
  var getIntervalDayTime2 = ({ values }, index) => values.subarray(2 * index, 2 * (index + 1));
16934
17244
  var getIntervalYearMonth2 = ({ values }, index) => {
16935
- const interval = values[index];
17245
+ const interval2 = values[index];
16936
17246
  const int32s = new Int32Array(2);
16937
- int32s[0] = Math.trunc(interval / 12);
16938
- int32s[1] = Math.trunc(interval % 12);
17247
+ int32s[0] = Math.trunc(interval2 / 12);
17248
+ int32s[1] = Math.trunc(interval2 % 12);
16939
17249
  return int32s;
16940
17250
  };
16941
17251
  var getDurationSecond2 = ({ values }, index) => values[index];
@@ -25903,7 +26213,7 @@ function wasmConnector(options = {}) {
25903
26213
  /**
25904
26214
  * Query the DuckDB-WASM instance.
25905
26215
  * @param {object} query
25906
- * @param {string} [query.type] The query type: 'exec', 'arrow', or 'json'.
26216
+ * @param {'exec' | 'arrow' | 'json'} [query.type] The query type: 'exec', 'arrow', or 'json'.
25907
26217
  * @param {string} query.sql A SQL query string.
25908
26218
  * @returns the query result
25909
26219
  */
@@ -25911,7 +26221,7 @@ function wasmConnector(options = {}) {
25911
26221
  const { type, sql: sql2 } = query;
25912
26222
  const con2 = await getConnection();
25913
26223
  const result = await con2.query(sql2);
25914
- return type === "exec" ? void 0 : type === "arrow" ? result : Array.from(result);
26224
+ return type === "exec" ? void 0 : type === "arrow" ? result : result.toArray();
25915
26225
  }
25916
26226
  };
25917
26227
  }
@@ -25985,9 +26295,14 @@ export {
25985
26295
  convertArrowValue,
25986
26296
  coordinator,
25987
26297
  distinct,
26298
+ interval,
26299
+ intervals,
25988
26300
  isArrowTable,
25989
26301
  isParam,
25990
26302
  isSelection,
26303
+ match,
26304
+ point,
26305
+ points,
25991
26306
  restConnector,
25992
26307
  socketConnector,
25993
26308
  synchronizer,